STATUS adaptH265CpdNalsFromAnnexBToHvcc()

in src/mkvgen/src/NalAdapter.c [270:416]


STATUS adaptH265CpdNalsFromAnnexBToHvcc(PBYTE pCpd, UINT32 cpdSize, PBYTE pAdaptedCpd, PUINT32 pAdaptedCpdSize)
{
    STATUS retStatus = STATUS_SUCCESS;
    UINT32 naluSize, adaptedRawSize, adaptedCpdSize = 0, naluCount = 0, i;
    PBYTE pAdaptedBits = NULL, pCurPnt = pAdaptedCpd, pSrcPnt;
    H265SpsInfo spsInfo;
    UINT32 naluSizes[3];
    PBYTE naluPtrs[3];
    // Nalu types in order - VPS, SPS, PPS
    BYTE naluTypes[3] = {HEVC_VPS_NALU_TYPE, HEVC_SPS_NALU_TYPE, HEVC_PPS_NALU_TYPE};

    CHK(pCpd != NULL && pAdaptedCpdSize != NULL, STATUS_NULL_ARG);

    // We should have at least 3 NALs with at least single byte data so even the short start code should account for 12
    CHK(cpdSize >= MIN_H_265_ANNEXB_CPD_SIZE, STATUS_MKV_MIN_ANNEX_B_CPD_SIZE);

    // Convert the raw bits
    CHK_STATUS(adaptFrameNalsFromAnnexBToAvcc(pCpd, cpdSize, FALSE, NULL, &adaptedRawSize));

    // Allocate enough storage to store the data temporarily
    pAdaptedBits = (PBYTE) MEMALLOC(adaptedRawSize);
    CHK(pAdaptedBits != NULL, STATUS_NOT_ENOUGH_MEMORY);

    // Get the converted bits
    CHK_STATUS(adaptFrameNalsFromAnnexBToAvcc(pCpd, cpdSize, FALSE, pAdaptedBits, &adaptedRawSize));

    // Set the source pointer to walk the adapted data
    pSrcPnt = pAdaptedBits;

    // Get the NALu count and store the pointer to SPS
    // It should be VPS/SPS/PPS
    // In some cases the PPS might be missing
    while (naluCount < ARRAY_SIZE(naluTypes) && (UINT32)(pSrcPnt - pAdaptedBits) < adaptedRawSize) {
        CHK(pSrcPnt - pAdaptedBits + SIZEOF(UINT32) <= adaptedRawSize, STATUS_MKV_INVALID_ANNEXB_CPD_NALUS);
        naluSize = (UINT32) GET_UNALIGNED_BIG_ENDIAN((PUINT32) pSrcPnt);

        // Store the NALU pointer
        naluPtrs[naluCount] = pSrcPnt + SIZEOF(UINT32);
        naluSizes[naluCount] = naluSize;

        pSrcPnt += SIZEOF(UINT32) + naluSize;
        CHK((UINT32)(pSrcPnt - pAdaptedBits) <= adaptedRawSize, STATUS_MKV_INVALID_ANNEXB_CPD_NALUS);
        naluCount++;
    }

    CHK(naluCount >= 2, STATUS_MKV_ANNEXB_CPD_MISSING_NALUS);

    // Limit NALus to 3
    naluCount = MIN(3, naluCount);

    // Calculate the required size
    adaptedCpdSize = HEVC_CPD_HEADER_OVERHEAD + naluCount * HEVC_CPD_ENTRY_OVERHEAD;

    // Add the raw size
    for (i = 0; i < naluCount; i++) {
        adaptedCpdSize += naluSizes[i];
    }

    // Quick check for size only
    CHK(pAdaptedCpd != NULL, retStatus);

    // If we get a larger buffer size then it's an error
    CHK(adaptedCpdSize <= *pAdaptedCpdSize, STATUS_BUFFER_TOO_SMALL);

    // Parse the SPS
    CHK_STATUS(parseH265Sps(naluPtrs[1], naluSizes[1], &spsInfo));

    // Start converting and copying it to the output

    // configurationVersion
    *pCurPnt++ = HEVC_CONFIG_VERSION_CODE;

    // general_profile_space, general_tier_flag, general_profile_idc
    *pCurPnt++ = (UINT8)(spsInfo.general_profile_space << 6 | (spsInfo.general_tier_flag & 0x01) << 5 | (spsInfo.general_profile_idc & 0x1f));

    // general_profile_compatibility_flags
    *pCurPnt++ = spsInfo.general_profile_compatibility_flags[0];
    *pCurPnt++ = spsInfo.general_profile_compatibility_flags[1];
    *pCurPnt++ = spsInfo.general_profile_compatibility_flags[2];
    *pCurPnt++ = spsInfo.general_profile_compatibility_flags[3];

    // general_constraint_indicator_flags
    *pCurPnt++ = spsInfo.general_constraint_indicator_flags[0];
    *pCurPnt++ = spsInfo.general_constraint_indicator_flags[1];
    *pCurPnt++ = spsInfo.general_constraint_indicator_flags[2];
    *pCurPnt++ = spsInfo.general_constraint_indicator_flags[3];
    *pCurPnt++ = spsInfo.general_constraint_indicator_flags[4];
    *pCurPnt++ = spsInfo.general_constraint_indicator_flags[5];

    // general_level_idc
    *pCurPnt++ = spsInfo.general_level_idc;

    // min_spatial_segmentation_idc
    *pCurPnt++ = 0xf0;
    *pCurPnt++ = 0x00;

    // parallelismType
    *pCurPnt++ = 0xfc;

    // chroma_format_idc
    *pCurPnt++ = (UINT8)(0xfc | (spsInfo.chroma_format_idc & 0x03));

    // bit_depth_luma_minus8
    *pCurPnt++ = (UINT8)(0xf8 | (spsInfo.bit_depth_luma_minus8 & 0x07));

    // bit_depth_luma_minus8
    *pCurPnt++ = (UINT8)(0xf8 | (spsInfo.bit_depth_chroma_minus8 & 0x07));

    // avgFrameRate
    *pCurPnt++ = 0x00;
    *pCurPnt++ = 0x00;

    // constantFrameRate, numTemporalLayers, temporalIdNested, lengthSizeMinusOne
    *pCurPnt++ = 0x0f;

    // numOfArrays
    *pCurPnt++ = (UINT8) naluCount;

    for (i = 0; i < naluCount; i++) {
        // array_completeness, reserved, NAL_unit_type
        *pCurPnt++ = naluTypes[i];

        // numNalus
        *pCurPnt++ = 0x00;
        *pCurPnt++ = 0x01;

        // nalUnitLength
        PUT_UNALIGNED_BIG_ENDIAN((PINT16) pCurPnt, (UINT16) naluSizes[i]);
        pCurPnt += SIZEOF(UINT16);

        // Write the NALu data
        MEMCPY(pCurPnt, naluPtrs[i], naluSizes[i]);
        pCurPnt += naluSizes[i];
    }

CleanUp:

    if (pAdaptedCpdSize != NULL) {
        *pAdaptedCpdSize = adaptedCpdSize;
    }

    if (pAdaptedBits != NULL) {
        MEMFREE(pAdaptedBits);
    }

    return retStatus;
}