Skip to content

Add support for MMT/TLV container - #2647

Open
saindriches wants to merge 8 commits into
MediaArea:masterfrom
saindriches:mmt-tlv
Open

Add support for MMT/TLV container#2647
saindriches wants to merge 8 commits into
MediaArea:masterfrom
saindriches:mmt-tlv

Conversation

@saindriches

Copy link
Copy Markdown

Adds a parser for MMT (ISO/IEC 23008-1) over TLV packets (ARIB STD-B32), the container used by Japanese ISDB-S3 4K/8K broadcasts. This covers what is currently needed to identify and inspect these files; further fields can follow as use cases arise.

  • Maps the MPT asset list to HEVC, AAC, and TTML streams, and probes each for codec detail.
  • Surfaces the present MH-EIT event as a Menu chapter, with the stream start time and a duration derived from the MPU presentation timestamps, in UTC.
  • Adds the MH-SDT service name, and from the MH-audio-component descriptor the audio language, a default-track flag, and a Visually Impaired service kind for audio-description tracks.
  • Reports scrambled streams with Encryption set to Encrypted, and still reads timing and service metadata from the clear signaling.

Samples

This is my first contribution here, so please point out anything I have done wrong or should change.

@saindriches

saindriches commented Jul 5, 2026

Copy link
Copy Markdown
Author

A follow-up round since the first review, still all from the clear signaling so it works on scrambled files:

  • Fills the video description (resolution, frame rate, scan, display aspect, BT.2020 colour) from the MH-video-component descriptor, preferring the decoded HEVC ES when present.
  • Takes the audio channel count, positions, and layout from the shared AAC channel tables.
  • Groups a stream's main and robust-backup versions (hierarchical transmission, used for rain fade) from the Asset_Group_Descriptor into an AlternateGroup, defaulting the selection-level-0 member.
  • Binds the streams to the current MPT version so a shorter later program leaves no stale tracks, and settles the A/V config across a program boundary.
  • Fixes Duration when there is no MH-TOT (the NTP clock froze after one read), and reads MH-EIT descriptor lengths per the tag's field size so the program name survives a leading 8-bit descriptor.

@cjee21

cjee21 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

More files have to be updated. You may refer to https://github.com/MediaArea/MediaInfoLib/pull/2458/changes. The MediaInfo_Internal::LibraryIsModified part of MediaInfo_File.cpphave to be updated too. You can also activate GitHub Actions on your fork to ensure the CI (which ensures it builds on all tested build systems, compilers and platforms) passes without waiting for a maintainer to approve the workflow run here.

Generally only General_Format needs to be filled for format. The other details come automatically from the database (Format.csv) that is used to generate MediaInfo_Config_Automatic.cpp.

I also see that this is mostly implemented differently from a typical MediaInfo parser in that is does not use MediaInfo's Get/Skip and bitstream parsing functions beyond the header part so it will not produce a trace (--trace_level=1) other than the header, bypasses internal integrity checks and offset tracking as well as may be difficult for others to maintain or read the bitstream syntax. Not sure if that is acceptable since I am just a contributor.

For example, this:

void File_MmtTlv::Parse_Mmtp(const int8u* Data, size_t Size)
{
    if (Size < 12)
        return;
    int8u b0               = Data[0];
    bool  packet_counter   = (b0 >> 5) & 1;
    bool  extension_header = (b0 >> 1) & 1;
    int8u payload_type     = Data[1] & 0x3F;
    const int8u* p = Data + 2;
    size_t       n = Size - 2;
    // packet_id(16) + distribute_timestamp(32) + packet_sequence_number(32)
    if (n < 10)
        return;
    int16u packet_id  = RB16(p);

should be something (may not be exactly like this, just example) like:

void File_MmtTlv::Parse_Mmtp()
{
    bool  packet_counter, extension_header;
    int8u payload_type;
    int16u packet_id;
    BS_Begin();
    Skip_S1(2,                                                  "version");
    Get_SB (   packet_counter,                                  "packet_counter_flag");
    Skip_S1(2,                                                  "FEC_type");
    Skip_S1(1,                                                  "reserved");
    Get_SB (   extension_header,                                "extension_flag");
    Skip_SB(                                                    "RAP_flag");
    Skip_S1(2,                                                  "reserved");
    Get_S1 (6, payload_type,                                    "payload_type");
    BS_End();
    Get_B2 (packet_id,                                          "packet_id");

I did not test but reading the codes, looks like parsing with HEVC and AAC parser may not work properly as well.

@saindriches

Copy link
Copy Markdown
Author

Thank you for the important notes, I will do a review and maybe force-push a rewrite then.

@JeromeMartinez

Copy link
Copy Markdown
Member

@saindriches thank you for your PR. Updated @cjee21 comments are good, please update the PR with that.
It permits to debug the files with MediaTrace.
I may be slow to merge (@cjee21 knows that :( ), but I value your work much.

@JeromeMartinez JeromeMartinez self-assigned this Jul 7, 2026
MMT (MPEG Media Transport, ISO/IEC 23008-1) framed in TLV packets (ARIB
STD-B32), the container used by Japanese ISDB-S3 BS/CS 4K/8K broadcasts.

Maps the MPT asset list to HEVC, AAC and TTML streams; surfaces the present
MH-EIT event as a Menu chapter (name, description, schedule), the MH-SDT
service name, the video/audio component descriptors, and the scramble state.
Everything comes from the clear signaling, so it also works on scrambled files.

Structured like File_MpegTs: File_MmtTlv handles TLV, compressed-IP and MMTP
framing plus fragment reassembly, then hands complete signaling tables to
File_Mmt (tables) and File_Mmt_Descriptors (descriptors) via Open_Buffer, and
creates a child ES parser per asset by asset_type. Parsing uses the standard
Get_/Skip_/BS_ helpers throughout, so it produces a full trace.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
Adds the MMT/TLV row (category Multiple, extension mmts, description) to
Format.csv and the generated MediaInfo_Config_Automatic.cpp, so the format info
and extensions come from the database rather than being filled by the parser.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
Registers File_MmtTlv, File_Mmt and File_Mmt_Descriptors in Makefile.am,
configure.ac (--disable-mmttlv, MEDIAINFO_MMTTLV_NO/_YES), the CMake feature
list, the MSVC 2022 and 2026 projects (vcxproj, filters, UWP) and the Qt
project.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
@saindriches

Copy link
Copy Markdown
Author

I rewrote this to match the style, now squashed into three commits. Structurally it follows the MPEG-TS side: File_MmtTlv does the TLV/MMTP framing and reassembly, then hands tables to File_Mmt and descriptors to File_Mmt_Descriptors, like File_MpegTs / File_Mpeg_Psi / File_Mpeg_Descriptors. Everything is parsed with the usual helpers, so the trace is complete. All STD-B60 tables and descriptors are named even when not parsed further.

Tested on a lot of real captures, and CI is green including the minimal build. Happy to make any changes needed.

@cjee21

cjee21 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

I still see custom byte reading functions and some manual pointer (assigned from MediaInfo's Buffer) and size operations. Is this really needed and cannot be done with MediaInfo's built-in features?

I am not sure about this but this seems incorrect. mpt_desc_len is already parsed by sub parser and after that skip mpt_desc_len again? There's also probably no need to manually pass Buffer to sub parser. Same with desc_loop_len and maybe others.

        Open_Buffer_Init(&Desc);
        Open_Buffer_Continue(&Desc, Buffer + Buffer_Offset + (size_t)Element_Offset, mpt_desc_len);
        Open_Buffer_Finalize(&Desc);
        Skip_XX(mpt_desc_len,                                   "MPT_descriptors");

And what is this Data/Size parameters and void casting that look useless?

void File_MmtTlv::Parse_Mpu(int16u packet_id, int32u seq_num, const int8u* Data, size_t Size)
{
    (void)Data; (void)Size; // parse at the element cursor (== Data/Size handed by Parse_Mmtp)

Can consider using smart pointer for the audio/video parsers so that no need to manually delete and set to null. Since new and delete is at different places far apart, will also prevent mistakes in the future if that part is ever modified.

This is from my quick look-through. I did not test or analyze in-depth.

@saindriches

Copy link
Copy Markdown
Author

Thanks a lot for the review. I don't have deep experience with C++, this is helpful.
I will deal with the descriptor sub-parsing, the a/v parsers, and the avoidable manual byte reads.

In MMT the fragment boundaries are signaled in the MMTP header (the fragmentation_indicator), not in the payload, so the container has to decode those per-packet flags and join the fragments in packet_sequence_number order before a complete signaling message or MFU exists, and the remaining reads work on that assembled buffer.

@cjee21

cjee21 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Another thing is init of variables can be done directly at the declaration in the header file instead of assigning in the constructor.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
The forward scan could commit the present event and finalize before the first NTP/TOT arrived, so a recording that opens a few seconds before a program boundary froze on the outgoing program (e.g. a 1-minute PR spot) and missed the target program's assets. Leave the event uncommitted until a clock is in hand, so the boundary hop always runs and the scan cannot finalize early.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
@cjee21

cjee21 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Just some minor things found by static analysis:

Warning	C26819	Unannotated fallthrough between switch labels (es.78).	MediaInfoLib	MediaInfoLib\Source\MediaInfo\Multiple\File_MmtTlv.cpp	1500		
MediaInfoLib/Source/MediaInfo/Multiple/File_MmtTlv.cpp:1323:33:style: Variable 'Found' can be declared as reference to const [constVariableReference]

This part does not look right to me from the offsets but since I don't really know about this format and what's happening there, let @JeromeMartinez comment when he reviews. I am not that good at handling fragments and conversions like handling removing HEVC AnnexB emulation prevention bytes or passing fragmented XMP from JPEG to XMP parser.

002DD62   data_unit_length:                     21 (0x0015)
002DD64   access_unit_delimiter (6 bytes)
002DD64    Header (5 bytes)
002DD64     start_code_prefix_one_3bytes:       1 (0x000001)
002DD67     nal_unit_type:                      35 (0x23) - (6 bits)
002DD67     nuh_layer_id:                       0 (0x00) - (6 bits)
002DD68     nuh_temporal_id_plus1:              1 (0x1) - (3 bits)
002DD69    pic_type:                            0 (0x0) - (3 bits) - I
002DD64   data_unit:                            (21 bytes)
002DD79   data_unit_length:                     45 (0x002D)
002DD7B   video_parameter_set (30 bytes)

Also Trace for AAC and some Data looks like they are outside MPU.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
A fragmented NAL was reassembled and fed to the child at the last fragment's
offset, so it was traced past the MPU the bytes came from. Feed each fragment as
it arrives (FrameIsAlwaysComplete=false, MustAdaptSubOffsets=true) so it is
traced where it lives. AAC keeps reassembling first, since its LOAS length
header is only known once the whole unit is in hand.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
The MPU line gave no hint what its payload was, so the child ES appeared with
nothing tying it to a codec. Name it from the MPT asset_type (4CC plus codec,
so an unmapped type still shows its 4CC), and spell out fragmentation_indicator.
Trace only.

Signed-off-by: Rainbaby <rainbaby@outlook.jp>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants