wader / fq

jq for binary formats - tool, language and decoders for working with binary and text formats
Other
9.79k stars 227 forks source link

ADM (Audio Definition Model) and Dolby Metadata in WAV/RIFF #991

Open JohnnyMarnell opened 3 months ago

JohnnyMarnell commented 3 months ago

<chna> and <axml> chunks of Audio Definition Model:

Dolby Metadata <dbmd> chunk, e.g. Atmos, AC3, Dolby Digital [Plus]:

Notes:

wader commented 3 months ago

Hey! nice! will review asap, today or during the weekend at latest

wader commented 3 months ago

To extract a field as binary you can do this:

# stdout is a tty so does fq's own hexdump
$ fq '.chunks[0] | tobytes' -o line_bytes=10 format/riff/testdata/bext.wav
     │00 01 02 03 04 05 06 07 08 09│0123456789│
0x000│62 65 78 74 5a 02 00 00 00 00│bextZ.....│.: raw bits 0x0-0x262 (610)
0x00a│00 00 00 00 00 00 00 00 00 00│..........│
0x014│00 00 00 00 00 00 00 00 00 00│..........│
*    │until 0x261.7 (end) (610)    │

# but if stdout is pipe it will output raw bytes
$ fq '.chunks[0] | tobytes' format/riff/testdata/bext.wav | hexdump -C
00000000  62 65 78 74 5a 02 00 00  00 00 00 00 00 00 00 00  |bextZ...........|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000100  00 00 00 00 00 00 00 00  52 45 41 50 45 52 00 00  |........REAPER..|
00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000140  00 00 00 00 00 00 00 00  32 30 30 31 2d 30 32 2d  |........2001-02-|
00000150  30 33 30 34 2d 30 35 2d  30 36 00 00 00 00 00 00  |0304-05-06......|
00000160  00 00 01 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000170  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000260  00 00                                             |..|
00000262

To concat/stitich things you can do something like this, sorry not the greatest example:

$ fq '.chunks[0] | [(.originator | tobytes[2:4]), "hello", 0xff, .version] | tobytes ' format/riff/testdata/bext.wav | hexdump -C
00000000  41 50 68 65 6c 6c 6f ff  01 00                    |APhello...|
0000000a
wader commented 3 months ago

Any idea what the license is for the official EBU ADM test files? ok to modify and distribute? sometimes i might be enough to strip out the audio etc somehow and keep just the parts fq will decode

I know that some linux distributions like debian are not happy with distribution the source package if it contains anything with restrictive license.

wader commented 3 months ago

Found this https://tech.ebu.ch/files/live/sites/tech/files/shared/testmaterial/ebu_test_sequences_policy_prices_190315.pdf not sure if it covert the adm files but sadly sounds very restrictive :( hmm whyyyy

wader commented 3 months ago

Hey! spent some time learning about adm, atoms etc and made things a bit more fq idiomatic here https://github.com/wader/fq/tree/jm/adm

JohnnyMarnell commented 3 months ago

Found this https://tech.ebu.ch/files/live/sites/tech/files/shared/testmaterial/ebu_test_sequences_policy_prices_190315.pdf not sure if it covert the adm files but sadly sounds very restrictive :( hmm whyyyy

I was able to make a quite small ADM BWF, hope to get it in + testes soon!

wader commented 3 months ago

So feel free to push my changes to this branch, i might continue on it later in the week

wader commented 3 months ago

Pushed some more stuff, now dolby_metadata is a proper format, not sure if adm should be one also hmm?

Maybe could be nice to squash all the commits to get a more clean picture

wader commented 3 months ago

still confused about trim config stuff and program_info, do you have any spec (verison 0.0.0.7?) or code that parses it. the dolby code seems to just skip it? (why?? 😄 )

wader commented 3 months ago

https://github.com/MediaArea/MediaInfoLib/blob/abdbb218b07f6cc0d4504c863ac5b42ecfab6fc6/Source/MediaInfo/Audio/File_DolbyAudioMetadata.cpp might be a good reference

JohnnyMarnell commented 1 month ago

Pushed some more stuff, now dolby_metadata is a proper format, not sure if adm should be one also hmm? Maybe could be nice to squash all the commits to get a more clean picture

Integrated your changes (finally) (thanks so much!), going to look into the formats / docs, since proprietary Dolby is separate. And will get out my git manual and attempt commits clean up 🥴

JohnnyMarnell commented 1 month ago

TIL MediaInfo, thank so much for the reference...

wader commented 1 month ago

Pushed some more stuff, now dolby_metadata is a proper format, not sure if adm should be one also hmm? Maybe could be nice to squash all the commits to get a more clean picture

Integrated your changes (finally) (thanks so much!), going to look into the formats / docs, since proprietary Dolby is separate. And will get out my git manual and attempt commits clean up 🥴

👍 if you do make doc you will see how it works, both README.md and doc/formats.md will be updated automatically based on registered formats and doumentation.

It would be great if you could more or less squash all into one or few commits, preferably without merge commits.

wader commented 1 month ago

TIL MediaInfo, thank so much for the reference...

It's nice! similar to fq but less queryable and more about interpreting/validating things. Meet the main developer of it some years ago at a "no time to wait" conference, both there to present and yap about our tools :)

JohnnyMarnell commented 4 weeks ago

@wader One of last steps is to remove dolby.go, currently in there because of some comments I need to migrate, but mainly because the "dolby supplemental" metadata segment appears to still work and parse there, but I'm not seeing it in the new dolby_metadata.go code

wader commented 4 weeks ago

@wader One of last steps is to remove dolby.go, currently in there because of some comments I need to migrate, but mainly because the "dolby supplemental" metadata segment appears to still work and parse there, but I'm not seeing it in the new dolby_metadata.go code

Hmm stange, if i remove dolby.go it seems to build fine for me. But i don't see any supplement metadata_segment for the tests?

JohnnyMarnell commented 3 weeks ago

@wader Sorry, that wasn't clear, tests don't currently reflect it. If you run and compare this command's output as currently:

go run . -d wav '.chunks[] | select(.id | IN("dbmd")) | tovalue' ./format/riff/testdata/dolby_metadata.wav

vs after commenting and switching d.Format() for old_dbmdDecode(d) here: https://github.com/JohnnyMarnell/fq/blob/jm/adm/format/riff/wav.go#L188-L190 , you can see the dolby_atmos_supplemental isn't getting parsed currently. Not spotting why tho, I'm assuming it's something dumb I'm doing.

wader commented 3 weeks ago

@wader Sorry, that wasn't clear, tests don't currently reflect it. If you run and compare this command's output as currently:

go run . -d wav '.chunks[] | select(.id | IN("dbmd")) | tovalue' ./format/riff/testdata/dolby_metadata.wav

vs after commenting and switching d.Format() for old_dbmdDecode(d) here: https://github.com/JohnnyMarnell/fq/blob/jm/adm/format/riff/wav.go#L188-L190 , you can see the dolby_atmos_supplemental isn't getting parsed currently. Not spotting why tho, I'm assuming it's something dumb I'm doing.

Hmm try this, it seems to make things work. Probably one or more segment decoders don't read their full segment size. Using d.FramedFn it will make sure to limit and seek correctly.

diff --git a/format/riff/dolby_metadata.go b/format/riff/dolby_metadata.go
index 56a52113..50a9a717 100644
--- a/format/riff/dolby_metadata.go
+++ b/format/riff/dolby_metadata.go
@@ -51,23 +51,25 @@ func dbmdDecode(d *decode.D) any {

                segmentSize := d.FieldU16("size")

-               switch segmentID {
-               case metadataSegmentTypeDolbyE:
-                   parseDolbyE(d)
-               case metadataSegmentTypeDolbyDigital:
-                   parseDolbyDigital(d)
-               case metadataSegmentTypeDolbyDigitalPlus:
-                   parseDolbyDigitalPlus(d)
-               case metadataSegmentTypeAudioInfo:
-                   parseAudioInfo(d)
-               case metadataSegmentTypeDolbyAtmos:
-                   parseDolbyAtmos(d)
-               case metadataSegmentTypeDolbyAtmosSupplemental:
-                   parseDolbyAtmosSupplemental(d)
-               default:
-                   d.FieldRawLen("unknown", int64(segmentSize*8))
-               }
-
+               d.FramedFn(int64(segmentSize)*8, func(d *decode.D) {
+
+                   switch segmentID {
+                   case metadataSegmentTypeDolbyE:
+                       parseDolbyE(d)
+                   case metadataSegmentTypeDolbyDigital:
+                       parseDolbyDigital(d)
+                   case metadataSegmentTypeDolbyDigitalPlus:
+                       parseDolbyDigitalPlus(d)
+                   case metadataSegmentTypeAudioInfo:
+                       parseAudioInfo(d)
+                   case metadataSegmentTypeDolbyAtmos:
+                       parseDolbyAtmos(d)
+                   case metadataSegmentTypeDolbyAtmosSupplemental:
+                       parseDolbyAtmosSupplemental(d)
+                   default:
+                       d.FieldRawLen("unknown", d.BitsLeft())
+                   }
+               })
                // TODO: use this to validate parsing
                d.FieldU8("checksum", scalar.UintHex)
            })