Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions format/mp4/boxes.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ func decodeBoxesWithParentData(ctx *decodeContext, d *decode.D, parentData any,
}
}

type rootBox struct {
ftypMajorBrand string
}

type irefBox struct {
version int
}
Expand Down Expand Up @@ -358,11 +362,15 @@ func decodeBoxIrefEntry(ctx *decodeContext, d *decode.D) {
})
}

func decodeBoxFtyp(d *decode.D) {
brand := d.FieldUTF8("major_brand", 4)
func decodeBoxFtyp(ctx *decodeContext, d *decode.D) {
root := ctx.rootBox()

brand := d.FieldUTF8("major_brand", 4, scalar.ActualTrimSpace)
root.ftypMajorBrand = brand

d.FieldU32("minor_version", scalar.UintFn(func(s scalar.Uint) (scalar.Uint, error) {
switch brand {
case "qt ":
case "qt":
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html#//apple_ref/doc/uid/TP40000939-CH203-BBCGDDDF
// "For QuickTime movie files, this takes the form of four binary-coded decimal values, indicating the century,
// year, and month of the QuickTime File Format Specification, followed by a binary coded decimal zero. For example,
Expand All @@ -382,9 +390,9 @@ func decodeBoxFtyp(d *decode.D) {
func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
switch typ {
case "ftyp":
decodeBoxFtyp(d)
decodeBoxFtyp(ctx, d)
case "styp":
decodeBoxFtyp(d)
decodeBoxFtyp(ctx, d)
case "mvhd":
version := d.FieldU8("version")
d.FieldU24("flags")
Expand Down Expand Up @@ -510,15 +518,23 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
d.FieldU16("value")
})
case "hdlr":
majorBrand := ctx.rootBox().ftypMajorBrand

d.FieldU8("version")
d.FieldU24("flags")
d.FieldUTF8NullFixedLen("component_type", 4)
subType := d.FieldUTF8("component_subtype", 4, subTypeNames, scalar.ActualTrimSpace)
d.FieldUTF8NullFixedLen("component_manufacturer", 4)
d.FieldU32("component_flags")
d.FieldU32("component_flags_mask")
// TODO: sometimes has a length prefix byte, how to know?
d.FieldUTF8NullFixedLen("component_name", int(d.BitsLeft()/8))

switch majorBrand {
case "qt":
// qt brand seems to use length prefixed strings
d.FieldUTF8ShortStringFixedLen("component_name", int(d.BitsLeft()/8))
default:
d.FieldUTF8NullFixedLen("component_name", int(d.BitsLeft()/8))
}

if t := ctx.currentTrack(); t != nil {
t.seenHdlr = true
Expand Down
7 changes: 7 additions & 0 deletions format/mp4/mp4.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ func (ctx *decodeContext) findParent(typ string) any {
return nil
}

func (ctx *decodeContext) rootBox() *rootBox {
t, _ := ctx.findParent("").(*rootBox)
return t
}

func (ctx *decodeContext) currentTrakBox() *trakBox {
t, _ := ctx.findParent("trak").(*trakBox)
return t
Expand Down Expand Up @@ -448,6 +453,8 @@ func mp4Decode(d *decode.D) any {

d.SeekRel(-8 * 8)

ctx.path = []pathEntry{{typ: "", data: &rootBox{}}}

decodeBoxes(ctx, d)
if len(ctx.tracks) > 0 {
mp4Tracks(d, ctx)
Expand Down
6 changes: 3 additions & 3 deletions format/mp4/testdata/in24.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ $ fq dv in24.mp4
| | | [0]{}: box 0x0-0x13.7 (20)
0x000|00 00 00 14 |.... | size: 20 0x0-0x3.7 (4)
0x000| 66 74 79 70 | ftyp | type: "ftyp" (File type and compatibility) 0x4-0x7.7 (4)
0x000| 71 74 20 20 | qt | major_brand: "qt " 0x8-0xb.7 (4)
0x000| 71 74 20 20 | qt | major_brand: "qt" 0x8-0xb.7 (4)
0x000| 00 00 02 00| ....| minor_version: 512 (0000.02) 0xc-0xf.7 (4)
| | | brands[0:1]: 0x10-0x13.7 (4)
0x010|71 74 20 20 |qt | [0]: "qt" brand 0x10-0x13.7 (4)
Expand Down Expand Up @@ -128,7 +128,7 @@ $ fq dv in24.mp4
0x260| 00 00 00 00 | .... | component_manufacturer: "" 0x264-0x267.7 (4)
0x260| 00 00 00 00 | .... | component_flags: 0 0x268-0x26b.7 (4)
0x260| 00 00 00 00| ....| component_flags_mask: 0 0x26c-0x26f.7 (4)
0x270|0c 53 6f 75 6e 64 48 61 6e 64 6c 65 72 |.SoundHandler | component_name: "\fSoundHandler" 0x270-0x27c.7 (13)
0x270|0c 53 6f 75 6e 64 48 61 6e 64 6c 65 72 |.SoundHandler | component_name: "SoundHandler" 0x270-0x27c.7 (13)
| | | [2]{}: box 0x27d-0x3ca.7 (334)
0x270| 00 00 01| ...| size: 334 0x27d-0x280.7 (4)
0x280|4e |N |
Expand All @@ -154,7 +154,7 @@ $ fq dv in24.mp4
0x2a0| 00 00 00| ...| component_flags: 0 0x2ad-0x2b0.7 (4)
0x2b0|00 |. |
0x2b0| 00 00 00 00 | .... | component_flags_mask: 0 0x2b1-0x2b4.7 (4)
0x2b0| 0b 44 61 74 61 48 61 6e 64 6c 65| .DataHandle| component_name: "\vDataHandler" 0x2b5-0x2c0.7 (12)
0x2b0| 0b 44 61 74 61 48 61 6e 64 6c 65| .DataHandle| component_name: "DataHandler" 0x2b5-0x2c0.7 (12)
0x2c0|72 |r |
| | | [2]{}: box 0x2c1-0x2e4.7 (36)
0x2c0| 00 00 00 24 | ...$ | size: 36 0x2c1-0x2c4.7 (4)
Expand Down
6 changes: 3 additions & 3 deletions format/mp4/testdata/lpcm.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ $ fq dv lpcm.mp4
| | | [0]{}: box 0x0-0x13.7 (20)
0x000|00 00 00 14 |.... | size: 20 0x0-0x3.7 (4)
0x000| 66 74 79 70 | ftyp | type: "ftyp" (File type and compatibility) 0x4-0x7.7 (4)
0x000| 71 74 20 20 | qt | major_brand: "qt " 0x8-0xb.7 (4)
0x000| 71 74 20 20 | qt | major_brand: "qt" 0x8-0xb.7 (4)
0x000| 00 00 02 00| ....| minor_version: 512 (0000.02) 0xc-0xf.7 (4)
| | | brands[0:1]: 0x10-0x13.7 (4)
0x010|71 74 20 20 |qt | [0]: "qt" brand 0x10-0x13.7 (4)
Expand Down Expand Up @@ -129,7 +129,7 @@ $ fq dv lpcm.mp4
0x390| 00 00 00 00| ....| component_manufacturer: "" 0x39c-0x39f.7 (4)
0x3a0|00 00 00 00 |.... | component_flags: 0 0x3a0-0x3a3.7 (4)
0x3a0| 00 00 00 00 | .... | component_flags_mask: 0 0x3a4-0x3a7.7 (4)
0x3a0| 0c 53 6f 75 6e 64 48 61| .SoundHa| component_name: "\fSoundHandler" 0x3a8-0x3b4.7 (13)
0x3a0| 0c 53 6f 75 6e 64 48 61| .SoundHa| component_name: "SoundHandler" 0x3a8-0x3b4.7 (13)
0x3b0|6e 64 6c 65 72 |ndler |
| | | [2]{}: box 0x3b5-0x4f0.7 (316)
0x3b0| 00 00 01 3c | ...< | size: 316 0x3b5-0x3b8.7 (4)
Expand All @@ -155,7 +155,7 @@ $ fq dv lpcm.mp4
0x3e0| 00 00 00 00 | .... | component_manufacturer: "" 0x3e1-0x3e4.7 (4)
0x3e0| 00 00 00 00 | .... | component_flags: 0 0x3e5-0x3e8.7 (4)
0x3e0| 00 00 00 00 | .... | component_flags_mask: 0 0x3e9-0x3ec.7 (4)
0x3e0| 0b 44 61| .Da| component_name: "\vDataHandler" 0x3ed-0x3f8.7 (12)
0x3e0| 0b 44 61| .Da| component_name: "DataHandler" 0x3ed-0x3f8.7 (12)
0x3f0|74 61 48 61 6e 64 6c 65 72 |taHandler |
| | | [2]{}: box 0x3f9-0x41c.7 (36)
0x3f0| 00 00 00 24 | ...$ | size: 36 0x3f9-0x3fc.7 (4)
Expand Down
6 changes: 3 additions & 3 deletions format/prores/testdata/prores_frame.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ $ fq -d mp4 dv prores_frame.mov
| | | [0]{}: box 0x0-0x13.7 (20)
0x0000|00 00 00 14 |.... | size: 20 0x0-0x3.7 (4)
0x0000| 66 74 79 70 | ftyp | type: "ftyp" (File type and compatibility) 0x4-0x7.7 (4)
0x0000| 71 74 20 20 | qt | major_brand: "qt " 0x8-0xb.7 (4)
0x0000| 71 74 20 20 | qt | major_brand: "qt" 0x8-0xb.7 (4)
0x0000| 00 00 02 00| ....| minor_version: 512 (0000.02) 0xc-0xf.7 (4)
| | | brands[0:1]: 0x10-0x13.7 (4)
0x0010|71 74 20 20 |qt | [0]: "qt" brand 0x10-0x13.7 (4)
Expand Down Expand Up @@ -149,7 +149,7 @@ $ fq -d mp4 dv prores_frame.mov
0x6d70| 00 00 00 00 | .... | component_flags: 0 0x6d79-0x6d7c.7 (4)
0x6d70| 00 00 00| ...| component_flags_mask: 0 0x6d7d-0x6d80.7 (4)
0x6d80|00 |. |
0x6d80| 0c 56 69 64 65 6f 48 61 6e 64 6c 65 72 | .VideoHandler | component_name: "\fVideoHandler" 0x6d81-0x6d8d.7 (13)
0x6d80| 0c 56 69 64 65 6f 48 61 6e 64 6c 65 72 | .VideoHandler | component_name: "VideoHandler" 0x6d81-0x6d8d.7 (13)
| | | [2]{}: box 0x6d8e-0x6edd.7 (336)
0x6d80| 00 00| ..| size: 336 0x6d8e-0x6d91.7 (4)
0x6d90|01 50 |.P |
Expand Down Expand Up @@ -178,7 +178,7 @@ $ fq -d mp4 dv prores_frame.mov
0x6dc0|00 00 |.. |
0x6dc0| 00 00 00 00 | .... | component_flags: 0 0x6dc2-0x6dc5.7 (4)
0x6dc0| 00 00 00 00 | .... | component_flags_mask: 0 0x6dc6-0x6dc9.7 (4)
0x6dc0| 0b 44 61 74 61 48| .DataH| component_name: "\vDataHandler" 0x6dca-0x6dd5.7 (12)
0x6dc0| 0b 44 61 74 61 48| .DataH| component_name: "DataHandler" 0x6dca-0x6dd5.7 (12)
0x6dd0|61 6e 64 6c 65 72 |andler |
| | | [2]{}: box 0x6dd6-0x6df9.7 (36)
0x6dd0| 00 00 00 24 | ...$ | size: 36 0x6dd6-0x6dd9.7 (4)
Expand Down
12 changes: 6 additions & 6 deletions pkg/decode/decode_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -19509,11 +19509,11 @@ func (d *D) FieldUTF16BE(name string, nBytes int, sms ...scalar.StrMapper) strin
// Reader UTF8ShortString

// TryUTF8ShortString tries to read one byte length fixed UTF8 string
func (d *D) TryUTF8ShortString() (string, error) { return d.tryTextLenPrefixed(8, -1, UTF8BOM) }
func (d *D) TryUTF8ShortString() (string, error) { return d.tryTextLenPrefixed(1, -1, UTF8BOM) }

// UTF8ShortString reads one byte length fixed UTF8 string
func (d *D) UTF8ShortString() string {
v, err := d.tryTextLenPrefixed(8, -1, UTF8BOM)
v, err := d.tryTextLenPrefixed(1, -1, UTF8BOM)
if err != nil {
panic(IOError{Err: err, Op: "UTF8ShortString", Pos: d.Pos()})
}
Expand All @@ -19523,7 +19523,7 @@ func (d *D) UTF8ShortString() string {
// TryFieldScalarUTF8ShortString tries to add a field and read one byte length fixed UTF8 string
func (d *D) TryFieldScalarUTF8ShortString(name string, sms ...scalar.StrMapper) (*scalar.Str, error) {
s, err := d.TryFieldScalarStrFn(name, func(d *D) (scalar.Str, error) {
v, err := d.tryTextLenPrefixed(8, -1, UTF8BOM)
v, err := d.tryTextLenPrefixed(1, -1, UTF8BOM)
return scalar.Str{Actual: v}, err
}, sms...)
if err != nil {
Expand Down Expand Up @@ -19556,12 +19556,12 @@ func (d *D) FieldUTF8ShortString(name string, sms ...scalar.StrMapper) string {

// TryUTF8ShortStringFixedLen tries to read fixedBytes bytes long one byte length prefixed UTF8 string
func (d *D) TryUTF8ShortStringFixedLen(fixedBytes int) (string, error) {
return d.tryTextLenPrefixed(8, fixedBytes, UTF8BOM)
return d.tryTextLenPrefixed(1, fixedBytes, UTF8BOM)
}

// UTF8ShortStringFixedLen reads fixedBytes bytes long one byte length prefixed UTF8 string
func (d *D) UTF8ShortStringFixedLen(fixedBytes int) string {
v, err := d.tryTextLenPrefixed(8, fixedBytes, UTF8BOM)
v, err := d.tryTextLenPrefixed(1, fixedBytes, UTF8BOM)
if err != nil {
panic(IOError{Err: err, Op: "UTF8ShortStringFixedLen", Pos: d.Pos()})
}
Expand All @@ -19571,7 +19571,7 @@ func (d *D) UTF8ShortStringFixedLen(fixedBytes int) string {
// TryFieldScalarUTF8ShortStringFixedLen tries to add a field and read fixedBytes bytes long one byte length prefixed UTF8 string
func (d *D) TryFieldScalarUTF8ShortStringFixedLen(name string, fixedBytes int, sms ...scalar.StrMapper) (*scalar.Str, error) {
s, err := d.TryFieldScalarStrFn(name, func(d *D) (scalar.Str, error) {
v, err := d.tryTextLenPrefixed(8, fixedBytes, UTF8BOM)
v, err := d.tryTextLenPrefixed(1, fixedBytes, UTF8BOM)
return scalar.Str{Actual: v}, err
}, sms...)
if err != nil {
Expand Down
25 changes: 10 additions & 15 deletions pkg/decode/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,43 +146,38 @@ func (d *D) tryText(nBytes int, e encoding.Encoding) (string, error) {
}

// read length prefixed text (ex pascal short string)
// lBits length prefix
// lenBytes length prefix
// fixedBytes if != -1 read nBytes but trim to length
//
//nolint:unparam
func (d *D) tryTextLenPrefixed(lenBits int, fixedBytes int, e encoding.Encoding) (string, error) {
if lenBits < 0 {
return "", fmt.Errorf("tryTextLenPrefixed lenBits must be >= 0 (%d)", lenBits)
}
if fixedBytes < 0 {
return "", fmt.Errorf("tryTextLenPrefixed fixedBytes must be >= 0 (%d)", fixedBytes)
func (d *D) tryTextLenPrefixed(prefixLenBytes int, fixedBytes int, e encoding.Encoding) (string, error) {
if prefixLenBytes < 0 {
return "", fmt.Errorf("tryTextLenPrefixed lenBytes must be >= 0 (%d)", prefixLenBytes)
}
bytesLeft := d.BitsLeft() / 8
if int64(fixedBytes) > bytesLeft {
return "", fmt.Errorf("tryTextLenPrefixed fixedBytes %d outside, %d bytes left", fixedBytes, bytesLeft)
}

p := d.Pos()
l, err := d.TryUintBits(lenBits)
lenBytes, err := d.TryUintBits(prefixLenBytes * 8)
if err != nil {
return "", err
}

n := int(l)
readBytes := int(lenBytes)
if fixedBytes != -1 {
n = fixedBytes - 1
// TODO: error?
if l > uint64(n) {
l = uint64(n)
}
readBytes = fixedBytes - prefixLenBytes
lenBytes = mathex.Min(lenBytes, uint64(readBytes))
}

bs, err := d.TryBytesLen(n)
bs, err := d.TryBytesLen(readBytes)
if err != nil {
d.SeekAbs(p)
return "", err
}
return e.NewDecoder().String(string(bs[0:l]))
return e.NewDecoder().String(string(bs[0:lenBytes]))
}

func (d *D) tryTextNull(charBytes int, e encoding.Encoding) (string, error) {
Expand Down
Loading