diff --git a/enc.go b/enc.go index 5740c30..4144645 100644 --- a/enc.go +++ b/enc.go @@ -24,7 +24,7 @@ func Encode(w io.Writer, stream *Stream) error { enc := &encoder{bw: bitio.NewWriter(buf)} // Store FLAC signature. - if _, err := enc.bw.Write(signature); err != nil { + if _, err := enc.bw.Write(flacSignature); err != nil { return errutil.Err(err) } diff --git a/flac.go b/flac.go index b47a3de..c5f34f9 100644 --- a/flac.go +++ b/flac.go @@ -30,6 +30,8 @@ import ( "github.com/mewkiz/flac/frame" "github.com/mewkiz/flac/meta" + + "github.com/mikkyang/id3-go/encodedbytes" ) // A Stream contains the metadata blocks and provides access to the audio frames @@ -79,8 +81,11 @@ func New(r io.Reader) (stream *Stream, err error) { return stream, nil } -// signature marks the beginning of a FLAC stream. -var signature = []byte("fLaC") +// flacSignature marks the beginning of a FLAC stream. +var flacSignature = []byte("fLaC") + +// id3Signature marks the beginning of an ID3 stream, used to skip over ID3 data. +var id3Signature = []byte("ID3") // parseStreamInfo verifies the signature which marks the beginning of a FLAC // stream, and parses the StreamInfo metadata block. It returns a boolean value @@ -90,12 +95,24 @@ func (stream *Stream) parseStreamInfo() (isLast bool, err error) { // Verify FLAC signature. r := stream.r var buf [4]byte - _, err = io.ReadFull(r, buf[:]) - if err != nil { + if _, err = io.ReadFull(r, buf[:]); err != nil { return false, err } - if !bytes.Equal(buf[:], signature) { - return false, fmt.Errorf("flac.parseStreamInfo: invalid FLAC signature; expected %q, got %q", signature, buf) + + // Skip prepended ID3v2 data. + if bytes.Equal(buf[:3], id3Signature) { + if err := stream.skipID3v2(); err != nil { + return false, err + } + + // Second attempt at verifying signature. + if _, err = io.ReadFull(r, buf[:]); err != nil { + return false, err + } + } + + if !bytes.Equal(buf[:], flacSignature) { + return false, fmt.Errorf("flac.parseStreamInfo: invalid FLAC signature; expected %q, got %q", flacSignature, buf) } // Parse StreamInfo metadata block. @@ -111,6 +128,31 @@ func (stream *Stream) parseStreamInfo() (isLast bool, err error) { return block.IsLast, nil } +// skipId3v2 skips ID3v2 data prepended to flac files. +func (stream *Stream) skipID3v2() error { + r := bufio.NewReader(stream.r) + + // Discard unnecessary data from the ID3v2 header. + if _, err := r.Discard(2); err != nil { + return err + } + + // Read the size from the ID3v2 header. + var sizeBuf [4]byte + if _, err := r.Read(sizeBuf[:]); err != nil { + return err + } + + // The size is encoded as a synchsafe integer. + size, err := encodedbytes.SynchInt(sizeBuf[:]) + if err != nil { + return err + } + + _, err = r.Discard(int(size)) + return err +} + // Parse creates a new Stream for accessing the metadata blocks and audio // samples of r. It reads and parses the FLAC signature and all metadata blocks. // diff --git a/flac_test.go b/flac_test.go new file mode 100644 index 0000000..63d4142 --- /dev/null +++ b/flac_test.go @@ -0,0 +1,13 @@ +package flac_test + +import ( + "testing" + + "github.com/mewkiz/flac" +) + +func TestSkipID3v2(t *testing.T) { + if _, err := flac.ParseFile("testdata/id3.flac"); err != nil { + t.Fatal(err) + } +} diff --git a/testdata/id3.flac b/testdata/id3.flac new file mode 100644 index 0000000..bf4e74f Binary files /dev/null and b/testdata/id3.flac differ