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;
}