2020-06-08 03:29:51 +03:00
|
|
|
|
package mp4
|
|
|
|
|
|
2021-09-14 18:01:25 +03:00
|
|
|
|
// TODO: flags?
|
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
|
|
|
|
"time"
|
2021-08-17 13:06:32 +03:00
|
|
|
|
|
|
|
|
|
"github.com/wader/fq/format"
|
|
|
|
|
"github.com/wader/fq/pkg/decode"
|
2021-12-02 00:48:25 +03:00
|
|
|
|
"github.com/wader/fq/pkg/scalar"
|
2020-06-08 03:29:51 +03:00
|
|
|
|
)
|
|
|
|
|
|
2021-12-12 11:54:48 +03:00
|
|
|
|
// TODO: keep track of list of sampleSize/entries instead and change sample read code
|
|
|
|
|
const maxSampleEntryCount = 10_000_000
|
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var boxAliases = map[string]string{
|
|
|
|
|
"styp": "ftyp",
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 17:04:26 +03:00
|
|
|
|
const (
|
|
|
|
|
boxSizeRestOfFile = 0
|
|
|
|
|
boxSizeUse64bitSize = 1
|
|
|
|
|
)
|
|
|
|
|
|
2022-04-07 19:25:23 +03:00
|
|
|
|
var boxSizeNames = scalar.UToDescription{
|
|
|
|
|
boxSizeRestOfFile: "Rest of file",
|
|
|
|
|
boxSizeUse64bitSize: "Use 64 bit size",
|
2021-11-05 17:04:26 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 19:25:23 +03:00
|
|
|
|
var mediaTimeNames = scalar.SToDescription{
|
|
|
|
|
-1: "empty",
|
2021-11-05 17:04:26 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 19:25:23 +03:00
|
|
|
|
var subTypeNames = scalar.StrToDescription{
|
|
|
|
|
"alis": "Alias Data",
|
|
|
|
|
"camm": "Camera Metadata",
|
|
|
|
|
"crsm": "Clock Reference",
|
|
|
|
|
"data": "Data",
|
|
|
|
|
"hint": "Hint Track",
|
|
|
|
|
"ipsm": "IPMP",
|
|
|
|
|
"m7sm": "MPEG-7 Stream",
|
|
|
|
|
"mdir": "Metadata",
|
|
|
|
|
"mdta": "Metadata Tags",
|
|
|
|
|
"meta": "NRT Metadata",
|
|
|
|
|
"mjsm": "MPEG-J",
|
|
|
|
|
"nrtm": "Non-Real Time Metadata",
|
|
|
|
|
"ocsm": "Object Content",
|
|
|
|
|
"odsm": "Object Descriptor",
|
|
|
|
|
"pict": "Picture",
|
|
|
|
|
"priv": "Private",
|
|
|
|
|
"psmd": "Panasonic Static Metadata",
|
|
|
|
|
"sbtl": "Subtitle",
|
|
|
|
|
"sdsm": "Scene Description",
|
|
|
|
|
"soun": "Audio Track",
|
|
|
|
|
"subp": "Subpicture",
|
|
|
|
|
"text": "Text",
|
|
|
|
|
"tmcd": "Time Code",
|
|
|
|
|
"url ": "URL",
|
|
|
|
|
"vide": "Video Track",
|
2021-11-05 17:04:26 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
uuidIsmlManifestBytes = [16]byte{0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd, 0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66}
|
|
|
|
|
uuidXmpBytes = [16]byte{0xbe, 0x7a, 0xcf, 0xcb, 0x97, 0xa9, 0x42, 0xe8, 0x9c, 0x71, 0x99, 0x94, 0x91, 0xe3, 0xaf, 0xac}
|
|
|
|
|
uuidSphericalBytes = [16]byte{0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93, 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd}
|
|
|
|
|
uuidPspUsmtBytes = [16]byte{0x55, 0x53, 0x4d, 0x54, 0x21, 0xd2, 0x4f, 0xce, 0xbb, 0x88, 0x69, 0x5c, 0xfa, 0xc9, 0xc7, 0x40}
|
|
|
|
|
uuidTfxdBytes = [16]byte{0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6, 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2}
|
|
|
|
|
uuidTfrfBytes = [16]byte{0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95, 0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f}
|
|
|
|
|
uuidProfBytes = [16]byte{0x50, 0x52, 0x4f, 0x46, 0x21, 0xd2, 0x4f, 0xce, 0xbb, 0x88, 0x69, 0x5c, 0xfa, 0xc9, 0xc7, 0x40}
|
|
|
|
|
uuidIpodBytes = [16]byte{0x6b, 0x68, 0x40, 0xf2, 0x5f, 0x24, 0x4f, 0xc5, 0xba, 0x39, 0xa5, 0x1b, 0xcf, 0x03, 0x23, 0xf3}
|
|
|
|
|
)
|
|
|
|
|
|
2021-12-02 00:48:25 +03:00
|
|
|
|
var uuidNames = scalar.BytesToScalar{
|
|
|
|
|
{Bytes: uuidIsmlManifestBytes[:], Scalar: scalar.S{Sym: "isml_manifest"}},
|
|
|
|
|
{Bytes: uuidXmpBytes[:], Scalar: scalar.S{Sym: "xmp"}},
|
|
|
|
|
{Bytes: uuidSphericalBytes[:], Scalar: scalar.S{Sym: "spherical"}},
|
|
|
|
|
{Bytes: uuidPspUsmtBytes[:], Scalar: scalar.S{Sym: "psp_usmt"}},
|
|
|
|
|
{Bytes: uuidTfxdBytes[:], Scalar: scalar.S{Sym: "tfxd"}},
|
|
|
|
|
{Bytes: uuidTfrfBytes[:], Scalar: scalar.S{Sym: "tfrf"}},
|
|
|
|
|
{Bytes: uuidProfBytes[:], Scalar: scalar.S{Sym: "prof"}},
|
|
|
|
|
{Bytes: uuidIpodBytes[:], Scalar: scalar.S{Sym: "ipod"}},
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ISO 639-2/T language code 3 * 5bit packed uint + 1 zero bit
|
|
|
|
|
func decodeLang(d *decode.D) string {
|
|
|
|
|
d.U1()
|
|
|
|
|
return string([]byte{
|
|
|
|
|
byte(d.U5()) + 0x60,
|
|
|
|
|
byte(d.U5()) + 0x60,
|
|
|
|
|
byte(d.U5()) + 0x60},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Quicktime time seconds in January 1, 1904 UTC
|
2021-11-05 17:04:26 +03:00
|
|
|
|
var quicktimeEpochDate = time.Date(1904, time.January, 4, 0, 0, 0, 0, time.UTC)
|
|
|
|
|
|
2021-12-02 00:48:25 +03:00
|
|
|
|
var quicktimeEpoch = scalar.Fn(func(s scalar.S) (scalar.S, error) {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
uv, ok := s.Actual.(uint64)
|
|
|
|
|
if !ok {
|
|
|
|
|
return s, nil
|
|
|
|
|
}
|
|
|
|
|
s.Sym = quicktimeEpochDate.Add(time.Second * time.Duration(uv)).Format(time.RFC3339)
|
|
|
|
|
return s, nil
|
2021-12-02 00:48:25 +03:00
|
|
|
|
})
|
2020-06-08 03:29:51 +03:00
|
|
|
|
|
|
|
|
|
func decodeFieldMatrix(d *decode.D, name string) {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct(name, func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldFP32("a")
|
|
|
|
|
d.FieldFP32("b")
|
|
|
|
|
d.FieldFP("u", 32, 30)
|
|
|
|
|
d.FieldFP32("c")
|
|
|
|
|
d.FieldFP32("d")
|
|
|
|
|
d.FieldFP("v", 32, 30)
|
|
|
|
|
d.FieldFP32("x")
|
|
|
|
|
d.FieldFP32("y")
|
|
|
|
|
d.FieldFP("w", 32, 30)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 19:25:23 +03:00
|
|
|
|
func decodeBoxWithParentData(ctx *decodeContext, d *decode.D, parentData interface{}) {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
var typ string
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var dataSize uint64
|
|
|
|
|
|
2021-12-02 00:48:25 +03:00
|
|
|
|
boxSize := d.FieldU32("size", boxSizeNames)
|
|
|
|
|
typ = d.FieldUTF8("type", 4, boxDescriptions)
|
2021-11-05 17:04:26 +03:00
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
|
switch boxSize {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
case boxSizeRestOfFile:
|
2020-06-08 03:29:51 +03:00
|
|
|
|
dataSize = uint64(d.Len()-d.Pos()) / 8
|
2021-11-05 17:04:26 +03:00
|
|
|
|
case boxSizeUse64bitSize:
|
2021-11-23 13:24:09 +03:00
|
|
|
|
boxSize = d.FieldU64("size64")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
dataSize = boxSize - 16
|
|
|
|
|
default:
|
|
|
|
|
dataSize = boxSize - 8
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-14 15:34:52 +03:00
|
|
|
|
// TODO: add truncate to size option?
|
|
|
|
|
// if dataSize > uint64(d.BitsLeft()/8) {
|
|
|
|
|
// dataSize = uint64(d.BitsLeft() / 8)
|
|
|
|
|
// }
|
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
|
// TODO: not sure about this
|
|
|
|
|
switch {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
case typ == "<22>too":
|
2020-06-08 03:29:51 +03:00
|
|
|
|
typ = "_apple_list"
|
|
|
|
|
case typ[0] == 0xa9:
|
|
|
|
|
typ = "_apple_entry"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if a, ok := boxAliases[typ]; ok {
|
|
|
|
|
typ = a
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 19:25:23 +03:00
|
|
|
|
if parentData != nil {
|
|
|
|
|
ctx.path[len(ctx.path)-1].data = parentData
|
|
|
|
|
}
|
|
|
|
|
ctx.path = append(ctx.path, pathEntry{typ: typ, data: parentData})
|
2020-06-08 03:29:51 +03:00
|
|
|
|
|
|
|
|
|
if decodeFn, ok := boxDecoders[typ]; ok {
|
2022-01-13 20:34:59 +03:00
|
|
|
|
d.FramedFn(int64(dataSize*8), func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
decodeFn(ctx, d)
|
|
|
|
|
})
|
|
|
|
|
} else {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", int64(dataSize*8))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.path = ctx.path[0 : len(ctx.path)-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func decodeBoxes(ctx *decodeContext, d *decode.D) {
|
2022-04-07 19:25:23 +03:00
|
|
|
|
decodeBoxesWithParentData(ctx, d, nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func decodeBoxesWithParentData(ctx *decodeContext, d *decode.D, parentData interface{}) {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStructArrayLoop("boxes", "box", func() bool { return d.BitsLeft() >= 8*8 }, func(d *decode.D) {
|
2022-04-07 19:25:23 +03:00
|
|
|
|
decodeBoxWithParentData(ctx, d, parentData)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if d.BitsLeft() > 0 {
|
|
|
|
|
// "Some sample descriptions terminate with four zero bytes that are not otherwise indicated."
|
|
|
|
|
if d.BitsLeft() >= 32 && d.PeekBits(32) == 0 {
|
|
|
|
|
d.FieldU32("zero_terminator")
|
|
|
|
|
}
|
|
|
|
|
if d.BitsLeft() > 0 {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("padding", d.BitsLeft())
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var boxDecoders map[string]func(ctx *decodeContext, d *decode.D)
|
|
|
|
|
|
2022-04-07 19:25:23 +03:00
|
|
|
|
type irefBox struct {
|
|
|
|
|
version int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func irefEntryDecode(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
irefBox, ok := ctx.parent().data.(irefBox)
|
|
|
|
|
if !ok {
|
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idSize := 16
|
|
|
|
|
if irefBox.version != 0 {
|
|
|
|
|
idSize = 32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d.FieldU("from_id", idSize)
|
|
|
|
|
count := d.FieldU16("count")
|
|
|
|
|
|
|
|
|
|
d.FieldArray("ids", func(d *decode.D) {
|
|
|
|
|
for i := uint64(0); i < count; i++ {
|
|
|
|
|
d.FieldU("id", idSize)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
|
func init() {
|
|
|
|
|
boxDecoders = map[string]func(ctx *decodeContext, d *decode.D){
|
|
|
|
|
"ftyp": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldUTF8("major_brand", 4)
|
|
|
|
|
d.FieldU32("minor_version")
|
|
|
|
|
numBrands := d.BitsLeft() / 8 / 4
|
|
|
|
|
var i int64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArrayLoop("brands", func() bool { return i < numBrands }, func(d *decode.D) {
|
2021-12-02 00:48:25 +03:00
|
|
|
|
d.FieldUTF8("brand", 4, brandDescriptions, scalar.TrimSpace)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"mvhd": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
2021-09-17 23:13:15 +03:00
|
|
|
|
d.FieldU24("flags")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldU32("creation_time", quicktimeEpoch)
|
|
|
|
|
d.FieldU32("modification_time", quicktimeEpoch)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU32("time_scale")
|
|
|
|
|
d.FieldU32("duration")
|
|
|
|
|
d.FieldFP32("preferred_rate")
|
|
|
|
|
d.FieldFP16("preferred_volume")
|
|
|
|
|
d.FieldUTF8("reserved", 10)
|
|
|
|
|
decodeFieldMatrix(d, "matrix_structure")
|
|
|
|
|
d.FieldU32("preview_time")
|
|
|
|
|
d.FieldU32("preview_duration")
|
|
|
|
|
d.FieldU32("poster_time")
|
|
|
|
|
d.FieldU32("selection_time")
|
|
|
|
|
d.FieldU32("selection_duration")
|
|
|
|
|
d.FieldU32("current_time")
|
|
|
|
|
d.FieldU32("next_track_id")
|
|
|
|
|
},
|
|
|
|
|
"trak": decodeBoxes,
|
|
|
|
|
"edts": decodeBoxes,
|
|
|
|
|
"elst": func(_ *decodeContext, d *decode.D) {
|
2021-08-12 13:51:17 +03:00
|
|
|
|
version := d.FieldU8("version")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStructArrayLoop("entries", "entry", func() bool { return i < entryCount }, func(d *decode.D) {
|
2021-08-12 13:51:17 +03:00
|
|
|
|
d.FieldS32("segment_duration")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldSFn("media_time", func(d *decode.D) int64 {
|
2021-08-12 13:51:17 +03:00
|
|
|
|
var t int64
|
|
|
|
|
if version == 0 {
|
|
|
|
|
t = d.S32()
|
|
|
|
|
} else {
|
|
|
|
|
t = d.S64()
|
|
|
|
|
}
|
2021-11-05 17:04:26 +03:00
|
|
|
|
return t
|
2021-12-02 00:48:25 +03:00
|
|
|
|
}, mediaTimeNames)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldFP32("media_rate")
|
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"tref": decodeBoxes,
|
|
|
|
|
"tkhd": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldU32("creation_time", quicktimeEpoch)
|
|
|
|
|
d.FieldU32("modification_time", quicktimeEpoch)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
trackID := uint32(d.FieldU32("track_id"))
|
|
|
|
|
d.FieldU32("reserved1")
|
|
|
|
|
d.FieldU32("duration")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("reserved2", 8*8)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU16("layer")
|
|
|
|
|
d.FieldU16("alternate_group")
|
|
|
|
|
d.FieldFP16("volume")
|
|
|
|
|
d.FieldU16("reserved3")
|
|
|
|
|
decodeFieldMatrix(d, "matrix_structure")
|
|
|
|
|
d.FieldFP32("track_width")
|
|
|
|
|
d.FieldFP32("track_height")
|
|
|
|
|
|
|
|
|
|
// TODO: dup track id?
|
|
|
|
|
if _, ok := ctx.tracks[trackID]; !ok {
|
2022-01-17 15:32:51 +03:00
|
|
|
|
t := &track{id: int(trackID)}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
ctx.tracks[trackID] = t
|
|
|
|
|
ctx.currentTrack = t
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"mdia": decodeBoxes,
|
|
|
|
|
"mdhd": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
// TODO: timestamps
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldU32("creation_time", quicktimeEpoch)
|
|
|
|
|
d.FieldU32("modification_time", quicktimeEpoch)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU32("time_scale")
|
|
|
|
|
d.FieldU32("duration")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStrFn("language", decodeLang)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU16("quality")
|
|
|
|
|
},
|
2021-09-03 16:48:03 +03:00
|
|
|
|
"vmhd": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU16("graphicsmode")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("opcolor", func(d *decode.D) {
|
2021-11-19 17:24:53 +03:00
|
|
|
|
// TODO: is FP16?
|
2021-09-03 16:48:03 +03:00
|
|
|
|
d.FieldU16("value")
|
|
|
|
|
d.FieldU16("value")
|
|
|
|
|
d.FieldU16("value")
|
|
|
|
|
})
|
|
|
|
|
},
|
2020-06-08 03:29:51 +03:00
|
|
|
|
"hdlr": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-11-17 18:13:10 +03:00
|
|
|
|
d.FieldUTF8NullFixedLen("component_type", 4)
|
2021-12-02 00:48:25 +03:00
|
|
|
|
subType := d.FieldUTF8("component_subtype", 4, subTypeNames, scalar.TrimSpace)
|
2021-11-17 18:13:10 +03:00
|
|
|
|
d.FieldUTF8NullFixedLen("component_manufacturer", 4)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU32("component_flags")
|
|
|
|
|
d.FieldU32("component_flags_mask")
|
2021-12-08 12:45:10 +03:00
|
|
|
|
// TODO: sometimes has a length prefix byte, how to know?
|
2021-11-17 18:13:10 +03:00
|
|
|
|
d.FieldUTF8NullFixedLen("component_name", int(d.BitsLeft()/8))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
|
|
|
|
|
if ctx.currentTrack != nil {
|
|
|
|
|
// component_type seems to be all zero sometimes so can't look for "mhlr"
|
|
|
|
|
switch subType {
|
|
|
|
|
case "vide", "soun":
|
|
|
|
|
ctx.currentTrack.subType = subType
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"minf": decodeBoxes,
|
|
|
|
|
"dinf": decodeBoxes,
|
|
|
|
|
"dref": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStructArrayLoop("boxes", "box", func() bool { return i < entryCount }, func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
size := d.FieldU32("size")
|
|
|
|
|
d.FieldUTF8("type", 4)
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
dataSize := size - 12
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", int64(dataSize*8))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"stbl": decodeBoxes,
|
|
|
|
|
"stsd": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
|
|
|
|
// note called "boxes" here instead of "sample_descriptions" and data format is named "type".
|
|
|
|
|
// this is to make it easier to threat them as normal boxes
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArrayLoop("boxes", func() bool { return i < entryCount }, func(d *decode.D) {
|
|
|
|
|
d.FieldStruct("box", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
size := d.FieldU32("size")
|
|
|
|
|
dataFormat := d.FieldUTF8("type", 4)
|
|
|
|
|
subType := ""
|
|
|
|
|
if ctx.currentTrack != nil {
|
|
|
|
|
ctx.currentTrack.sampleDescriptions = append(ctx.currentTrack.sampleDescriptions, sampleDescription{
|
|
|
|
|
dataFormat: dataFormat,
|
|
|
|
|
})
|
|
|
|
|
subType = ctx.currentTrack.subType
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-13 20:34:59 +03:00
|
|
|
|
d.FramedFn(int64(size-8)*8, func(d *decode.D) {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("reserved", 6*8)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU16("data_reference_index")
|
|
|
|
|
|
|
|
|
|
switch subType {
|
|
|
|
|
case "soun", "vide":
|
|
|
|
|
|
|
|
|
|
version := d.FieldU16("version")
|
|
|
|
|
d.FieldU16("revision_level")
|
2021-09-14 18:01:25 +03:00
|
|
|
|
d.FieldU32("max_packet_size") // TODO: vendor for some subtype?
|
2020-06-08 03:29:51 +03:00
|
|
|
|
|
|
|
|
|
switch subType {
|
|
|
|
|
case "soun":
|
|
|
|
|
// AudioSampleEntry
|
|
|
|
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW1
|
|
|
|
|
switch version {
|
|
|
|
|
case 0:
|
|
|
|
|
d.FieldU16("num_audio_channels")
|
|
|
|
|
d.FieldU16("sample_size")
|
|
|
|
|
d.FieldU16("compression_id")
|
|
|
|
|
d.FieldU16("packet_size")
|
|
|
|
|
d.FieldFP32("sample_rate")
|
|
|
|
|
if d.BitsLeft() > 0 {
|
|
|
|
|
decodeBoxes(ctx, d)
|
|
|
|
|
}
|
|
|
|
|
case 1:
|
|
|
|
|
d.FieldU16("num_audio_channels")
|
|
|
|
|
d.FieldU16("sample_size")
|
|
|
|
|
d.FieldU16("compression_id")
|
|
|
|
|
d.FieldU16("packet_size")
|
|
|
|
|
d.FieldFP32("sample_rate")
|
|
|
|
|
d.FieldU32("samples_per_packet")
|
|
|
|
|
d.FieldU32("bytes_per_packet")
|
|
|
|
|
d.FieldU32("bytes_per_frame")
|
|
|
|
|
d.FieldU32("bytes_per_sample")
|
|
|
|
|
if d.BitsLeft() > 0 {
|
|
|
|
|
decodeBoxes(ctx, d)
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
|
|
|
|
d.FieldU16("always_3")
|
|
|
|
|
d.FieldU16("always_16")
|
2021-11-28 18:59:45 +03:00
|
|
|
|
d.FieldU16("always_minus_2") // TODO: as in const -2?
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU32("always_0")
|
|
|
|
|
d.FieldU32("always_65536")
|
|
|
|
|
d.FieldU32("size_of_struct_only")
|
|
|
|
|
d.FieldF64("audio_sample_rate")
|
|
|
|
|
d.FieldU32("num_audio_channels")
|
|
|
|
|
d.FieldU32("always_7f000000")
|
|
|
|
|
d.FieldU32("const_bits_per_channel")
|
|
|
|
|
d.FieldU32("format_specific_flags")
|
|
|
|
|
d.FieldU32("const_bytes_per_audio_packet")
|
|
|
|
|
d.FieldU32("const_lpcm_frames_per_audio_packet")
|
|
|
|
|
if d.BitsLeft() > 0 {
|
|
|
|
|
decodeBoxes(ctx, d)
|
|
|
|
|
}
|
|
|
|
|
default:
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
case "vide":
|
|
|
|
|
// VideoSampleEntry
|
|
|
|
|
// TODO: version 0 and 1 same?
|
|
|
|
|
switch version {
|
|
|
|
|
case 0, 1:
|
|
|
|
|
d.FieldU32("temporal_quality")
|
|
|
|
|
d.FieldU32("spatial_quality")
|
|
|
|
|
d.FieldU16("width")
|
|
|
|
|
d.FieldU16("height")
|
|
|
|
|
d.FieldFP32("horizontal_resolution")
|
|
|
|
|
d.FieldFP32("vertical_resolution")
|
|
|
|
|
d.FieldU32("data_size")
|
|
|
|
|
d.FieldU16("frame_count")
|
2021-11-17 18:13:10 +03:00
|
|
|
|
d.FieldUTF8ShortStringFixedLen("compressor_name", 32)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU16("depth")
|
|
|
|
|
d.FieldS16("color_table_id")
|
|
|
|
|
// TODO: if 0 decode ctab
|
|
|
|
|
if d.BitsLeft() > 0 {
|
|
|
|
|
decodeBoxes(ctx, d)
|
|
|
|
|
}
|
|
|
|
|
default:
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
// case "hint": TODO: Hint entry
|
|
|
|
|
default:
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
default:
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"avcC": func(ctx *decodeContext, d *decode.D) {
|
2021-11-19 18:44:06 +03:00
|
|
|
|
dv, v := d.FieldFormat("descriptor", mpegAVCDCRFormat, nil)
|
|
|
|
|
avcDcrOut, ok := v.(format.AvcDcrOut)
|
|
|
|
|
if dv != nil && !ok {
|
2021-11-16 19:11:26 +03:00
|
|
|
|
panic(fmt.Sprintf("expected AvcDcrOut got %#+v", v))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
if ctx.currentTrack != nil {
|
2021-09-16 16:29:11 +03:00
|
|
|
|
ctx.currentTrack.formatInArg = format.AvcIn{LengthSize: avcDcrOut.LengthSize} //nolint:gosimple
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"hvcC": func(ctx *decodeContext, d *decode.D) {
|
2021-11-19 18:44:06 +03:00
|
|
|
|
dv, v := d.FieldFormat("descriptor", mpegHEVCDCRFrameFormat, nil)
|
|
|
|
|
hevcDcrOut, ok := v.(format.HevcDcrOut)
|
|
|
|
|
if dv != nil && !ok {
|
2021-11-16 19:11:26 +03:00
|
|
|
|
panic(fmt.Sprintf("expected HevcDcrOut got %#+v", v))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
if ctx.currentTrack != nil {
|
2021-09-16 16:29:11 +03:00
|
|
|
|
ctx.currentTrack.formatInArg = format.HevcIn{LengthSize: hevcDcrOut.LengthSize} //nolint:gosimple
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"dfLa": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-11-19 18:44:06 +03:00
|
|
|
|
dv, v := d.FieldFormat("descriptor", flacMetadatablocksFormat, nil)
|
|
|
|
|
flacMetadatablockOut, ok := v.(format.FlacMetadatablocksOut)
|
|
|
|
|
if dv != nil && !ok {
|
2021-11-16 19:11:26 +03:00
|
|
|
|
panic(fmt.Sprintf("expected FlacMetadatablockOut got %#+v", v))
|
2021-09-17 16:46:13 +03:00
|
|
|
|
}
|
|
|
|
|
if flacMetadatablockOut.HasStreamInfo {
|
|
|
|
|
if ctx.currentTrack != nil {
|
|
|
|
|
ctx.currentTrack.formatInArg = format.FlacFrameIn{StreamInfo: flacMetadatablockOut.StreamInfo}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
2021-09-17 16:46:13 +03:00
|
|
|
|
}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"dOps": func(_ *decodeContext, d *decode.D) {
|
2021-09-17 16:51:37 +03:00
|
|
|
|
d.FieldFormat("descriptor", opusPacketFrameFormat, nil)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"av1C": func(_ *decodeContext, d *decode.D) {
|
2021-09-17 16:51:37 +03:00
|
|
|
|
d.FieldFormat("descriptor", av1CCRFormat, nil)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"vpcC": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-09-17 16:51:37 +03:00
|
|
|
|
d.FieldFormat("descriptor", vpxCCRFormat, nil)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"esds": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU32("version")
|
|
|
|
|
|
2021-11-19 18:44:06 +03:00
|
|
|
|
dv, v := d.FieldFormat("descriptor", mpegESFormat, nil)
|
|
|
|
|
mpegEsOut, ok := v.(format.MpegEsOut)
|
|
|
|
|
if dv != nil && !ok {
|
2021-11-16 19:11:26 +03:00
|
|
|
|
panic(fmt.Sprintf("expected mpegEsOut got %#+v", v))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ctx.currentTrack != nil && len(mpegEsOut.DecoderConfigs) > 0 {
|
|
|
|
|
dc := mpegEsOut.DecoderConfigs[0]
|
|
|
|
|
ctx.currentTrack.objectType = dc.ObjectType
|
2021-09-16 16:29:11 +03:00
|
|
|
|
ctx.currentTrack.formatInArg = format.AACFrameIn{ObjectType: dc.ASCObjectType}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"stts": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
numEntries := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStructArrayLoop("entries", "entry", func() bool { return i < numEntries }, func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU32("count")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
d.FieldU32("delta")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"stsc": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStructArrayLoop("entries", "entry", func() bool { return i < entryCount }, func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
firstChunk := uint32(d.FieldU32("first_chunk"))
|
|
|
|
|
samplesPerChunk := uint32(d.FieldU32("samples_per_chunk"))
|
|
|
|
|
d.FieldU32("sample_description_id")
|
|
|
|
|
|
|
|
|
|
if ctx.currentTrack != nil {
|
|
|
|
|
ctx.currentTrack.stsc = append(ctx.currentTrack.stsc, stsc{
|
2022-01-17 15:32:51 +03:00
|
|
|
|
firstChunk: int(firstChunk),
|
|
|
|
|
samplesPerChunk: int(samplesPerChunk),
|
2020-06-08 03:29:51 +03:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"stsz": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
// TODO: bytes_per_sample from audio stsd?
|
|
|
|
|
sampleSize := d.FieldU32("sample_size")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2022-02-07 14:35:11 +03:00
|
|
|
|
if ctx.currentTrack != nil && len(ctx.currentTrack.stsz) > 0 {
|
|
|
|
|
d.Errorf("multiple stsz or stz2 boxes")
|
|
|
|
|
}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
if sampleSize == 0 {
|
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArrayLoop("entries", func() bool { return i < entryCount }, func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
size := uint32(d.FieldU32("size"))
|
|
|
|
|
if ctx.currentTrack != nil {
|
2022-01-15 19:42:50 +03:00
|
|
|
|
ctx.currentTrack.stsz = append(ctx.currentTrack.stsz, stsz{
|
2022-01-17 15:32:51 +03:00
|
|
|
|
size: int64(size),
|
2022-01-15 19:42:50 +03:00
|
|
|
|
count: 1,
|
|
|
|
|
})
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
if ctx.currentTrack != nil {
|
2022-01-15 19:42:50 +03:00
|
|
|
|
ctx.currentTrack.stsz = append(ctx.currentTrack.stsz, stsz{
|
2022-01-17 15:32:51 +03:00
|
|
|
|
size: int64(sampleSize),
|
|
|
|
|
count: int(entryCount),
|
2022-01-15 19:42:50 +03:00
|
|
|
|
})
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
2022-02-07 14:35:11 +03:00
|
|
|
|
"stz2": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
fieldSize := d.FieldU32("field_size")
|
|
|
|
|
if fieldSize > 16 {
|
|
|
|
|
d.Errorf("field_size %d > 16", fieldSize)
|
|
|
|
|
}
|
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
|
|
|
|
var i uint64
|
|
|
|
|
if ctx.currentTrack != nil && len(ctx.currentTrack.stsz) > 0 {
|
|
|
|
|
d.Errorf("multiple stsz or stz2 boxes")
|
|
|
|
|
}
|
|
|
|
|
d.FieldArrayLoop("entries", func() bool { return i < entryCount }, func(d *decode.D) {
|
|
|
|
|
size := uint32(d.FieldU("size", int(fieldSize)))
|
|
|
|
|
if ctx.currentTrack != nil {
|
|
|
|
|
ctx.currentTrack.stsz = append(ctx.currentTrack.stsz, stsz{
|
|
|
|
|
size: int64(size),
|
|
|
|
|
count: 1,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
2020-06-08 03:29:51 +03:00
|
|
|
|
"stco": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArrayLoop("entries", func() bool { return i < entryCount }, func(d *decode.D) {
|
2021-08-12 13:51:17 +03:00
|
|
|
|
chunkOffset := d.FieldU32("chunk_offset")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
if ctx.currentTrack != nil {
|
2022-01-17 15:32:51 +03:00
|
|
|
|
ctx.currentTrack.stco = append(ctx.currentTrack.stco, int64(chunkOffset))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"stss": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
2021-08-12 13:51:17 +03:00
|
|
|
|
for i := uint64(0); i < entryCount; i++ {
|
|
|
|
|
d.FieldU32("sample_number")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"sdtp": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
// TODO: should be count from stsz
|
2021-09-03 16:48:03 +03:00
|
|
|
|
// TODO: can we know count here or do we need to defer decoding somehow?
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for d.NotEnd() {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("entry", func(d *decode.D) {
|
2021-09-03 16:48:03 +03:00
|
|
|
|
d.FieldU2("reserved")
|
2021-12-02 00:48:25 +03:00
|
|
|
|
values := scalar.UToSymStr{
|
2021-09-03 16:48:03 +03:00
|
|
|
|
0: "unknown",
|
|
|
|
|
1: "yes",
|
|
|
|
|
2: "no",
|
|
|
|
|
}
|
2021-12-02 00:48:25 +03:00
|
|
|
|
d.FieldU2("sample_depends_on", values)
|
|
|
|
|
d.FieldU2("sample_is_depended_on", values)
|
|
|
|
|
d.FieldU2("sample_has_redundancy", values)
|
2021-09-03 16:48:03 +03:00
|
|
|
|
})
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"ctts": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStructArrayLoop("entries", "entry", func() bool { return i < entryCount }, func(d *decode.D) {
|
2021-12-20 12:59:16 +03:00
|
|
|
|
d.FieldS32("sample_count")
|
|
|
|
|
d.FieldS32("sample_offset")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
// TODO: refactor: merge with stco?
|
|
|
|
|
"co64": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArrayLoop("entries", func() bool { return i < entryCount }, func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
offset := d.FieldU64("offset")
|
|
|
|
|
if ctx.currentTrack != nil {
|
2022-01-17 15:32:51 +03:00
|
|
|
|
ctx.currentTrack.stco = append(ctx.currentTrack.stco, int64(offset))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"sidx": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU32("reference_id")
|
|
|
|
|
d.FieldU32("timescale")
|
|
|
|
|
if version == 0 {
|
|
|
|
|
d.FieldU32("pts")
|
|
|
|
|
d.FieldU32("offset")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU64("pts")
|
|
|
|
|
d.FieldU64("offset")
|
|
|
|
|
}
|
|
|
|
|
d.FieldU16("reserved")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
numEntries := d.FieldU16("entry_count")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
var i uint64
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStructArrayLoop("entries", "entry", func() bool { return i < numEntries }, func(d *decode.D) {
|
2021-08-12 13:51:17 +03:00
|
|
|
|
d.FieldU1("reference_type")
|
|
|
|
|
d.FieldU31("size")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU32("duration")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
d.FieldU1("starts_with_sap")
|
|
|
|
|
d.FieldU3("sap_type")
|
|
|
|
|
d.FieldU28("sap_delta_time")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
i++
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"udta": decodeBoxes,
|
|
|
|
|
"meta": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
// TODO: meta box sometimes has a 4 byte unknown field? (flag/version?)
|
|
|
|
|
maybeFlags := d.PeekBits(32)
|
|
|
|
|
if maybeFlags == 0 {
|
|
|
|
|
// TODO: rename?
|
|
|
|
|
d.FieldU32("maybe_flags")
|
|
|
|
|
}
|
|
|
|
|
decodeBoxes(ctx, d)
|
|
|
|
|
},
|
|
|
|
|
"ilst": decodeBoxes,
|
|
|
|
|
"_apple_list": decodeBoxes,
|
|
|
|
|
"_apple_entry": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldUTF8("data", int(d.BitsLeft()/8))
|
|
|
|
|
},
|
|
|
|
|
"data": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU32("reserved")
|
2022-04-07 19:25:23 +03:00
|
|
|
|
if ctx.isParent("covr") {
|
2022-04-11 23:45:46 +03:00
|
|
|
|
d.FieldFormatOrRawLen("data", d.BitsLeft(), imageFormat, nil)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
} else {
|
|
|
|
|
d.FieldUTF8("data", int(d.BitsLeft()/8))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"moov": decodeBoxes,
|
|
|
|
|
"moof": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
ctx.currentMoofOffset = (d.Pos() / 8) - 8
|
|
|
|
|
decodeBoxes(ctx, d)
|
|
|
|
|
},
|
|
|
|
|
// Track Fragment
|
|
|
|
|
"traf": decodeBoxes,
|
|
|
|
|
// Movie Fragment Header
|
|
|
|
|
"mfhd": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU32("sequence_number")
|
|
|
|
|
},
|
|
|
|
|
// Track Fragment Header
|
|
|
|
|
"tfhd": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
baseDataOffsetPresent := false
|
|
|
|
|
sampleDescriptionIndexPresent := false
|
|
|
|
|
defaultSampleDurationPresent := false
|
|
|
|
|
defaultSampleSizePresent := false
|
|
|
|
|
defaultSampleFlagsPresent := false
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("flags", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU7("unused0")
|
|
|
|
|
d.FieldBool("duration_is_empty")
|
|
|
|
|
d.FieldU10("unused1")
|
|
|
|
|
defaultSampleFlagsPresent = d.FieldBool("default_sample_flags_present")
|
|
|
|
|
defaultSampleSizePresent = d.FieldBool("default_sample_size_present")
|
|
|
|
|
defaultSampleDurationPresent = d.FieldBool("default_sample_duration_present")
|
|
|
|
|
d.FieldU1("unused2")
|
|
|
|
|
sampleDescriptionIndexPresent = d.FieldBool("sample_description_index_present")
|
|
|
|
|
baseDataOffsetPresent = d.FieldBool("base_data_offset_present")
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
trackID := uint32(d.FieldU32("track_id"))
|
|
|
|
|
m := &moof{}
|
|
|
|
|
ctx.currentTrack = ctx.tracks[trackID]
|
|
|
|
|
if ctx.currentTrack != nil {
|
|
|
|
|
ctx.currentTrack.moofs = append(ctx.currentTrack.moofs, m)
|
|
|
|
|
ctx.currentTrack.currentMoof = m
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if baseDataOffsetPresent {
|
|
|
|
|
d.FieldU64("base_data_offset")
|
|
|
|
|
}
|
|
|
|
|
if sampleDescriptionIndexPresent {
|
2022-01-17 15:32:51 +03:00
|
|
|
|
m.defaultSampleDescriptionIndex = int(d.FieldU32("sample_description_index"))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
if defaultSampleDurationPresent {
|
|
|
|
|
d.FieldU32("default_sample_duration")
|
|
|
|
|
}
|
|
|
|
|
if defaultSampleSizePresent {
|
2022-01-17 15:32:51 +03:00
|
|
|
|
m.defaultSampleSize = int64(d.FieldU32("default_sample_size"))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
if defaultSampleFlagsPresent {
|
|
|
|
|
d.FieldU32("default_sample_flags")
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// Track Fragment Run
|
|
|
|
|
"trun": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
m := &moof{}
|
|
|
|
|
if ctx.currentTrack != nil && ctx.currentTrack.currentMoof != nil {
|
|
|
|
|
m = ctx.currentTrack.currentMoof
|
|
|
|
|
}
|
|
|
|
|
m.offset = ctx.currentMoofOffset
|
|
|
|
|
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
sampleCompositionTimeOffsetsPresent := false
|
|
|
|
|
sampleFlagsPresent := false
|
|
|
|
|
sampleSizePresent := false
|
|
|
|
|
sampleDurationPresent := false
|
|
|
|
|
firstSampleFlagsPresent := false
|
|
|
|
|
dataOffsetPresent := false
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("flags", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU12("unused0")
|
2021-09-02 19:02:04 +03:00
|
|
|
|
sampleCompositionTimeOffsetsPresent = d.FieldBool("sample_composition_time_offsets_present")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
sampleFlagsPresent = d.FieldBool("sample_flags_present")
|
|
|
|
|
sampleSizePresent = d.FieldBool("sample_size_present")
|
|
|
|
|
sampleDurationPresent = d.FieldBool("sample_duration_present")
|
|
|
|
|
d.FieldU5("unused1")
|
|
|
|
|
firstSampleFlagsPresent = d.FieldBool("first_sample_flags_present")
|
|
|
|
|
d.FieldU1("unused2")
|
|
|
|
|
dataOffsetPresent = d.FieldBool("data_offset_present")
|
|
|
|
|
})
|
|
|
|
|
sampleCount := d.FieldU32("sample_count")
|
|
|
|
|
if dataOffsetPresent {
|
2022-01-17 15:32:51 +03:00
|
|
|
|
m.dataOffset = d.FieldS32("data_offset")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
if firstSampleFlagsPresent {
|
|
|
|
|
d.FieldU32("first_sample_flags")
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-12 11:54:48 +03:00
|
|
|
|
if sampleCount > maxSampleEntryCount {
|
2022-01-15 19:42:50 +03:00
|
|
|
|
d.Errorf("too many sample trun entries %d > %d", sampleCount, maxSampleEntryCount)
|
2021-12-12 11:54:48 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("samples", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < sampleCount; i++ {
|
|
|
|
|
sampleSize := m.defaultSampleSize
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("sample", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
if sampleDurationPresent {
|
|
|
|
|
d.FieldU32("sample_duration")
|
|
|
|
|
}
|
|
|
|
|
if sampleSizePresent {
|
2022-01-17 15:32:51 +03:00
|
|
|
|
sampleSize = int64(d.FieldU32("sample_size"))
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
if sampleFlagsPresent {
|
|
|
|
|
d.FieldU32("sample_flags")
|
|
|
|
|
}
|
|
|
|
|
if sampleCompositionTimeOffsetsPresent {
|
|
|
|
|
d.FieldU32("sample_composition_time_offset")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
m.samplesSizes = append(m.samplesSizes, sampleSize)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"tfdt": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
if version == 1 {
|
|
|
|
|
d.FieldU64("start_time")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU32("start_time")
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"mvex": decodeBoxes,
|
|
|
|
|
"trex": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU32("track_id")
|
|
|
|
|
d.FieldU32("default_sample_description_index")
|
|
|
|
|
d.FieldU32("default_sample_duration")
|
|
|
|
|
d.FieldU32("default_sample_size")
|
|
|
|
|
d.FieldU4("reserved0")
|
|
|
|
|
d.FieldU2("is_leading")
|
|
|
|
|
d.FieldU2("sample_depends_on")
|
|
|
|
|
d.FieldU2("sample_is_depended_on")
|
|
|
|
|
d.FieldU2("sample_has_redundancy")
|
|
|
|
|
d.FieldU3("sample_padding_value")
|
|
|
|
|
d.FieldU1("sample_is_non_sync_sample")
|
|
|
|
|
d.FieldU16("sample_degradation_priority")
|
|
|
|
|
},
|
|
|
|
|
"mfra": decodeBoxes,
|
|
|
|
|
"tfra": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2021-08-12 13:59:15 +03:00
|
|
|
|
d.FieldU32("track_id")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU26("reserved")
|
|
|
|
|
lengthSizeOfTrafNum := d.FieldU2("length_size_of_traf_num")
|
|
|
|
|
sampleLengthSizeOfTrunNum := d.FieldU2("sample_length_size_of_trun_num")
|
|
|
|
|
lengthSizeOfSampleNum := d.FieldU2("length_size_of_sample_num")
|
2021-08-12 13:51:17 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
2021-08-12 13:51:17 +03:00
|
|
|
|
for i := uint64(0); i < entryCount; i++ {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("entry", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
if version == 1 {
|
|
|
|
|
d.FieldU64("time")
|
|
|
|
|
d.FieldU64("moof_offset")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU32("time")
|
|
|
|
|
d.FieldU32("moof_offset")
|
|
|
|
|
}
|
|
|
|
|
d.FieldU("traf_number", int(lengthSizeOfTrafNum+1)*8)
|
|
|
|
|
d.FieldU("trun_number", int(sampleLengthSizeOfTrunNum+1)*8)
|
|
|
|
|
d.FieldU("sample_number", int(lengthSizeOfSampleNum+1)*8)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"mfro": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU32("mfra_size")
|
|
|
|
|
},
|
|
|
|
|
// TODO: item location
|
|
|
|
|
// HEIC image
|
|
|
|
|
"iloc": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
|
|
|
|
|
offsetSize := d.FieldU4("offset_size")
|
|
|
|
|
lengthSize := d.FieldU4("length_size")
|
|
|
|
|
baseOffsetSize := d.FieldU4("base_offset_size")
|
|
|
|
|
var indexSize uint64
|
|
|
|
|
switch version {
|
|
|
|
|
case 1, 2:
|
|
|
|
|
indexSize = d.FieldU4("index_size")
|
|
|
|
|
default:
|
|
|
|
|
d.FieldU4("reserved")
|
|
|
|
|
}
|
|
|
|
|
var itemCount uint64
|
|
|
|
|
if version < 2 {
|
|
|
|
|
itemCount = d.FieldU16("item_count")
|
|
|
|
|
} else {
|
|
|
|
|
itemCount = d.FieldU32("item_count")
|
|
|
|
|
}
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("items", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < itemCount; i++ {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("item", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
switch version {
|
|
|
|
|
case 0, 1:
|
|
|
|
|
d.FieldU16("id")
|
|
|
|
|
case 2:
|
|
|
|
|
d.FieldU32("id")
|
|
|
|
|
}
|
|
|
|
|
switch version {
|
|
|
|
|
case 1, 2:
|
|
|
|
|
d.FieldU12("reserved")
|
|
|
|
|
d.FieldU4("construction_method")
|
|
|
|
|
}
|
|
|
|
|
d.FieldU16("data_reference_index")
|
|
|
|
|
d.FieldU("base_offset", int(baseOffsetSize)*8)
|
|
|
|
|
extentCount := d.FieldU16("extent_count")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("extends", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < extentCount; i++ {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("extent", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
if (version == 1 || version == 2) && indexSize > 0 {
|
|
|
|
|
d.FieldU("index", int(offsetSize)*8)
|
|
|
|
|
}
|
|
|
|
|
d.FieldU("offset", int(offsetSize)*8)
|
|
|
|
|
d.FieldU("length", int(lengthSize)*8)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"infe": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU16("id")
|
|
|
|
|
d.FieldU16("protection_index")
|
2021-11-17 18:13:10 +03:00
|
|
|
|
d.FieldUTF8Null("item_name")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
// TODO: really optional? seems so
|
|
|
|
|
if d.NotEnd() {
|
2021-11-17 18:13:10 +03:00
|
|
|
|
d.FieldUTF8Null("content_type")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
if d.NotEnd() {
|
2021-11-17 18:13:10 +03:00
|
|
|
|
d.FieldUTF8Null("content_encoding")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"iinf": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
_ = d.FieldU16("entry_count")
|
|
|
|
|
decodeBoxes(ctx, d)
|
|
|
|
|
},
|
|
|
|
|
"iprp": decodeBoxes,
|
|
|
|
|
"ipco": decodeBoxes,
|
|
|
|
|
"ID32": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU1("pad")
|
2021-09-14 18:01:25 +03:00
|
|
|
|
// ISO-639-2/T as 3*5 bit integers - 0x60
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStrFn("language", func(d *decode.D) string {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
s := ""
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
s += fmt.Sprintf("%c", int(d.U5())+0x60)
|
|
|
|
|
}
|
2021-11-05 17:04:26 +03:00
|
|
|
|
return s
|
2020-06-08 03:29:51 +03:00
|
|
|
|
})
|
2021-09-16 16:29:11 +03:00
|
|
|
|
d.FieldFormat("data", id3v2Format, nil)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"mehd": func(_ *decodeContext, d *decode.D) {
|
2021-09-03 16:48:03 +03:00
|
|
|
|
d.FieldU8("version")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
flags := d.FieldU24("flags")
|
|
|
|
|
if flags&0b1 != 0 {
|
|
|
|
|
d.FieldU64("fragment_duration")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU32("fragment_duration")
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"pssh": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
var (
|
|
|
|
|
systemIDCommon = [16]byte{0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b}
|
|
|
|
|
systemIDWidevine = [16]byte{0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed}
|
|
|
|
|
systemIDPlayReady = [16]byte{0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95}
|
|
|
|
|
)
|
2021-12-02 00:48:25 +03:00
|
|
|
|
systemIDNames := scalar.BytesToScalar{
|
2022-04-05 14:57:55 +03:00
|
|
|
|
{Bytes: systemIDCommon[:], Scalar: scalar.S{Sym: "common"}},
|
|
|
|
|
{Bytes: systemIDWidevine[:], Scalar: scalar.S{Sym: "widevine"}},
|
|
|
|
|
{Bytes: systemIDPlayReady[:], Scalar: scalar.S{Sym: "playReady"}},
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
2022-01-24 23:21:48 +03:00
|
|
|
|
systemIDBR := d.FieldRawLen("system_id", 6*8, systemIDNames)
|
2021-11-05 17:04:26 +03:00
|
|
|
|
// TODO: make nicer
|
2022-01-24 23:21:48 +03:00
|
|
|
|
systemID := d.MustReadAllBits(systemIDBR)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
switch version {
|
|
|
|
|
case 0:
|
|
|
|
|
case 1:
|
|
|
|
|
kidCount := d.FieldU32("kid_count")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("kids", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < kidCount; i++ {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("kid", 16*8)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
dataLen := d.FieldU32("data_size")
|
|
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
case bytes.Equal(systemID, systemIDWidevine[:]):
|
2021-09-16 16:29:11 +03:00
|
|
|
|
d.FieldFormatLen("data", int64(dataLen)*8, protoBufWidevineFormat, nil)
|
2021-09-02 14:08:26 +03:00
|
|
|
|
case bytes.Equal(systemID, systemIDPlayReady[:]):
|
2021-09-16 16:29:11 +03:00
|
|
|
|
d.FieldFormatLen("data", int64(dataLen)*8, psshPlayreadyFormat, nil)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
case systemID == nil:
|
|
|
|
|
fallthrough
|
|
|
|
|
default:
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", int64(dataLen)*8)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"sinf": decodeBoxes,
|
2021-09-02 19:02:04 +03:00
|
|
|
|
"frma": func(ctx *decodeContext, d *decode.D) {
|
|
|
|
|
format := d.FieldUTF8("format", 4)
|
|
|
|
|
|
|
|
|
|
// set to original data format
|
|
|
|
|
// TODO: how to handle multiple descriptors? track current?
|
|
|
|
|
if ctx.currentTrack != nil && len(ctx.currentTrack.sampleDescriptions) > 0 {
|
|
|
|
|
ctx.currentTrack.sampleDescriptions[0].originalFormat = format
|
|
|
|
|
}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"schm": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldUTF8("encryption_type", 4)
|
|
|
|
|
d.FieldU16("encryption_version")
|
|
|
|
|
if d.BitsLeft() > 0 {
|
|
|
|
|
d.FieldUTF8("uri", int(d.BitsLeft())/8)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"schi": decodeBoxes,
|
|
|
|
|
"btrt": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU32("decoding_buffer_size")
|
|
|
|
|
d.FieldU32("max_bitrate")
|
|
|
|
|
d.FieldU32("avg_bitrate")
|
|
|
|
|
},
|
|
|
|
|
"pasp": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU32("h_spacing")
|
|
|
|
|
d.FieldU32("v_spacing")
|
|
|
|
|
},
|
|
|
|
|
"uuid": func(_ *decodeContext, d *decode.D) {
|
2021-12-02 00:48:25 +03:00
|
|
|
|
d.FieldRawLen("uuid", 16*8, scalar.RawUUID, uuidNames)
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"keys": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < entryCount; i++ {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("entry", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
keySize := d.FieldU32("key_size")
|
|
|
|
|
d.FieldUTF8("key_namespace", 4)
|
|
|
|
|
d.FieldUTF8("key_name", int(keySize)-8)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"wave": decodeBoxes,
|
|
|
|
|
"saiz": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
flags := d.FieldU24("flags")
|
|
|
|
|
if flags&0b1 != 0 {
|
|
|
|
|
d.FieldU32("aux_info_type")
|
|
|
|
|
d.FieldU32("aux_info_type_parameter")
|
|
|
|
|
}
|
|
|
|
|
defaultSampleInfoSize := d.FieldU8("default_sample_info_size")
|
|
|
|
|
sampleCount := d.FieldU32("sample_count")
|
|
|
|
|
if defaultSampleInfoSize == 0 {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("sample_size_info_table", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < sampleCount; i++ {
|
|
|
|
|
d.FieldU8("sample_size")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"sgpd": func(_ *decodeContext, d *decode.D) {
|
2021-09-03 16:48:03 +03:00
|
|
|
|
version := d.FieldU8("version")
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU32("grouping_type")
|
2021-09-03 16:48:03 +03:00
|
|
|
|
var defaultLength uint64
|
|
|
|
|
if version == 1 {
|
|
|
|
|
defaultLength = d.FieldU32("default_length")
|
|
|
|
|
}
|
|
|
|
|
if version >= 2 {
|
|
|
|
|
d.FieldU32("default_sample_description_index")
|
|
|
|
|
}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2022-01-23 16:11:55 +03:00
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < entryCount; i++ {
|
2021-09-03 16:48:03 +03:00
|
|
|
|
entryLen := defaultLength
|
|
|
|
|
if version == 1 {
|
|
|
|
|
if defaultLength == 0 {
|
2022-01-23 16:11:55 +03:00
|
|
|
|
entryLen = d.FieldU32("description_length")
|
|
|
|
|
} else if entryLen == 0 {
|
|
|
|
|
d.Fatalf("sgpd groups entry len <= 0 version 1")
|
2021-09-03 16:48:03 +03:00
|
|
|
|
}
|
2022-01-23 16:11:55 +03:00
|
|
|
|
} else if entryLen == 0 {
|
|
|
|
|
d.Fatalf("sgpd groups entry len <= 0")
|
2021-09-03 16:48:03 +03:00
|
|
|
|
}
|
2022-01-23 16:11:55 +03:00
|
|
|
|
d.FieldRawLen("data", int64(entryLen)*8)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"sbgp": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
|
|
|
|
|
d.FieldU32("grouping_type")
|
|
|
|
|
if version == 1 {
|
|
|
|
|
d.FieldU32("grouping_type_parameter")
|
|
|
|
|
}
|
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < entryCount; i++ {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldStruct("entry", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
d.FieldU32("sample_count")
|
|
|
|
|
d.FieldU32("group_description_index")
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"saio": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
flags := d.FieldU24("flags")
|
|
|
|
|
|
|
|
|
|
if flags&0b1 != 0 {
|
|
|
|
|
d.FieldU32("aux_info_type")
|
|
|
|
|
d.FieldU32("aux_info_type_parameter")
|
|
|
|
|
}
|
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
for i := uint64(0); i < entryCount; i++ {
|
|
|
|
|
if version == 0 {
|
|
|
|
|
d.FieldU32("offset")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU64("offset")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"senc": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
|
|
|
|
|
d.FieldU32("sample_count")
|
2021-09-14 18:01:25 +03:00
|
|
|
|
// TODO need iv size here
|
2020-06-08 03:29:51 +03:00
|
|
|
|
},
|
|
|
|
|
"tenc": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
|
|
|
|
|
d.FieldU8("reserved0")
|
|
|
|
|
switch version {
|
|
|
|
|
case 0:
|
|
|
|
|
d.FieldU8("reserved1")
|
|
|
|
|
default:
|
|
|
|
|
d.FieldU4("default_crypto_bytes")
|
|
|
|
|
d.FieldU4("default_skip_bytes")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defaultIsEncrypted := d.FieldU8("default_is_encrypted")
|
|
|
|
|
defaultIVSize := d.FieldU8("default_iv_size")
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("default_kid", 8*16)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
|
|
|
|
|
if defaultIsEncrypted != 0 && defaultIVSize == 0 {
|
|
|
|
|
d.FieldU8("default_constant_iv_size")
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"covr": decodeBoxes,
|
|
|
|
|
"dec3": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU13("data_rate")
|
|
|
|
|
d.FieldU3("num_ind_sub")
|
|
|
|
|
d.FieldU2("fscod")
|
|
|
|
|
d.FieldU5("bsid")
|
|
|
|
|
d.FieldU5("bsmod")
|
|
|
|
|
d.FieldU3("acmod")
|
|
|
|
|
d.FieldU1("lfeon")
|
|
|
|
|
d.FieldU3("reserved0")
|
|
|
|
|
numDepSub := d.FieldU4("num_dep_sub")
|
|
|
|
|
if numDepSub > 0 {
|
|
|
|
|
d.FieldU9("chan_loc")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU1("reserved1")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if d.BitsLeft() >= 16 {
|
|
|
|
|
d.FieldU7("reserved2")
|
|
|
|
|
ec3JocFlag := d.FieldBool("ec3_job_flag")
|
|
|
|
|
if ec3JocFlag {
|
|
|
|
|
d.FieldU1("ec3_job_complexity")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"dac4": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU3("ac4_dsi_version")
|
|
|
|
|
bitstreamVersion := d.FieldU7("bitstream_version")
|
|
|
|
|
d.FieldU1("fs_index")
|
|
|
|
|
d.FieldU4("frame_rate_index")
|
|
|
|
|
d.FieldU9("n_presentation")
|
|
|
|
|
|
|
|
|
|
if bitstreamVersion > 1 {
|
|
|
|
|
hasProgramID := d.FieldBool("has_program_id")
|
|
|
|
|
if hasProgramID {
|
|
|
|
|
d.FieldU16("short_program_id")
|
2021-09-27 12:01:14 +03:00
|
|
|
|
hasUUID := d.FieldBool("has_uuid")
|
|
|
|
|
if hasUUID {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("uuid", 16*8)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if ac4DsiVersion == 1 {
|
|
|
|
|
// d.FieldU2("bit_rate_mode")
|
|
|
|
|
// d.FieldU32("bit_rate")
|
|
|
|
|
// d.FieldU32("bit_rate_precision")
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// if ac4DsiVersion == 1 {
|
|
|
|
|
|
2021-11-05 17:04:26 +03:00
|
|
|
|
// d.FieldArray("presentations", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
// for i := uint64(0); i < nPresentation; i++ {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
// d.FieldStruct("presentation", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
|
// d.FieldU8("presentation_version")
|
|
|
|
|
// presBytes := d.FieldUFn("pres_bytes", func() (uint64, decode.DisplayFormat, string) {
|
|
|
|
|
// n := d.U8()
|
|
|
|
|
// if n == 0x0ff {
|
|
|
|
|
// n += d.U16()
|
|
|
|
|
// }
|
|
|
|
|
// return n, decode.NumberDecimal, ""
|
|
|
|
|
// })
|
2021-11-05 17:04:26 +03:00
|
|
|
|
// d.FieldRawLen("data", int64(presBytes)*8)
|
2020-06-08 03:29:51 +03:00
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
if d.BitsLeft() > 0 {
|
2021-11-05 17:04:26 +03:00
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|
|
|
|
|
},
|
2021-11-19 17:24:53 +03:00
|
|
|
|
"tapt": decodeBoxes,
|
|
|
|
|
"clef": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldFP32("width")
|
|
|
|
|
d.FieldFP32("height")
|
|
|
|
|
},
|
|
|
|
|
"prof": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldFP32("width")
|
|
|
|
|
d.FieldFP32("height")
|
|
|
|
|
},
|
|
|
|
|
"enof": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldFP32("width")
|
|
|
|
|
d.FieldFP32("height")
|
|
|
|
|
},
|
|
|
|
|
"clap": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU32("aperture_width_n")
|
|
|
|
|
d.FieldU32("aperture_width_d")
|
|
|
|
|
d.FieldU32("aperture_height_n")
|
|
|
|
|
d.FieldU32("aperture_height_d")
|
|
|
|
|
d.FieldU32("horiz_off_n")
|
|
|
|
|
d.FieldU32("horiz_off_d")
|
|
|
|
|
d.FieldU32("vert_off_n")
|
|
|
|
|
d.FieldU32("vert_off_d")
|
|
|
|
|
},
|
2021-11-22 15:26:32 +03:00
|
|
|
|
"smhd": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldFP16("balance")
|
|
|
|
|
d.FieldU16("reserved")
|
|
|
|
|
},
|
2022-03-01 13:43:50 +03:00
|
|
|
|
"colr": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
parameterType := d.FieldUTF8("parameter_type", 4)
|
|
|
|
|
|
|
|
|
|
switch parameterType {
|
|
|
|
|
case "nclx", "nclc":
|
|
|
|
|
d.FieldU16("primaries_index", format.ISO_23091_2_ColourPrimariesMap)
|
|
|
|
|
d.FieldU16("transfer_function_index", format.ISO_23091_2_TransferCharacteristicMap)
|
|
|
|
|
d.FieldU16("matrix_index", format.ISO_23091_2_MatrixCoefficients)
|
|
|
|
|
switch parameterType {
|
|
|
|
|
case "nclx":
|
|
|
|
|
d.FieldU8("color_range")
|
|
|
|
|
}
|
|
|
|
|
case "prof":
|
|
|
|
|
d.FieldFormat("profile", iccProfileFormat, nil)
|
|
|
|
|
default:
|
|
|
|
|
d.FieldRawLen("data", d.BitsLeft())
|
|
|
|
|
}
|
2022-03-01 13:58:14 +03:00
|
|
|
|
},
|
2022-03-23 15:30:28 +03:00
|
|
|
|
"ispe": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
d.FieldU32("image_width")
|
|
|
|
|
d.FieldU32("image_height")
|
|
|
|
|
},
|
|
|
|
|
"ipma": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
flags := d.FieldU24("flags")
|
|
|
|
|
entryCount := d.FieldU32("entry_count")
|
|
|
|
|
d.FieldArray("entries", func(d *decode.D) {
|
|
|
|
|
for i := uint64(0); i < entryCount; i++ {
|
|
|
|
|
d.FieldStruct("entry", func(d *decode.D) {
|
|
|
|
|
if version < 1 {
|
|
|
|
|
d.FieldU16("item_id")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU32("item_id")
|
|
|
|
|
}
|
|
|
|
|
associationCount := d.FieldU8("association_count")
|
|
|
|
|
d.FieldArray("associations", func(d *decode.D) {
|
|
|
|
|
for j := uint64(0); j < associationCount; j++ {
|
|
|
|
|
d.FieldStruct("association", func(d *decode.D) {
|
|
|
|
|
d.FieldBool("essential")
|
|
|
|
|
if flags&0b1 != 0 {
|
|
|
|
|
d.FieldU15("property_index")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU7("item_id")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
"pitm": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
version := d.FieldU8("version")
|
|
|
|
|
d.FieldU24("flags")
|
|
|
|
|
if version < 1 {
|
|
|
|
|
d.FieldU16("item_id")
|
|
|
|
|
} else {
|
|
|
|
|
d.FieldU32("item_id")
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"iref": func(ctx *decodeContext, d *decode.D) {
|
2022-04-07 19:25:23 +03:00
|
|
|
|
version := d.FieldU8("version")
|
2022-03-23 15:30:28 +03:00
|
|
|
|
d.FieldU24("flags")
|
2022-04-07 19:25:23 +03:00
|
|
|
|
decodeBoxesWithParentData(ctx, d, irefBox{version: int(version)})
|
|
|
|
|
},
|
|
|
|
|
"dimg": irefEntryDecode,
|
|
|
|
|
"thmb": irefEntryDecode,
|
|
|
|
|
"cdsc": irefEntryDecode,
|
|
|
|
|
"irot": func(_ *decodeContext, d *decode.D) {
|
|
|
|
|
d.FieldU8("rotation", scalar.UToSymU{
|
|
|
|
|
0: 0,
|
|
|
|
|
1: 90,
|
|
|
|
|
2: 180,
|
|
|
|
|
3: 270,
|
|
|
|
|
})
|
2022-03-23 15:30:28 +03:00
|
|
|
|
},
|
2022-03-01 13:58:14 +03:00
|
|
|
|
}
|
2020-06-08 03:29:51 +03:00
|
|
|
|
}
|