STATUS mkvgenPackageFrame()

in src/mkvgen/src/MkvGenerator.c [214:391]


STATUS mkvgenPackageFrame(PMkvGenerator pMkvGenerator, PFrame pFrame, PTrackInfo pTrackInfo, PBYTE pBuffer, PUINT32 pSize,
                          PEncodedFrameInfo pEncodedFrameInfo)
{
    ENTERS();
    STATUS retStatus = STATUS_SUCCESS;
    PStreamMkvGenerator pStreamMkvGenerator = NULL;
    MKV_STREAM_STATE streamState = MKV_STATE_START_STREAM;
    UINT32 bufferSize = 0, encodedLen = 0, packagedSize = 0, adaptedFrameSize = 0, overheadSize = 0, dataOffset = 0;
    // Evaluated presentation and decode timestamps
    UINT64 pts = 0, dts = 0, duration = 0;
    PBYTE pCurrentPnt = pBuffer;
    MKV_NALS_ADAPTATION nalsAdaptation;

    // Check the input params
    CHK(pSize != NULL && pMkvGenerator != NULL && pTrackInfo != NULL, STATUS_NULL_ARG);

    pStreamMkvGenerator = (PStreamMkvGenerator) pMkvGenerator;

    // Validate and extract the timestamp
    CHK_STATUS(mkvgenValidateFrame(pStreamMkvGenerator, pFrame, pTrackInfo, &pts, &dts, &duration, &streamState));

    // Check to see if we can extract the CPD first.
    // Currently, we will process only if all of these conditions hold:
    // * NAL adaptation is specified from Annex-B
    // * CPD is not present yet
    // * We have a video track
    // * We have a key frame
    // * The content type is either H264 or H265
    if (pStreamMkvGenerator->nalsAdaptation == MKV_NALS_ADAPT_ANNEXB && pTrackInfo->codecPrivateDataSize == 0 &&
        pTrackInfo->trackType == MKV_TRACK_INFO_TYPE_VIDEO && CHECK_FRAME_FLAG_KEY_FRAME(pFrame->flags) &&
        (pStreamMkvGenerator->contentType & MKV_CONTENT_TYPE_H264_H265) != MKV_CONTENT_TYPE_NONE) {
        if (STATUS_FAILED(mkvgenExtractCpdFromAnnexBFrame(pStreamMkvGenerator, pFrame, pTrackInfo))) {
            DLOGW("Warning: Failed auto-extracting the CPD from the key frame.");
        }
    }

    // Calculate the necessary size

    // Get the overhead when packaging MKV
    overheadSize = mkvgenGetFrameOverhead(pStreamMkvGenerator, streamState);

    // NAL adaptation should only be done for video frames
    nalsAdaptation = pTrackInfo->trackType == MKV_TRACK_INFO_TYPE_VIDEO ? pStreamMkvGenerator->nalsAdaptation : MKV_NALS_ADAPT_NONE;

    // Get the adapted size of the frame and add to the overall size
    CHK_STATUS(getAdaptedFrameSize(pFrame, nalsAdaptation, &adaptedFrameSize));
    packagedSize = overheadSize + adaptedFrameSize;

    // Check if we are asked for size only and early return if so
    CHK(pBuffer != NULL, STATUS_SUCCESS);

    // Preliminary check for the buffer size
    CHK(*pSize >= packagedSize, STATUS_NOT_ENOUGH_MEMORY);

    // Start with the full buffer
    bufferSize = *pSize;

    // Generate the actual data
    switch (streamState) {
        case MKV_STATE_START_STREAM:
            if (pStreamMkvGenerator->generatorState == MKV_GENERATOR_STATE_START) {
                // Encode in sequence and subtract the size
                CHK_STATUS(mkvgenEbmlEncodeHeader(pCurrentPnt, bufferSize, &encodedLen));
                bufferSize -= encodedLen;
                pCurrentPnt += encodedLen;

                CHK_STATUS(mkvgenEbmlEncodeSegmentHeader(pCurrentPnt, bufferSize, &encodedLen));
                bufferSize -= encodedLen;
                pCurrentPnt += encodedLen;

                pStreamMkvGenerator->generatorState = MKV_GENERATOR_STATE_SEGMENT_HEADER;
            }

            if (pStreamMkvGenerator->generatorState == MKV_GENERATOR_STATE_SEGMENT_HEADER) {
                CHK_STATUS(mkvgenEbmlEncodeSegmentInfo(pStreamMkvGenerator, pCurrentPnt, bufferSize, &encodedLen));
                bufferSize -= encodedLen;
                pCurrentPnt += encodedLen;

                CHK_STATUS(mkvgenEbmlEncodeTrackInfo(pCurrentPnt, bufferSize, pStreamMkvGenerator, &encodedLen));
                bufferSize -= encodedLen;
                pCurrentPnt += encodedLen;

                pStreamMkvGenerator->generatorState = MKV_GENERATOR_STATE_CLUSTER_INFO;
            }

            // We are only interested in the header size in the data offset return
            dataOffset = overheadSize - mkvgenGetFrameOverhead(pStreamMkvGenerator, MKV_STATE_START_CLUSTER);

            // Fall-through
        case MKV_STATE_START_CLUSTER:
            // If we just added tags then we need to add the segment and track info
            if (pStreamMkvGenerator->generatorState == MKV_GENERATOR_STATE_SEGMENT_HEADER) {
                CHK_STATUS(mkvgenEbmlEncodeSegmentInfo(pStreamMkvGenerator, pCurrentPnt, bufferSize, &encodedLen));
                bufferSize -= encodedLen;
                pCurrentPnt += encodedLen;

                CHK_STATUS(mkvgenEbmlEncodeTrackInfo(pCurrentPnt, bufferSize, pStreamMkvGenerator, &encodedLen));
                bufferSize -= encodedLen;
                pCurrentPnt += encodedLen;

                pStreamMkvGenerator->generatorState = MKV_GENERATOR_STATE_CLUSTER_INFO;
            }

            // Store the stream timestamp if we started the stream
            if (!pStreamMkvGenerator->streamStartTimestampStored) {
                pStreamMkvGenerator->streamStartTimestamp = pts;
                pStreamMkvGenerator->streamStartTimestampStored = TRUE;
            }

            // Adjust the timestamp to the beginning of the stream if no absolute clustering
            CHK_STATUS(mkvgenEbmlEncodeClusterInfo(pCurrentPnt, bufferSize,
                                                   pStreamMkvGenerator->absoluteTimeClusters ? pts : pts - pStreamMkvGenerator->streamStartTimestamp,
                                                   &encodedLen));
            bufferSize -= encodedLen;
            pCurrentPnt += encodedLen;

            // Store the timestamp of the last cluster
            pStreamMkvGenerator->lastClusterPts = pts;
            pStreamMkvGenerator->lastClusterDts = dts;

            // indicate a cluster start
            pStreamMkvGenerator->generatorState = MKV_GENERATOR_STATE_CLUSTER_INFO;

            // Fall-through
        case MKV_STATE_START_BLOCK:
            // Ensure we are not in a TAGs state
            CHK(pStreamMkvGenerator->generatorState == MKV_GENERATOR_STATE_CLUSTER_INFO ||
                    pStreamMkvGenerator->generatorState == MKV_GENERATOR_STATE_SIMPLE_BLOCK,
                STATUS_MKV_INVALID_GENERATOR_STATE_TAGS);

            // Calculate the timestamp of the Frame relative to the cluster start
            if (pStreamMkvGenerator->generatorState == MKV_GENERATOR_STATE_CLUSTER_INFO) {
                pts = dts = 0;
            } else {
                // Make the timestamp relative
                pts -= pStreamMkvGenerator->lastClusterPts;
                dts -= pStreamMkvGenerator->lastClusterDts;
            }

            // The timecode for the frame has only 2 bytes which represent a signed int.
            CHK(pts <= MAX_INT16, STATUS_MKV_LARGE_FRAME_TIMECODE);

            // Adjust the timestamp to the start of the cluster
            CHK_STATUS(mkvgenEbmlEncodeSimpleBlock(pCurrentPnt, bufferSize, (INT16) pts, pFrame, nalsAdaptation, adaptedFrameSize,
                                                   pStreamMkvGenerator, &encodedLen));
            bufferSize -= encodedLen;
            pCurrentPnt += encodedLen;

            // Indicate a simple block
            pStreamMkvGenerator->generatorState = MKV_GENERATOR_STATE_SIMPLE_BLOCK;
            break;
    }

    // Validate the size
    CHK(packagedSize == (UINT32)(pCurrentPnt - pBuffer), STATUS_INTERNAL_ERROR);

CleanUp:

    if (STATUS_SUCCEEDED(retStatus)) {
        // Set the size and the state before return
        *pSize = packagedSize;

        if (pEncodedFrameInfo != NULL && pStreamMkvGenerator != NULL) {
            pEncodedFrameInfo->streamStartTs =
                MKV_TIMECODE_TO_TIMESTAMP(pStreamMkvGenerator->streamStartTimestamp, pStreamMkvGenerator->timecodeScale);
            pEncodedFrameInfo->clusterPts = MKV_TIMECODE_TO_TIMESTAMP(pStreamMkvGenerator->lastClusterPts, pStreamMkvGenerator->timecodeScale);
            pEncodedFrameInfo->clusterDts = MKV_TIMECODE_TO_TIMESTAMP(pStreamMkvGenerator->lastClusterDts, pStreamMkvGenerator->timecodeScale);
            pEncodedFrameInfo->framePts = MKV_TIMECODE_TO_TIMESTAMP(pts, pStreamMkvGenerator->timecodeScale);
            pEncodedFrameInfo->frameDts = MKV_TIMECODE_TO_TIMESTAMP(dts, pStreamMkvGenerator->timecodeScale);
            pEncodedFrameInfo->duration = MKV_TIMECODE_TO_TIMESTAMP(duration, pStreamMkvGenerator->timecodeScale);
            pEncodedFrameInfo->dataOffset = (UINT16) dataOffset;
            pEncodedFrameInfo->streamState = streamState;
        }
    }

    LEAVES();
    return retStatus;
}