Skip ID3v2 data prepended to flac files on parsing (#21)

* Change signature to flacSignature

In order to disambiguate the introduction of the ID3v2 signature.

* Add check flac files containing ID3v2 data

* Implement skipId3v2

Note: a new import for decoding synchronized integers from ID3v2 headers
was introduced.

* Add error checking on the r.Discard calls

* Fix comments

* Use limited scope for error handling

* Capitalize ID in skipID3v2

* Fix comments

* Add testcase for skipping id3 data
This commit is contained in:
Henry Eklind 2018-05-26 00:27:20 +09:00 committed by Robin Eklind
parent 50441e5efb
commit 36cc17efed
4 changed files with 62 additions and 7 deletions

2
enc.go
View file

@ -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)
}

54
flac.go
View file

@ -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.
//

13
flac_test.go Normal file
View file

@ -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)
}
}

BIN
testdata/id3.flac vendored Normal file

Binary file not shown.