1
1
mirror of https://github.com/wader/fq.git synced 2024-11-22 15:45:45 +03:00
fq/format/ogg/ogg.go

181 lines
4.9 KiB
Go
Raw Normal View History

2020-06-08 03:29:51 +03:00
package ogg
// https://xiph.org/ogg/doc/framing.html
import (
"bytes"
"fmt"
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
2020-06-08 03:29:51 +03:00
)
var oggPageGroup decode.Group
var vorbisPacketGroup decode.Group
var opusPacketGroup decode.Group
var flacMetadatablockGroup decode.Group
var flacFrameGroup decode.Group
2020-06-08 03:29:51 +03:00
func init() {
interp.RegisterFormat(
format.Ogg,
&decode.Format{
Description: "OGG file",
Groups: []*decode.Group{format.Probe},
DecodeFn: decodeOgg,
Dependencies: []decode.Dependency{
2023-05-01 14:19:04 +03:00
{Groups: []*decode.Group{format.Ogg_Page}, Out: &oggPageGroup},
{Groups: []*decode.Group{format.Vorbis_Packet}, Out: &vorbisPacketGroup},
{Groups: []*decode.Group{format.Opus_Packet}, Out: &opusPacketGroup},
{Groups: []*decode.Group{format.FLAC_Metadatablock}, Out: &flacMetadatablockGroup},
{Groups: []*decode.Group{format.FLAC_Frame}, Out: &flacFrameGroup},
},
})
2020-06-08 03:29:51 +03:00
}
var (
vorbisIdentification = []byte("\x01vorbis")
opusIdentification = []byte("OpusHead")
flacIdentification = []byte("\x7fFLAC")
2020-06-08 03:29:51 +03:00
)
type streamCodec int
const (
codecUnknown streamCodec = iota
codecVorbis
codecOpus
codecFlac
2020-06-08 03:29:51 +03:00
)
type stream struct {
sequenceNo uint32
packetBuf []byte
packetD *decode.D
codec streamCodec
2023-05-01 14:19:04 +03:00
flacStreamInfo format.FLAC_Stream_Info
2020-06-08 03:29:51 +03:00
}
func decodeOgg(d *decode.D) any {
2020-06-08 03:29:51 +03:00
validPages := 0
streams := map[uint32]*stream{}
streamsD := d.FieldArrayValue("streams")
2020-06-08 03:29:51 +03:00
d.FieldArray("pages", func(d *decode.D) {
2020-06-08 03:29:51 +03:00
for !d.End() {
_, dv, _ := d.TryFieldFormat("page", &oggPageGroup, nil)
2020-06-08 03:29:51 +03:00
if dv == nil {
break
}
2023-05-01 14:19:04 +03:00
oggPageOut, ok := dv.(format.Ogg_Page_Out)
2020-06-08 03:29:51 +03:00
if !ok {
panic("page decode is not a oggPageOut")
}
s, sFound := streams[oggPageOut.StreamSerialNumber]
if !sFound {
var packetsD *decode.D
streamsD.FieldStruct("stream", func(d *decode.D) {
d.FieldValueUint("serial_number", uint64(oggPageOut.StreamSerialNumber))
packetsD = d.FieldArrayValue("packets")
2020-06-08 03:29:51 +03:00
})
s = &stream{
sequenceNo: oggPageOut.SequenceNo,
packetD: packetsD,
codec: codecUnknown,
}
streams[oggPageOut.StreamSerialNumber] = s
}
// if !sFound && !oggPageOut.IsFirstPage {
// // TODO: not first page and we haven't seen the stream before
// // log.Println("not first page and we haven't seen the stream before")
// }
// hasData := len(s.packetBuf) > 0
// if oggPageOut.IsContinuedPacket && !hasData {
// // TODO: continuation but we haven't seen any packet data yet
// // log.Println("continuation but we haven't seen any packet data yet")
// }
// if !oggPageOut.IsFirstPage && s.sequenceNo+1 != oggPageOut.SequenceNo {
// // TODO: page gap
// // log.Println("page gap")
// }
for _, bs := range oggPageOut.Segments {
s.packetBuf = append(s.packetBuf, bs...)
if len(bs) < 255 {
br := bitio.NewBitReader(s.packetBuf, -1)
2020-06-08 03:29:51 +03:00
if s.codec == codecUnknown {
if bytes.HasPrefix(s.packetBuf, vorbisIdentification) {
2020-06-08 03:29:51 +03:00
s.codec = codecVorbis
} else if bytes.HasPrefix(s.packetBuf, opusIdentification) {
2020-06-08 03:29:51 +03:00
s.codec = codecOpus
} else if bytes.HasPrefix(s.packetBuf, flacIdentification) {
s.codec = codecFlac
2020-06-08 03:29:51 +03:00
}
}
switch s.codec {
case codecVorbis:
// TODO: err
if _, _, err := s.packetD.TryFieldFormatBitBuf("packet", br, &vorbisPacketGroup, nil); err != nil {
s.packetD.FieldRootBitBuf("packet", br)
}
2020-06-08 03:29:51 +03:00
case codecOpus:
// TODO: err
if _, _, err := s.packetD.TryFieldFormatBitBuf("packet", br, &opusPacketGroup, nil); err != nil {
s.packetD.FieldRootBitBuf("packet", br)
}
case codecFlac:
if len(s.packetBuf) == 0 {
return
}
switch {
case s.packetBuf[0] == 0x7f:
s.packetD.FieldStructRootBitBufFn("packet", br, func(d *decode.D) {
d.FieldU8("type")
d.FieldUTF8("signature", 4)
d.FieldU8("major")
d.FieldU8("minor")
d.FieldU16("header_packets")
d.FieldUTF8("flac_signature", 4)
_, v := d.FieldFormat("metadatablock", &flacMetadatablockGroup, nil)
2023-05-01 14:19:04 +03:00
flacMetadatablockOut, ok := v.(format.FLAC_Metadatablock_Out)
if !ok {
panic(fmt.Sprintf("expected FlacMetadatablockOut, got %#+v", flacMetadatablockOut))
}
s.flacStreamInfo = flacMetadatablockOut.StreamInfo
})
case s.packetBuf[0] == 0xff:
s.packetD.FieldFormatBitBuf("packet", br, &flacFrameGroup, nil)
default:
s.packetD.FieldFormatBitBuf("packet", br, &flacMetadatablockGroup, nil)
}
2020-06-08 03:29:51 +03:00
case codecUnknown:
s.packetD.FieldRootBitBuf("packet", br)
2020-06-08 03:29:51 +03:00
}
s.packetBuf = nil
}
}
s.sequenceNo = oggPageOut.SequenceNo
if oggPageOut.IsLastPage {
delete(streams, oggPageOut.StreamSerialNumber)
}
validPages++
}
})
if validPages == 0 {
d.Fatalf("no pages found")
2020-06-08 03:29:51 +03:00
}
return nil
}