From 477ad58407e739374943dbf9c8ea0118be5dfca2 Mon Sep 17 00:00:00 2001 From: Marek Olejnik Date: Wed, 3 Sep 2025 01:14:17 +0200 Subject: [PATCH] Enable access to reconstructed picture output in the encoder Access to the reconstructed frame from the H.264 encoder is useful in scenarios where the application layer needs to perform further processing or analysis on the encoded video. By exposing the reconstructed picture, the encoder facilitates integration with advanced video processing workflows and enables more flexible application-level control. --- codec/api/wels/codec_app_def.h | 31 +++++++++------ codec/encoder/core/src/encoder_ext.cpp | 55 ++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/codec/api/wels/codec_app_def.h b/codec/api/wels/codec_app_def.h index 823d38d9ac..be2328a893 100644 --- a/codec/api/wels/codec_app_def.h +++ b/codec/api/wels/codec_app_def.h @@ -641,18 +641,6 @@ typedef struct { float rPsnr[3]; ///< PSNR values for Y/U/V } SLayerBSInfo, *PLayerBSInfo; -/** -* @brief Frame bit stream info -*/ -typedef struct { - int iLayerNum; - SLayerBSInfo sLayerInfo[MAX_LAYER_NUM_OF_FRAME]; - - EVideoFrameType eFrameType; - int iFrameSizeInBytes; - long long uiTimeStamp; -} SFrameBSInfo, *PFrameBSInfo; - /** * @brief Structure for source picture */ @@ -668,6 +656,25 @@ typedef struct Source_Picture_s { bool bPsnrV; ///< get V PSNR for this frame } SSourcePicture; +/** +* @brief Structure for reconstructed picture +*/ +typedef SSourcePicture SReconPicture; + +/** +* @brief Frame bit stream info +*/ +typedef struct { + int iLayerNum; + SLayerBSInfo sLayerInfo[MAX_LAYER_NUM_OF_FRAME]; + + EVideoFrameType eFrameType; + int iFrameSizeInBytes; + long long uiTimeStamp; + SReconPicture *pReconPic; ///< pointer to the reconstructed picture + bool bHaveRecon; ///< whether have reconstructed picture data +} SFrameBSInfo, *PFrameBSInfo; + /** * @brief Structure for bit rate info */ diff --git a/codec/encoder/core/src/encoder_ext.cpp b/codec/encoder/core/src/encoder_ext.cpp index 71ec0d1a0d..7021d82d9e 100644 --- a/codec/encoder/core/src/encoder_ext.cpp +++ b/codec/encoder/core/src/encoder_ext.cpp @@ -3907,6 +3907,61 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour WelsLog (pLogCtx, WELS_LOG_WARNING, "WelsEncoderEncodeExt()MinCr Checking,codec bitstream size is larger than Level limitation"); } + + if (iSpatialNum == 1 && NULL != pFbi->pReconPic + && (NULL != pFbi->pReconPic->pData[0] + || NULL != pFbi->pReconPic->pData[1] + || NULL != pFbi->pReconPic->pData[2])) { + SReconPicture* pReconPic = pFbi->pReconPic; + unsigned char* pReconData[4] = {pReconPic->pData[0], pReconPic->pData[1], + pReconPic->pData[2], pReconPic->pData[3]}; + SWelsSPS* pSpsTmp = pCtx->pCurDqLayer->sLayerInfo.pSpsP; + bool bFrameCroppingFlag = pSpsTmp->bFrameCroppingFlag; + SCropOffset* pFrameCrop = &pSpsTmp->sFrameCrop; + + const int32_t kiStrideY = fsnr->iLineSize[0]; + const int32_t kiLumaWidth = bFrameCroppingFlag ? (fsnr->iWidthInPixel - ((pFrameCrop->iCropLeft + + pFrameCrop->iCropRight) << 1)) : fsnr->iWidthInPixel; + const int32_t kiLumaHeight = bFrameCroppingFlag ? (fsnr->iHeightInPixel - ((pFrameCrop->iCropTop + + pFrameCrop->iCropBottom) << 1)) : fsnr->iHeightInPixel; + const int32_t kiChromaWidth = kiLumaWidth >> 1; + const int32_t kiChromaHeight = kiLumaHeight >> 1; + + pReconPic->iColorFormat = videoFormatI420; + pReconPic->iPicWidth = kiLumaWidth; + pReconPic->iPicHeight = kiLumaHeight; + pReconPic->iStride[0] = kiStrideY; + pReconPic->uiTimeStamp = pSrcPic->uiTimeStamp; + + // Copy Y plane + if (NULL != pReconData[0]) { + unsigned char* pSrcY = bFrameCroppingFlag ? (fsnr->pData[0] + kiStrideY * (pFrameCrop->iCropTop << 1) + + (pFrameCrop->iCropLeft << 1)) : fsnr->pData[0]; + for (int32_t j = 0; j < kiLumaHeight; ++j) { + memcpy (pReconData[0] + j * kiLumaWidth, pSrcY + j * kiStrideY, kiLumaWidth); + } + } + + // Copy U and V planes + for (int i = 1; i < 3; ++i) { + if (NULL == pReconData[i]) + continue; + const int32_t kiStrideUV = fsnr->iLineSize[i]; + pReconPic->iStride[i] = kiStrideUV; + unsigned char* pSrcUV = bFrameCroppingFlag ? (fsnr->pData[i] + kiStrideUV * pFrameCrop->iCropTop + + pFrameCrop->iCropLeft) + : fsnr->pData[i]; + for (int32_t j = 0; j < kiChromaHeight; ++j) { + memcpy (pReconData[i] + j * kiChromaWidth, pSrcUV + j * kiStrideUV, kiChromaWidth); + } + } + + pFbi->bHaveRecon = true; + } else { + pFbi->bHaveRecon = false; + } + + #ifdef ENABLE_FRAME_DUMP { DumpDependencyRec (fsnr, &pSvcParam->sDependencyLayers[iCurDid].sRecFileName[0], iCurDid,