STATUS mkvgenEbmlEncodeTrackInfo()

in src/mkvgen/src/MkvGenerator.c [1102:1264]


STATUS mkvgenEbmlEncodeTrackInfo(PBYTE pBuffer, UINT32 bufferSize, PStreamMkvGenerator pMkvGenerator, PUINT32 pEncodedLen)
{
    STATUS retStatus = STATUS_SUCCESS;
    UINT32 j, encodedCpdLen = 0, cpdSize = 0, encodedLen = 0, cpdOffset = 0;
    UINT32 mkvTrackDataSize = 0, mkvTracksDataSize = 0, codecIdDataSize = 0, trackNameDataSize = 0;
    PBYTE pTrackStart = NULL;
    UINT32 trackSpecificDataOffset = 0, mkvAudioBitsSize = 0;
    PTrackInfo pTrackInfo = NULL;

    CHK(pEncodedLen != NULL && pMkvGenerator != NULL, STATUS_NULL_ARG);

    // Set the size first
    *pEncodedLen = mkvgenGetMkvTrackHeaderSize(pMkvGenerator->trackInfoList, pMkvGenerator->trackInfoCount);

    // Quick return if we just need to calculate the size
    CHK(pBuffer != NULL, retStatus);

    CHK(bufferSize >= *pEncodedLen, STATUS_NOT_ENOUGH_MEMORY);

    // Copy over Tracks Header bytes (element + size)
    MEMCPY(pBuffer, MKV_TRACKS_ELEM_BITS, MKV_TRACKS_ELEM_BITS_SIZE);
    // Save the beginning of TrackEntry element
    pTrackStart = pBuffer + MKV_TRACK_ENTRY_OFFSET;

    for (j = 0; j < pMkvGenerator->trackInfoCount; ++j) {
        pTrackInfo = &pMkvGenerator->trackInfoList[j];

        MEMCPY(pTrackStart, MKV_TRACK_INFO_BITS, MKV_TRACK_INFO_BITS_SIZE);
        mkvTrackDataSize += MKV_TRACK_INFO_BITS_SIZE - MKV_TRACK_ENTRY_HEADER_BITS_SIZE;
        // Offset into available spots to store elements not in default TrackEntry template.
        // Such elements include audio, video, cpd.
        trackSpecificDataOffset = MKV_TRACK_INFO_BITS_SIZE;

        // Fix-up the track type
        *(pTrackStart + MKV_TRACK_TYPE_OFFSET) = (BYTE) pTrackInfo->trackType;

        // Package track name
        trackNameDataSize = (UINT32) STRNLEN(pTrackInfo->trackName, MKV_MAX_TRACK_NAME_LEN);
        if (trackNameDataSize > 0) {
            MEMCPY(pTrackStart + trackSpecificDataOffset, MKV_TRACK_NAME_BITS, MKV_TRACK_NAME_BITS_SIZE);
            trackSpecificDataOffset += MKV_TRACK_NAME_BITS_SIZE;

            // Encode the track name
            CHK_STATUS(
                mkvgenEbmlEncodeNumber(trackNameDataSize, pTrackStart + trackSpecificDataOffset, bufferSize - trackSpecificDataOffset, &encodedLen));
            trackSpecificDataOffset += encodedLen;

            MEMCPY(pTrackStart + trackSpecificDataOffset, pTrackInfo->trackName, trackNameDataSize);
            trackSpecificDataOffset += trackNameDataSize;
            mkvTrackDataSize += MKV_TRACK_NAME_BITS_SIZE + encodedLen + trackNameDataSize;
        }
        // done packaging track name

        // Package codec id
        codecIdDataSize = (UINT32) STRNLEN(pTrackInfo->codecId, MKV_MAX_CODEC_ID_LEN);
        if (codecIdDataSize > 0) {
            MEMCPY(pTrackStart + trackSpecificDataOffset, MKV_CODEC_ID_BITS, MKV_CODEC_ID_BITS_SIZE);
            trackSpecificDataOffset += MKV_CODEC_ID_BITS_SIZE;

            // Encode the codec id size
            CHK_STATUS(
                mkvgenEbmlEncodeNumber(codecIdDataSize, pTrackStart + trackSpecificDataOffset, bufferSize - trackSpecificDataOffset, &encodedLen));
            trackSpecificDataOffset += encodedLen;
            mkvTrackDataSize += MKV_CODEC_ID_BITS_SIZE + encodedLen + codecIdDataSize;
            MEMCPY(pTrackStart + trackSpecificDataOffset, pTrackInfo->codecId, codecIdDataSize);
            trackSpecificDataOffset += codecIdDataSize;
        }
        // done packaging codec id

        // Fix up track number. Use trackInfo's index as mkv track number. Mkv track number starts from 1
        *(pTrackStart + MKV_TRACK_NUMBER_OFFSET) = (BYTE)(j + 1);

        // Fix up the track UID
        PUT_UNALIGNED_BIG_ENDIAN((PINT64)(pTrackStart + MKV_TRACK_ID_OFFSET), pTrackInfo->trackId);

        // Append the video config if any
        if (GENERATE_VIDEO_CONFIG(&pMkvGenerator->trackInfoList[j])) {
            // Record the amount of storage used in order to skip to next track later
            mkvTrackDataSize += MKV_TRACK_VIDEO_BITS_SIZE;

            // Copy the element first
            MEMCPY(pTrackStart + trackSpecificDataOffset, MKV_TRACK_VIDEO_BITS, MKV_TRACK_VIDEO_BITS_SIZE);

            // Fix-up the width and height
            PUT_UNALIGNED_BIG_ENDIAN((PINT16)(pTrackStart + trackSpecificDataOffset + MKV_TRACK_VIDEO_WIDTH_OFFSET),
                                     pTrackInfo->trackCustomData.trackVideoConfig.videoWidth);
            PUT_UNALIGNED_BIG_ENDIAN((PINT16)(pTrackStart + trackSpecificDataOffset + MKV_TRACK_VIDEO_HEIGHT_OFFSET),
                                     pTrackInfo->trackCustomData.trackVideoConfig.videoHeight);

            trackSpecificDataOffset += MKV_TRACK_VIDEO_BITS_SIZE;

        } else if (GENERATE_AUDIO_CONFIG(&pMkvGenerator->trackInfoList[j])) {
            mkvAudioBitsSize = mkvgenGetMkvAudioBitsSize(&pMkvGenerator->trackInfoList[j]);

            // Record the amount of storage used in order to skip to next track later
            mkvTrackDataSize += mkvAudioBitsSize;

            // Copy the element first
            MEMCPY(pTrackStart + trackSpecificDataOffset, MKV_TRACK_AUDIO_BITS, MKV_TRACK_AUDIO_BITS_SIZE);

            PUT_UNALIGNED_BIG_ENDIAN((PINT64)(pTrackStart + trackSpecificDataOffset + MKV_TRACK_AUDIO_SAMPLING_RATE_OFFSET),
                                     *((PINT64)(&pTrackInfo->trackCustomData.trackAudioConfig.samplingFrequency)));
            *(pTrackStart + trackSpecificDataOffset + MKV_TRACK_AUDIO_CHANNELS_OFFSET) =
                (UINT8) pTrackInfo->trackCustomData.trackAudioConfig.channelConfig;

            if (pMkvGenerator->trackInfoList[j].trackCustomData.trackAudioConfig.bitDepth != 0) {
                MEMCPY(pTrackStart + trackSpecificDataOffset + MKV_TRACK_AUDIO_BITS_SIZE, MKV_TRACK_AUDIO_BIT_DEPTH_BITS,
                       MKV_TRACK_AUDIO_BIT_DEPTH_BITS_SIZE);
                *(pTrackStart + trackSpecificDataOffset + MKV_TRACK_AUDIO_BIT_DEPTH_OFFSET) =
                    (UINT8) pTrackInfo->trackCustomData.trackAudioConfig.bitDepth;

                // fix up audio element data size
                encodedLen = (0x10000000 | (UINT32)(mkvAudioBitsSize)) - MKV_TRACK_AUDIO_EBML_HEADER_SIZE;
                // +1 to skip the audio element
                PUT_UNALIGNED_BIG_ENDIAN((PINT32)(pTrackStart + trackSpecificDataOffset + 1), encodedLen);
            }

            trackSpecificDataOffset += mkvAudioBitsSize;
        }

        // Append the codec private data if any
        if (pTrackInfo->codecPrivateDataSize != 0 && pTrackInfo->codecPrivateData != NULL) {
            // Offset of the CPD right after the main structure and the video config if any
            cpdOffset = trackSpecificDataOffset;

            // Copy the element first
            MEMCPY(pTrackStart + cpdOffset, MKV_CODEC_PRIVATE_DATA_ELEM, MKV_CODEC_PRIVATE_DATA_ELEM_SIZE);
            cpdSize = MKV_CODEC_PRIVATE_DATA_ELEM_SIZE;

            // Encode the CPD size
            CHK_STATUS(mkvgenEbmlEncodeNumber(pTrackInfo->codecPrivateDataSize, pTrackStart + cpdOffset + cpdSize, bufferSize - cpdOffset - cpdSize,
                                              &encodedCpdLen));
            cpdSize += encodedCpdLen;

            CHK(cpdOffset + cpdSize + pTrackInfo->codecPrivateDataSize <= bufferSize, STATUS_NOT_ENOUGH_MEMORY);

            // Copy the actual CPD bits
            MEMCPY(pTrackStart + cpdOffset + cpdSize, pTrackInfo->codecPrivateData, pTrackInfo->codecPrivateDataSize);
            cpdSize += pTrackInfo->codecPrivateDataSize;
            mkvTrackDataSize += cpdSize;
        }

        // Important! Need to fix-up the overall track header element size and the track entry element size
        // Encode and fix-up the size - encode 4 bytes
        encodedLen = 0x10000000 | (UINT32)(mkvTrackDataSize);
        PUT_UNALIGNED_BIG_ENDIAN((PINT32)(pTrackStart + MKV_TRACK_ENTRY_SIZE_OFFSET), encodedLen);

        // Need to add back TrackEntry header bits to skip to the end of current TrackEntry
        pTrackStart += mkvTrackDataSize + MKV_TRACK_ENTRY_HEADER_BITS_SIZE;
        // TrackEntry header bits size also need to be accounted when calculating overall Tracks size
        mkvTracksDataSize += mkvTrackDataSize + MKV_TRACK_ENTRY_HEADER_BITS_SIZE;
        // Reset mkvTrackDataSize as we are starting for a new track
        mkvTrackDataSize = 0;
    }
    // Important! Need to fix-up the overall track header element size and the track entry element size
    // Encode and fix-up the size - encode 4 bytes
    encodedLen = 0x10000000 | (UINT32)(mkvTracksDataSize);
    PUT_UNALIGNED_BIG_ENDIAN((PINT32)(pBuffer + MKV_TRACK_HEADER_SIZE_OFFSET), encodedLen);

CleanUp:

    return retStatus;
}