mirror of
https://github.com/wader/fq.git
synced 2024-11-23 09:56:07 +03:00
Merge pull request #723 from wader/mp4-brand-qt-symbolic
mp4: Nicer major brand and handle some qt brand short strings better
This commit is contained in:
commit
9f38734cef
@ -304,6 +304,10 @@ func decodeBoxesWithParentData(ctx *decodeContext, d *decode.D, parentData any,
|
||||
}
|
||||
}
|
||||
|
||||
type rootBox struct {
|
||||
ftypMajorBrand string
|
||||
}
|
||||
|
||||
type irefBox struct {
|
||||
version int
|
||||
}
|
||||
@ -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,
|
||||
@ -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")
|
||||
@ -510,6 +518,8 @@ 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)
|
||||
@ -517,8 +527,14 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
|
||||
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
|
||||
|
@ -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
|
||||
@ -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)
|
||||
|
6
format/mp4/testdata/in24.fqtest
vendored
6
format/mp4/testdata/in24.fqtest
vendored
@ -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)
|
||||
@ -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 |
|
||||
@ -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)
|
||||
|
6
format/mp4/testdata/lpcm.fqtest
vendored
6
format/mp4/testdata/lpcm.fqtest
vendored
@ -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)
|
||||
@ -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)
|
||||
@ -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)
|
||||
|
6
format/prores/testdata/prores_frame.fqtest
vendored
6
format/prores/testdata/prores_frame.fqtest
vendored
@ -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)
|
||||
@ -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 |
|
||||
@ -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)
|
||||
|
@ -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()})
|
||||
}
|
||||
@ -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 {
|
||||
@ -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()})
|
||||
}
|
||||
@ -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 {
|
||||
|
@ -146,16 +146,13 @@ 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 {
|
||||
@ -163,26 +160,24 @@ func (d *D) tryTextLenPrefixed(lenBits int, fixedBytes int, e encoding.Encoding)
|
||||
}
|
||||
|
||||
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) {
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user