1
1
mirror of https://github.com/wader/fq.git synced 2024-12-23 21:31:33 +03:00
fq/format/opus/opus_packet.go
Mattias Wadman 9b81d4d3ab decode: More type safe API and split scalar into multiple types
Preparation to make decoder use less memory and API more type safe.
Now each scalar type has it's own struct type so it can store different
things and enables to have a scalar interface.
Also own types will enable experimenting with decode DLS designs like
using chained methods that are type aware.
2022-12-14 16:23:58 +01:00

125 lines
3.3 KiB
Go

package opus
// https://tools.ietf.org/html/rfc7845
import (
"bytes"
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
)
var vorbisComment decode.Group
func init() {
interp.RegisterFormat(decode.Format{
Name: format.OPUS_PACKET,
Description: "Opus packet",
DecodeFn: opusDecode,
Dependencies: []decode.Dependency{
{Names: []string{format.VORBIS_COMMENT}, Group: &vorbisComment},
},
})
}
func opusDecode(d *decode.D, _ any) any {
d.Endian = decode.LittleEndian
var prefix []byte
if d.BitsLeft() >= 8*8 {
prefix = d.PeekBytes(8)
}
switch {
case bytes.Equal(prefix, []byte("OpusHead")):
d.FieldValueStr("type", "head")
d.FieldUTF8("prefix", 8)
d.FieldU8("version")
channelCount := d.FieldU8("channel_count")
d.FieldU16("pre_skip")
d.FieldU32("sample_rate")
d.FieldU16("output_gain")
mapFamily := d.FieldU8("map_family")
if mapFamily != 0 {
d.FieldU8("stream_count")
d.FieldU8("coupled_count")
i := uint64(0)
d.FieldArrayLoop("channel_mappings", func() bool { return i < channelCount }, func(d *decode.D) {
d.FieldU8("channel_mapping")
})
}
case bytes.Equal(prefix, []byte("OpusTags")):
d.FieldValueStr("type", "tags")
d.FieldUTF8("prefix", 8)
d.FieldFormat("comment", vorbisComment, nil)
default:
d.FieldValueStr("type", "audio")
d.FieldStruct("toc", func(d *decode.D) {
d.FieldStruct("config", func(d *decode.D) {
configurations := map[uint64]struct {
mode string
bandwidth string
frameSize float64
}{
0: {"SILK-only", "NB", 10},
1: {"SILK-only", "NB", 20},
2: {"SILK-only", "NB", 40},
3: {"SILK-only", "NB", 60},
4: {"SILK-only", "MB", 10},
5: {"SILK-only", "MB", 20},
6: {"SILK-only", "MB", 40},
7: {"SILK-only", "MB", 60},
8: {"SILK-only", "WB", 10},
9: {"SILK-only", "WB", 20},
10: {"SILK-only", "WB", 40},
11: {"SILK-only", "WB", 60},
12: {"Hybrid", "SWB", 10},
13: {"Hybrid", "SWB", 20},
14: {"Hybrid", "FB", 10},
15: {"Hybrid", "FB", 20},
16: {"CELT-only", "NB", 2.5},
17: {"CELT-only", "NB", 5},
18: {"CELT-only", "NB", 10},
19: {"CELT-only", "NB", 20},
20: {"CELT-only", "WB", 2.5},
21: {"CELT-only", "WB", 5},
22: {"CELT-only", "WB", 10},
23: {"CELT-only", "WB", 20},
24: {"CELT-only", "SWB", 2.5},
25: {"CELT-only", "SWB", 5},
26: {"CELT-only", "SWB", 10},
27: {"CELT-only", "SWB", 20},
28: {"CELT-only", "FB", 2.5},
29: {"CELT-only", "FB", 5},
30: {"CELT-only", "FB", 10},
31: {"CELT-only", "FB", 20},
}
n := d.FieldU5("config")
config := configurations[n]
d.FieldValueStr("mode", config.mode)
d.FieldValueStr("bandwidth", config.bandwidth)
d.FieldValueFlt("frame_size", config.frameSize)
})
d.FieldBool("stereo")
d.FieldStruct("frames_per_packet", func(d *decode.D) {
framesPerPacketConfigs := map[uint64]struct {
frames uint64
mode string
}{
0: {1, "1 frame"},
1: {2, "2 frames, equal size"},
2: {2, "2 frames, different size"},
3: {0, "arbitrary number of frames"},
}
n := d.FieldU2("config")
config := framesPerPacketConfigs[n]
d.FieldValueUint("frames", config.frames)
d.FieldValueStr("mode", config.mode)
})
d.FieldRawLen("data", d.BitsLeft())
})
}
return nil
}