Skip to content

Commit 7757134

Browse files
committed
mp4: Decode pcmC and chnl box
1 parent 44fa1c3 commit 7757134

File tree

6 files changed

+137
-22
lines changed

6 files changed

+137
-22
lines changed

format/mp4/boxes.go

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -591,13 +591,14 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
591591
size := d.FieldU32("size")
592592
dataFormat := d.FieldUTF8("type", 4, dataFormatNames, scalar.ActualTrimSpace)
593593
subType := ""
594-
if t := ctx.currentTrack(); t != nil {
595-
t.sampleDescriptions = append(t.sampleDescriptions, sampleDescription{
594+
track := ctx.currentTrack()
595+
if track != nil {
596+
track.sampleDescriptions = append(track.sampleDescriptions, sampleDescription{
596597
dataFormat: dataFormat,
597598
})
598599

599-
if t.seenHdlr {
600-
subType = t.subType
600+
if track.seenHdlr {
601+
subType = track.subType
601602
} else {
602603
// TODO: seems to be ffmpeg mov.c, where is this documented in specs?
603604
// no hdlr box found, guess using dataFormat
@@ -617,7 +618,6 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
617618

618619
switch subType {
619620
case "soun", "vide":
620-
621621
version := d.FieldU16("version")
622622
d.FieldU16("revision_level")
623623
d.FieldU32("max_packet_size") // TODO: vendor for some subtype?
@@ -626,9 +626,10 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
626626
case "soun":
627627
// AudioSampleEntry
628628
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW1
629+
var numAudioChannels uint64
629630
switch version {
630631
case 0:
631-
d.FieldU16("num_audio_channels")
632+
numAudioChannels = d.FieldU16("num_audio_channels")
632633
d.FieldU16("sample_size")
633634
d.FieldU16("compression_id")
634635
d.FieldU16("packet_size")
@@ -637,7 +638,7 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
637638
decodeBoxes(ctx, d)
638639
}
639640
case 1:
640-
d.FieldU16("num_audio_channels")
641+
numAudioChannels = d.FieldU16("num_audio_channels")
641642
d.FieldU16("sample_size")
642643
d.FieldU16("compression_id")
643644
d.FieldU16("packet_size")
@@ -657,7 +658,7 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
657658
d.FieldU32("always_65536")
658659
d.FieldU32("size_of_struct_only")
659660
d.FieldF64("audio_sample_rate")
660-
d.FieldU32("num_audio_channels")
661+
numAudioChannels = d.FieldU32("num_audio_channels")
661662
d.FieldU32("always_7f000000")
662663
d.FieldU32("const_bits_per_channel")
663664
d.FieldU32("format_specific_flags")
@@ -669,6 +670,9 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
669670
default:
670671
d.FieldRawLen("data", d.BitsLeft())
671672
}
673+
if track != nil {
674+
track.stsdNumAudioChannels = numAudioChannels
675+
}
672676
case "vide":
673677
// VideoSampleEntry
674678
// TODO: version 0 and 1 same?
@@ -1832,6 +1836,88 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
18321836
d.FieldRawLen("uid", 128)
18331837
}
18341838
})
1839+
case "pcmC":
1840+
d.FieldU8("version")
1841+
d.FieldU24("flags")
1842+
d.FieldU8("format_flags")
1843+
d.FieldU8("sample_size")
1844+
case "chnl":
1845+
version := d.FieldU8("version")
1846+
d.FieldU24("flags")
1847+
1848+
if version == 0 {
1849+
hasObjects := false
1850+
hasChannels := false
1851+
d.FieldStruct("stream_structure", func(d *decode.D) {
1852+
d.FieldRawLen("unused", 6)
1853+
hasObjects = d.FieldBool("objects")
1854+
hasChannels = d.FieldBool("channels")
1855+
})
1856+
if hasChannels {
1857+
definedLayout := d.FieldU8("defined_layout")
1858+
if definedLayout == 0 {
1859+
track := ctx.currentTrack()
1860+
if track == nil {
1861+
d.FieldRawLen("rest", d.BitsLeft())
1862+
break
1863+
}
1864+
d.FieldArray("channels", func(d *decode.D) {
1865+
for i := 0; i < int(track.stsdNumAudioChannels); i++ {
1866+
d.FieldStruct("channel", func(d *decode.D) {
1867+
speakerPosition := d.FieldU8("speaker_position")
1868+
if speakerPosition == 126 {
1869+
d.FieldS16("azimuth")
1870+
d.FieldS8("elevation")
1871+
}
1872+
})
1873+
}
1874+
})
1875+
} else {
1876+
d.FieldU64("omitted_channels_map")
1877+
}
1878+
}
1879+
if hasObjects {
1880+
d.FieldU8("object_count")
1881+
}
1882+
} else {
1883+
hasObjects := false
1884+
hasChannels := false
1885+
d.FieldStruct("stream_structure", func(d *decode.D) {
1886+
d.FieldRawLen("unused", 2)
1887+
hasObjects = d.FieldBool("objects")
1888+
hasChannels = d.FieldBool("channels")
1889+
})
1890+
d.FieldU4("format_ordering")
1891+
d.FieldU8("base_channel_count")
1892+
if hasChannels {
1893+
definedLayout := d.FieldU8("defined_layout")
1894+
if definedLayout == 0 {
1895+
layoutChannelCount := d.FieldU8("layout_channel_count")
1896+
d.FieldArray("channels", func(d *decode.D) {
1897+
for i := 0; i < int(layoutChannelCount); i++ {
1898+
d.FieldStruct("channel", func(d *decode.D) {
1899+
speakerPosition := d.FieldU8("speaker_position")
1900+
if speakerPosition == 126 {
1901+
d.FieldS16("azimuth")
1902+
d.FieldS8("elevation")
1903+
}
1904+
})
1905+
}
1906+
})
1907+
} else {
1908+
d.FieldRawLen("reserved", 4)
1909+
d.FieldU3("channel_order_definition")
1910+
omittedChannelsPresent := d.FieldBool("omitted_channels_present")
1911+
if omittedChannelsPresent {
1912+
d.FieldU64("omitted_channels_map")
1913+
}
1914+
}
1915+
}
1916+
if hasObjects {
1917+
// ISO/IEC 14496-12:2022:
1918+
// > object_count is derived from baseChannelCount
1919+
}
1920+
}
18351921

18361922
default:
18371923
// there are at least 4 ways to encode udta metadata in mov/mp4 files.

format/mp4/mp4.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -136,20 +136,21 @@ type stsz struct {
136136
}
137137

138138
type track struct {
139-
seenHdlr bool
140-
fragment bool
141-
id int
142-
sampleDescriptions []sampleDescription
143-
subType string
144-
stco []int64
145-
stsc []stsc
146-
stsz []stsz
147-
formatInArg any
148-
objectType int // if data format is "mp4a"
149-
defaultIVSize int
150-
moofs []*moof // for fmp4
151-
dref bool
152-
drefURL string
139+
seenHdlr bool
140+
fragment bool
141+
id int
142+
sampleDescriptions []sampleDescription
143+
subType string
144+
stco []int64
145+
stsc []stsc
146+
stsz []stsz
147+
formatInArg any
148+
objectType int // if data format is "mp4a"
149+
defaultIVSize int
150+
moofs []*moof // for fmp4
151+
dref bool
152+
drefURL string
153+
stsdNumAudioChannels uint64
153154
}
154155

155156
type pathEntry struct {

format/mp4/testdata/chnl-ver1

16 Bytes
Binary file not shown.

format/mp4/testdata/chnl-ver1.fqtest

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
$ fq -o force=true -d mp4 dv chnl-ver1
2+
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: chnl-ver1 (mp4) 0x0-0x10 (16)
3+
| | | boxes[0:1]: 0x0-0x10 (16)
4+
| | | [0]{}: box 0x0-0x10 (16)
5+
0x00|00 00 00 10 |.... | size: 16 0x0-0x4 (4)
6+
0x00| 63 68 6e 6c | chnl | type: "chnl" 0x4-0x8 (4)
7+
0x00| 01 | . | version: 1 0x8-0x9 (1)
8+
0x00| 00 00 00 | ... | flags: 0 0x9-0xc (3)
9+
| | | stream_structure{}: 0xc-0xc.4 (0.4)
10+
0x00| 11 | . | unused: raw bits 0xc-0xc.2 (0.2)
11+
0x00| 11 | . | objects: false 0xc.2-0xc.3 (0.1)
12+
0x00| 11 | . | channels: true 0xc.3-0xc.4 (0.1)
13+
0x00| 11 | . | format_ordering: 1 0xc.4-0xd (0.4)
14+
0x00| 02 | . | base_channel_count: 2 0xd-0xe (1)
15+
0x00| 02 | . | defined_layout: 2 0xe-0xf (1)
16+
0x00| 00| .| reserved: raw bits 0xf-0xf.4 (0.4)
17+
0x00| 00| .| channel_order_definition: 0 0xf.4-0xf.7 (0.3)
18+
0x00| 00| .| omitted_channels_present: false 0xf.7-0x10 (0.1)

format/mp4/testdata/pcmC

14 Bytes
Binary file not shown.

format/mp4/testdata/pcmC.fqtest

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
$ fq -o force=true -d mp4 dv pcmC
2+
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: pcmC (mp4) 0x0-0xe (14)
3+
| | | boxes[0:1]: 0x0-0xe (14)
4+
| | | [0]{}: box 0x0-0xe (14)
5+
0x0|00 00 00 0e |.... | size: 14 0x0-0x4 (4)
6+
0x0| 70 63 6d 43 | pcmC | type: "pcmC" 0x4-0x8 (4)
7+
0x0| 00 | . | version: 0 0x8-0x9 (1)
8+
0x0| 00 00 00 | ... | flags: 0 0x9-0xc (3)
9+
0x0| 01 | . | format_flags: 1 0xc-0xd (1)
10+
0x0| 18| | .| | sample_size: 24 0xd-0xe (1)

0 commit comments

Comments
 (0)