Where is pixel format stored in H.264 MP4 file?
Asked Answered
U

1

1

I'm working on a transmuxer that will convert an H.264/AAC RTMP stream to a valid MP4 file. I'm mostly done. I'm parsing the AMF tag, reading the AVCDecoderConfigurationRecord and AACSpecificConfig, I'm generating a valid moov atom, etc.

After discovering and fixing a few bugs in my code, I've got a mostly valid MP4 file. However when I attempt to read the video in ffprobe I get the following error:

[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f9fb4000b80] Failed to open codec in avformat_find_stream_info
    Last message repeated 1 times
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f9fb4000b80] Could not find codec parameters for stream 1 (Video: h264 (avc1 / 0x31637661), none, 640x360): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options

It's unable to find the pixel format. Skimming through my AVCDecoderConfigurationRecord parsing logic (which is used to generate the avcC atom as part of the avc1 atom), I have the following:

// Parsed per: https://github.com/LiminWang/simple-rtmp-server/blob/master/trunk/doc/H.264-AVC-ISO_IEC_14496-15.pdf
var info = parseAVCConfig(packet);

// Fortunately my video sample has one of each of these
// I may need to concatenate multiple in the future
var sps = info.sps[0];
var pps = info.pps[0];

var avcc = box(
    types.avcC,
    new Uint8Array([
        // Version
        0x01,
        // Profile
        info.profile,
        // Profile Compat
        info.compat,
        // Level
        info.level,
        // LengthSizeMinusOne, hard-coded to 4 bytes (copied HLS.js)
        0xfc | 3,
        // 3bit reserved (111) + numOfSequenceParameterSets
        0xE0 | sps.byteLength
    ]
        .concat(Array.from(sps))
        .concat([
            // NumOfPictureParametersets
            pps.byteLength
        ])
        .concat(Array.from(pps))
    )
);

As you can see the avcc atom contains the profile, compat, and level -- but after that I just copy over the SPS and PPS directly from the AVCDecoderConfigurationRecord. Nowhere in the atom do I define a pixel format, so I assumed it was part of the SPS or PPS.

Looking at the spec for the AVCDecoderConfigurationRecord, there's nothing specifically called "pixel format", but there is a "chroma_format", "bit_depth_luma_minus8", and "bit_depth_chroma_minus_8" -- however these only exist if the profile is 100, 110, 122, or 244. My profile is 66 (and these bytes don't exist for me)

At the moment this proof of concept I'm doing only has to support a single video, so worst-case scenario I can hard-code the pixel format to yuv420. But I don't even know where to put this information in the output MP4. Does it go in the avcC atom? Or the avc1 atom? Or the mvhd atom?

Links:

Uranology answered 12/12, 2018 at 14:42 Comment(0)
A
1

Take a look at the chroma_format_idc Rec. ITU-T H.264 (04/2017) - 7.3.2.1.1 Sequence parameter set data syntax. chroma_format_idc is part of SPS. For profile_idc 100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134 or 135 the chroma_format_idc is stored inside the SPS. Otherwise you assume 1 (= 4:2:0).

7.4.2.1.1 Sequence parameter set data semantics

chroma_format_idc specifies the chroma sampling relative to the luma sampling as specified in clause 6.2. The value of
chroma_format_idc shall be in the range of 0 to 3, inclusive. When chroma_format_idc is not present, it shall be inferred
to be equal to 1 (4:2:0 chroma format).
Arched answered 12/12, 2018 at 17:38 Comment(8)
I need to look up Rec. ITU-T H.264 (04/2017) - 7.3.2.1.1 to see if I can find an official spec to read, but my particular data has a profile_idc of 66 (0x42), so shouldn't ffprobe assume it's 1 (= 4:2:0)? Why is ffprobe saying it can't find the pixel format?Uranology
To add yet more confusion: I transmuxed the same video I'm streaming with ffmpeg to compare the two moov atoms. The file made with ffmpeg is detected perfectly by ffprobe, but they have identical (byte-for-byte) SPS values. I can upload the two files somewhere for comparison.Uranology
Your buffer.mp4 file has no entries in any of the sample table boxes like 'stts', 'stsc', 'stsz' and 'stco'. Your 'mdat' is missing the size. So ffmpeg can access any samples.Arched
So if the profile isn't in the list, the pixel format is determined from the video data?Uranology
Probably not. But your file is invalid - so ffprobe may just give you a wrong error message. Try creating a valid file and then run ffprobe.Arched
The issue I'm running into is that the file I'm creating should be able to play using Media Source Extensions in a browser using byte range requests (#35178297). This is why my sample table boxes are empty. I've made an updated file with moof atoms but I'm not sure how to fill the mdat atoms properly as it's still not playing for me. I'll update the links in my questionUranology
MSE can't guess what's in your 'mdat' - so you either have to populate the sample tables - works for video on demand (not live). Or you have to create a fragmented mp4 file which uses a different set of boxes for sample offsets. Regardless - nothing is going to work unless your player can find the sample offsets - pointing into the 'mdat'Arched
So at this point you've answered the original question (the pixel format is only stored in the moov atom for certain profiles, otherwise it's determined by reading the samples -- which have to be written in a way ffprobe can find them). My new issue (figuring out how to write the samples and sample offsets) is unrelated to the original question. So I'll go ahead and mark your answer while I continue to research moof atoms and how to get the samples workingUranology

© 2022 - 2024 — McMap. All rights reserved.