Commit 3137f7f1 for openh264
commit 3137f7f18fd3a166ed7f282cb917cd9a83ecc6ee
Author: BenzhengZhang <140143892+BenzhengZhang@users.noreply.github.com>
Date: Fri Aug 28 16:09:30 2026 +0800
decoder: validate EC copy source/destination compatibility (#3990)
Co-authored-by: benzzhan <benzzhan@cisco.com>
diff --git a/codec/decoder/core/src/error_concealment.cpp b/codec/decoder/core/src/error_concealment.cpp
index 606c49ad..768f45b5 100644
--- a/codec/decoder/core/src/error_concealment.cpp
+++ b/codec/decoder/core/src/error_concealment.cpp
@@ -39,6 +39,40 @@
#include "cpu_core.h"
namespace WelsDec {
+
+static inline int32_t GetEcPicWidthInPixel (const PWelsDecoderContext pCtx, const PPicture pPic) {
+ if (pPic != NULL && pPic->iWidthInPixel > 0) {
+ return pPic->iWidthInPixel;
+ }
+ return pCtx->pSps->iMbWidth << 4;
+}
+
+static inline int32_t GetEcPicHeightInPixel (const PWelsDecoderContext pCtx, const PPicture pPic) {
+ if (pPic != NULL && pPic->iHeightInPixel > 0) {
+ return pPic->iHeightInPixel;
+ }
+ return pCtx->pSps->iMbHeight << 4;
+}
+
+static inline bool IsEcRefPicCompatible (const PWelsDecoderContext pCtx, const PPicture pDstPic, const PPicture pSrcPic) {
+ if (pDstPic == NULL || pSrcPic == NULL) {
+ return false;
+ }
+
+ const int32_t iDstWidthInPixel = GetEcPicWidthInPixel (pCtx, pDstPic);
+ const int32_t iDstHeightInPixel = GetEcPicHeightInPixel (pCtx, pDstPic);
+ const int32_t iSrcWidthInPixel = GetEcPicWidthInPixel (pCtx, pSrcPic);
+ const int32_t iSrcHeightInPixel = GetEcPicHeightInPixel (pCtx, pSrcPic);
+
+ if (iDstWidthInPixel != iSrcWidthInPixel || iDstHeightInPixel != iSrcHeightInPixel) {
+ return false;
+ }
+
+ return pSrcPic->iLinesize[0] >= pDstPic->iLinesize[0]
+ && pSrcPic->iLinesize[1] >= pDstPic->iLinesize[1]
+ && pSrcPic->iLinesize[2] >= pDstPic->iLinesize[2];
+}
+
//Init
void InitErrorCon (PWelsDecoderContext pCtx) {
if ((pCtx->pParam->eEcActiveIdc == ERROR_CON_SLICE_COPY)
@@ -91,22 +125,25 @@ void InitErrorCon (PWelsDecoderContext pCtx) {
void DoErrorConFrameCopy (PWelsDecoderContext pCtx) {
PPicture pDstPic = pCtx->pDec;
PPicture pSrcPic = pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb;
- uint32_t uiHeightInPixelY = (pCtx->pSps->iMbHeight) << 4;
+ int32_t iHeightInPixelY = GetEcPicHeightInPixel (pCtx, pDstPic);
int32_t iStrideY = pDstPic->iLinesize[0];
int32_t iStrideUV = pDstPic->iLinesize[1];
pCtx->pDec->iMbEcedNum = pCtx->pSps->iMbWidth * pCtx->pSps->iMbHeight;
if ((pCtx->pParam->eEcActiveIdc == ERROR_CON_FRAME_COPY) && (pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag))
pSrcPic = NULL; //no cross IDR method, should fill in data instead of copy
+ if (!IsEcRefPicCompatible (pCtx, pDstPic, pSrcPic)) {
+ pSrcPic = NULL;
+ }
if (pSrcPic == NULL) { //no ref pic, assign specific data to picture
- memset (pDstPic->pData[0], 128, uiHeightInPixelY * iStrideY);
- memset (pDstPic->pData[1], 128, (uiHeightInPixelY >> 1) * iStrideUV);
- memset (pDstPic->pData[2], 128, (uiHeightInPixelY >> 1) * iStrideUV);
+ memset (pDstPic->pData[0], 128, iHeightInPixelY * iStrideY);
+ memset (pDstPic->pData[1], 128, (iHeightInPixelY >> 1) * iStrideUV);
+ memset (pDstPic->pData[2], 128, (iHeightInPixelY >> 1) * iStrideUV);
} else if (pSrcPic == pDstPic) {
WelsLog (& (pCtx->sLogCtx), WELS_LOG_WARNING, "DoErrorConFrameCopy()::EC memcpy overlap.");
} else { //has ref pic here
- memcpy (pDstPic->pData[0], pSrcPic->pData[0], uiHeightInPixelY * iStrideY);
- memcpy (pDstPic->pData[1], pSrcPic->pData[1], (uiHeightInPixelY >> 1) * iStrideUV);
- memcpy (pDstPic->pData[2], pSrcPic->pData[2], (uiHeightInPixelY >> 1) * iStrideUV);
+ memcpy (pDstPic->pData[0], pSrcPic->pData[0], iHeightInPixelY * iStrideY);
+ memcpy (pDstPic->pData[1], pSrcPic->pData[1], (iHeightInPixelY >> 1) * iStrideUV);
+ memcpy (pDstPic->pData[2], pSrcPic->pData[2], (iHeightInPixelY >> 1) * iStrideUV);
}
}
@@ -119,6 +156,9 @@ void DoErrorConSliceCopy (PWelsDecoderContext pCtx) {
PPicture pSrcPic = pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb;
if ((pCtx->pParam->eEcActiveIdc == ERROR_CON_SLICE_COPY) && (pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag))
pSrcPic = NULL; //no cross IDR method, should fill in data instead of copy
+ if (!IsEcRefPicCompatible (pCtx, pDstPic, pSrcPic)) {
+ pSrcPic = NULL;
+ }
//uint8_t *pDstData[3], *pSrcData[3];
bool* pMbCorrectlyDecodedFlag = pCtx->pCurDqLayer->pMbCorrectlyDecodedFlag;
diff --git a/test/decoder/DecUT_ErrorConcealment.cpp b/test/decoder/DecUT_ErrorConcealment.cpp
index 060bd60f..72d1d7b2 100644
--- a/test/decoder/DecUT_ErrorConcealment.cpp
+++ b/test/decoder/DecUT_ErrorConcealment.cpp
@@ -78,6 +78,8 @@ int32_t InitAndAllocInputData (PECInputCtx& pECCtx) {
pECCtx->sWelsPic.iLinesize[0] = pECCtx->sAncPic.iLinesize[0] = pECCtx->sSrcPic.iLinesize[0] = pECCtx->iLinesize[0];
pECCtx->sWelsPic.iLinesize[1] = pECCtx->sAncPic.iLinesize[1] = pECCtx->sSrcPic.iLinesize[1] = pECCtx->iLinesize[1];
pECCtx->sWelsPic.iLinesize[2] = pECCtx->sAncPic.iLinesize[2] = pECCtx->sSrcPic.iLinesize[2] = pECCtx->iLinesize[2];
+ pECCtx->sWelsPic.iWidthInPixel = pECCtx->sAncPic.iWidthInPixel = pECCtx->sSrcPic.iWidthInPixel = pECCtx->iMbWidth << 4;
+ pECCtx->sWelsPic.iHeightInPixel = pECCtx->sAncPic.iHeightInPixel = pECCtx->sSrcPic.iHeightInPixel = pECCtx->iMbHeight << 4;
pECCtx->pMbCorrectlyDecodedFlag = (bool*) WelsMallocz (pECCtx->iMbWidth * pECCtx->iMbHeight * sizeof (bool),
"pECCtx->pMbCorrectlyDecodedFlag");
@@ -228,6 +230,23 @@ bool ComparePictureDataI420 (uint8_t* pSrcData, uint8_t* pDstData, const uint32_
return bSame;
}
+bool IsPictureFilledWithValueI420 (uint8_t* pData, const uint32_t kiStride, const int32_t kiHeight, const uint8_t kuiValue) {
+ const int32_t iLumaSize = kiStride * kiHeight;
+ const int32_t iChromaSize = (kiStride >> 1) * (kiHeight >> 1);
+
+ for (int32_t i = 0; i < iLumaSize; ++i) {
+ if (pData[i] != kuiValue) {
+ return false;
+ }
+ }
+ for (int32_t i = 0; i < iChromaSize; ++i) {
+ if (pData[iLumaSize + i] != kuiValue || pData[iLumaSize + iChromaSize + i] != kuiValue) {
+ return false;
+ }
+ }
+ return true;
+}
+
//TEST cases followed
TEST (ErrorConTest, DoErrorConFrameCopy) {
bool bOK = true;
@@ -295,3 +314,52 @@ TEST (ErrorConTest, DoErrorConSliceCopy) {
FreeInputData (pECCtx);
}
+
+TEST (ErrorConTest, DoErrorConFrameCopyResolutionMismatchFallsBackToFill) {
+ PECInputCtx pECCtx = NULL;
+ if (InitAndAllocInputData (pECCtx)) {
+ FreeInputData (pECCtx);
+ return;
+ }
+
+ pECCtx->pCtx->pParam->eEcActiveIdc = ERROR_CON_FRAME_COPY_CROSS_IDR;
+ InitECCopyData (pECCtx);
+ pECCtx->pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag = 0;
+ pECCtx->pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb = &pECCtx->sSrcPic;
+
+ pECCtx->sSrcPic.iWidthInPixel -= 16;
+ memset (pECCtx->sWelsPic.pData[0], 7, pECCtx->iMbWidth * pECCtx->iMbHeight * 256 * 3 / 2);
+
+ DoErrorConFrameCopy (pECCtx->pCtx);
+
+ EXPECT_TRUE (IsPictureFilledWithValueI420 (pECCtx->sWelsPic.pData[0],
+ pECCtx->sWelsPic.iLinesize[0],
+ pECCtx->sWelsPic.iHeightInPixel,
+ 128));
+ FreeInputData (pECCtx);
+}
+
+TEST (ErrorConTest, DoErrorConSliceCopyResolutionMismatchFallsBackToFill) {
+ PECInputCtx pECCtx = NULL;
+ if (InitAndAllocInputData (pECCtx)) {
+ FreeInputData (pECCtx);
+ return;
+ }
+
+ pECCtx->pCtx->pParam->eEcActiveIdc = ERROR_CON_SLICE_COPY_CROSS_IDR;
+ InitECCopyData (pECCtx);
+ pECCtx->pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag = 0;
+ pECCtx->pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb = &pECCtx->sSrcPic;
+
+ memset (pECCtx->pMbCorrectlyDecodedFlag, 0, pECCtx->iMbWidth * pECCtx->iMbHeight * sizeof (bool));
+ pECCtx->sSrcPic.iHeightInPixel -= 16;
+ memset (pECCtx->sWelsPic.pData[0], 7, pECCtx->iMbWidth * pECCtx->iMbHeight * 256 * 3 / 2);
+
+ DoErrorConSliceCopy (pECCtx->pCtx);
+
+ EXPECT_TRUE (IsPictureFilledWithValueI420 (pECCtx->sWelsPic.pData[0],
+ pECCtx->sWelsPic.iLinesize[0],
+ pECCtx->sWelsPic.iHeightInPixel,
+ 128));
+ FreeInputData (pECCtx);
+}