STATUS parseH264SpsGetResolution()

in src/mkvgen/src/SpsParser.c [204:386]


STATUS parseH264SpsGetResolution(PBYTE pSps, UINT32 spsSize, PUINT16 pWidth, PUINT16 pHeight)
{
    STATUS retStatus = STATUS_SUCCESS;
    UINT32 frameCropLeftOffset = 0, frameCropRightOffset = 0, frameCropTopOffset = 0, frameCropBottomOffset = 0, picWidthInMbsMinus1 = 0,
           frameMbsOnlyFlag = 0, picHeightInMapUnitsMinus1 = 0, profileIdc = 0, sizeOfScalingList = 0, lastScale = 8, nextScale = 8,
           picOrderCntType = 0, numRefFramesInPicOrderCntCycle = 0, deltaScale = 0, chromaFormatIdc = 1, separateColourPlaneFlag = 0;
    UINT32 i, j, read;
    INT32 readInt, width, height, pixelWidth, pixelHeight, subWidthC, subHeightC, arrayWidth, cropUnitX, cropUnitY;

    BitReader bitReader;

    CHK(pSps != NULL && pWidth != NULL && pHeight != NULL, STATUS_NULL_ARG);
    CHK(spsSize != 0, STATUS_INVALID_ARG_LEN);

    // Create a bit reader on top of the SPS
    CHK_STATUS(bitReaderReset(&bitReader, pSps, spsSize * 8));

    // Read the SPS Nalu
    CHK_STATUS(bitReaderReadBits(&bitReader, 8, &read));
    CHK(((read & 0x80) == 0) && ((read & 0x60) != 0) && ((read & 0x1f) == SPS_NALU_TYPE), STATUS_MKV_INVALID_H264_H265_SPS_NALU);

    // Get the profile
    CHK_STATUS(bitReaderReadBits(&bitReader, 8, &profileIdc));

    // Skip the constraint set flags 0 - 5, the reserved 2 zero bits
    // 8 bits level_idc and after that read the exp golomb for seq_parameter_set_id
    CHK_STATUS(bitReaderReadBits(&bitReader, 6 + 2 + 8, &read));

    // Read exp golomb for seq_parameter_set_id
    CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &read));

    if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 ||
        profileIdc == 86 || profileIdc == 118 || profileIdc == 128 || profileIdc == 138) {
        // Read chroma_format_idc
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &chromaFormatIdc));

        if (chromaFormatIdc == 3) {
            // Read residual_colour_transform_flag
            CHK_STATUS(bitReaderReadBit(&bitReader, &separateColourPlaneFlag));
        }

        // Read bit_depth_luma_minus8
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &read));

        // Read bit_depth_chroma_minus8
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &read));

        // Read qpprime_y_zero_transform_bypass_flag
        CHK_STATUS(bitReaderReadBit(&bitReader, &read));

        // Read seq_scaling_matrix_present_flag
        CHK_STATUS(bitReaderReadBit(&bitReader, &read));

        if (read != 0) {
            for (i = 0; i < 8; i++) {
                // Read seq_scaling_list_present_flag
                CHK_STATUS(bitReaderReadBit(&bitReader, &read));

                if (read != 0) {
                    sizeOfScalingList = (i < 6) ? 16 : 64;
                    for (j = 0; j < sizeOfScalingList; j++) {
                        if (nextScale != 0) {
                            // Read delta scale
                            CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &deltaScale));
                            nextScale = (lastScale + deltaScale + 256) % 256;
                        }

                        lastScale = (nextScale == 0) ? lastScale : nextScale;
                    }
                }
            }
        }
    }

    // Read log2_max_frame_num_minus4
    CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &read));

    // Read pic_order_cnt_type
    CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &picOrderCntType));

    if (picOrderCntType == 0) {
        // Read log2_max_pic_order_cnt_lsb_minus4
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &read));
    } else if (picOrderCntType == 1) {
        // Read delta_pic_order_always_zero_flag
        CHK_STATUS(bitReaderReadBit(&bitReader, &read));

        // Read offset_for_non_ref_pic
        CHK_STATUS(bitReaderReadExpGolombSe(&bitReader, &readInt));

        // Read offset_for_top_to_bottom_field
        CHK_STATUS(bitReaderReadExpGolombSe(&bitReader, &readInt));

        // Read bit_depth_luma_minus8
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &numRefFramesInPicOrderCntCycle));

        for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
            // Read offset_for_ref_frame
            CHK_STATUS(bitReaderReadExpGolombSe(&bitReader, &readInt));
        }
    }

    // Read max_num_ref_frames
    CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &read));

    // Read gaps_in_frame_num_value_allowed_flag
    CHK_STATUS(bitReaderReadBit(&bitReader, &read));

    // Read pic_width_in_mbs_minus1
    CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &picWidthInMbsMinus1));

    // Read pic_height_in_map_units_minus1
    CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &picHeightInMapUnitsMinus1));

    // Read frame_mbs_only_flag
    CHK_STATUS(bitReaderReadBit(&bitReader, &frameMbsOnlyFlag));

    if (frameMbsOnlyFlag == 0) {
        // Read mb_adaptive_frame_field_flag
        CHK_STATUS(bitReaderReadBit(&bitReader, &read));
    }

    // Read direct_8x8_inference_flag
    CHK_STATUS(bitReaderReadBit(&bitReader, &read));

    // Read frame_cropping_flag
    CHK_STATUS(bitReaderReadBit(&bitReader, &read));

    if (read != 0) {
        // Read frame_crop_left_offset
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &frameCropLeftOffset));

        // Read frame_crop_right_offset
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &frameCropRightOffset));

        // Read frame_crop_top_offset
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &frameCropTopOffset));

        // Read frame_crop_bottom_offset
        CHK_STATUS(bitReaderReadExpGolomb(&bitReader, &frameCropBottomOffset));
    }

    // Read vui_parameters_present_flag
    CHK_STATUS(bitReaderReadBit(&bitReader, &read));

    // Proper width and height extraction is defined in part in
    // 7.3.2.1.1 for SPS syntax: https://www.itu.int/rec/T-REC-H.264-201304-S/en
    arrayWidth = frameMbsOnlyFlag != 0 ? 1 : 2;
    pixelWidth = (picWidthInMbsMinus1 + 1) * 16;
    pixelHeight = (picHeightInMapUnitsMinus1 + 1) * 16 * arrayWidth;

    switch (chromaFormatIdc) {
        case 1:
            subWidthC = 2;
            subHeightC = 2;
            break;

        case 2:
            subWidthC = 2;
            subHeightC = 1;
            break;

        default:
            subWidthC = 1;
            subHeightC = 1;
    }

    cropUnitX = subWidthC;
    cropUnitY = subHeightC * arrayWidth;

    width = pixelWidth - frameCropLeftOffset * cropUnitX - frameCropRightOffset * cropUnitX;
    height = pixelHeight - frameCropTopOffset * cropUnitY - frameCropBottomOffset * cropUnitY;

    CHK(width >= 0 && width <= MAX_UINT16, STATUS_MKV_INVALID_H264_H265_SPS_WIDTH);
    CHK(height >= 0 && height <= MAX_UINT16, STATUS_MKV_INVALID_H264_H265_SPS_HEIGHT);

    *pWidth = (UINT16) width;
    *pHeight = (UINT16) height;

CleanUp:

    return retStatus;
}