97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
package vorbis
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
var (
|
|
ErrNoHeaderSegment = errors.New("no header segment")
|
|
ErrNoMetadata = errors.New("no metadata found")
|
|
ErrCallReadRestAfterReadMetadata = errors.New("please call vorbis.Decoder.ReadRest() after having called vorbis.Decoder.ReadMetadata()")
|
|
ErrReadMetadataCalledTwice = errors.New("cannot call vorbis.Decoder.ReadMetadata() twice on the same file")
|
|
)
|
|
|
|
type Decoder struct {
|
|
r io.Reader
|
|
hasMetadata bool
|
|
}
|
|
|
|
func NewDecoder(r io.Reader) *Decoder {
|
|
return &Decoder{
|
|
r: r,
|
|
}
|
|
}
|
|
|
|
func (d *Decoder) readPage() (page OggPage, hdr VorbisHeader, err error) {
|
|
// Decode page.
|
|
page, err = OggDecode(d.r)
|
|
if err != nil {
|
|
return page, hdr, err
|
|
}
|
|
|
|
// We need to be able to access `page.Segments[0]`.
|
|
if len(page.Segments) == 0 {
|
|
return page, hdr, ErrNoHeaderSegment
|
|
}
|
|
|
|
// Decode Vorbis header, stored in `page.Segments[0]`.
|
|
hdr, err = VorbisHeaderDecode(bytes.NewBuffer(page.Segments[0]))
|
|
if err != nil {
|
|
return page, hdr, err
|
|
}
|
|
|
|
return page, hdr, nil
|
|
}
|
|
|
|
// Reads the Ogg/Vorbis file until it finds its metadata. Leaves the reader
|
|
// right after the end of the metadata. `crc32Sum` gives the crc32 checksum
|
|
// of the page containing the metadata. It is equivalent to the page checksum
|
|
// used in the Ogg container. Since the page contains more than just metadata,
|
|
// the checksum can usually be used as a unique identifier.
|
|
func (d *Decoder) ReadMetadata() (metadata *VorbisComment, crc32Sum uint32, err error) {
|
|
if d.hasMetadata {
|
|
return nil, 0, ErrReadMetadataCalledTwice
|
|
}
|
|
|
|
for {
|
|
page, hdr, err := d.readPage()
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
if (page.Header.HeaderType & FHeaderTypeEOS) > 0 {
|
|
// End of stream
|
|
return nil, 0, ErrNoMetadata
|
|
}
|
|
|
|
if hdr.PackType == PackTypeComment {
|
|
d.hasMetadata = true
|
|
return hdr.Comment, page.Header.Checksum, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// Must to be called after `ReadMetadata()`. Reads the rest of the Ogg/Vorbis
|
|
// file, leaving the reader right after the end of the Ogg/Vorbis file.
|
|
func (d *Decoder) ReadRest() error {
|
|
if !d.hasMetadata {
|
|
return ErrCallReadRestAfterReadMetadata
|
|
}
|
|
|
|
for {
|
|
page, _, err := d.readPage()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if (page.Header.HeaderType & FHeaderTypeEOS) > 0 {
|
|
// End of stream
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|