diff --git a/codec/api/svc/codec_app_def.h b/codec/api/svc/codec_app_def.h index 3889831b..1c2cffcf 100644 --- a/codec/api/svc/codec_app_def.h +++ b/codec/api/svc/codec_app_def.h @@ -484,14 +484,14 @@ typedef struct TagVideoDecoderStatistics { unsigned int uiWidth; // the width of encode/decode frame unsigned int uiHeight; // the height of encode/decode frame float fAverageFrameSpeedInMs; // Average_Decoding_Time - unsigned int uiDecodedFrameCount; // number of frames unsigned int uiResolutionChangeTimes; // uiResolutionChangeTimes - unsigned int - uiAvgEcRatio; // when EC is on, the average ratio of correct or EC areas, can be an indicator of reconstruction quality - unsigned int uiIDRReqNum; // number of actual IDR request - unsigned int uiLTRReqNum; // number of actual LTR request unsigned int uiIDRRecvNum; // number of actual IDR received + //EC on related + unsigned int uiAvgEcRatio; // when EC is on, the average ratio of correct or EC areas, can be an indicator of reconstruction quality + unsigned int uiEcIDRNum; // number of actual unintegrity IDR or not received but eced + unsigned int uiEcFrameNum; // + unsigned int uiIDRLostNum;//Decoder detect out the number of lost IDR lost } SDecoderStatistics; // in building, coming soon #endif//WELS_VIDEO_CODEC_APPLICATION_DEFINITION_H__ diff --git a/codec/decoder/core/inc/decoder_context.h b/codec/decoder/core/inc/decoder_context.h index f802a5c8..195f7147 100644 --- a/codec/decoder/core/inc/decoder_context.h +++ b/codec/decoder/core/inc/decoder_context.h @@ -372,6 +372,8 @@ SWelsCabacCtx sWelsCabacContexts[4][WELS_QP_MAX + 1][WELS_CONTEXT_COUNT]; bool bCabacInited; SWelsCabacCtx pCabacCtx[WELS_CONTEXT_COUNT]; PWelsCabacDecEngine pCabacDecEngine; +double dDecTime; +SDecoderStatistics sDecoderStatistics;// For real time debugging } SWelsDecoderContext, *PWelsDecoderContext; static inline void ResetActiveSPSForEachLayer (PWelsDecoderContext pCtx) { diff --git a/codec/decoder/core/src/decoder_core.cpp b/codec/decoder/core/src/decoder_core.cpp index 58b4d2b7..1c99e0e7 100644 --- a/codec/decoder/core/src/decoder_core.cpp +++ b/codec/decoder/core/src/decoder_core.cpp @@ -107,6 +107,14 @@ static inline int32_t DecodeFrameConstruction (PWelsDecoderContext pCtx, uint8_t pDstInfo->iBufferStatus = (int32_t) (bFrameCompleteFlag && pPic->bIsComplete); // When EC disable, ECed picture not output + + if ((pDstInfo->iBufferStatus == 1) && (pCurDq->sLayerInfo.sNalHeaderExt.bIdrFlag)) { + if (pPic->bIsComplete) + pCtx->sDecoderStatistics.uiIDRRecvNum++; + else + pCtx->sDecoderStatistics.uiEcIDRNum++; + } + if (pDstInfo->iBufferStatus == 0) { if (!bFrameCompleteFlag) pCtx->iErrorCode |= dsBitstreamError; @@ -974,6 +982,8 @@ int32_t UpdateAccessUnit (PWelsDecoderContext pCtx) { } if (uiActualIdx == pCurAu->uiActualUnitsNum) { // no found IDR nal within incoming AU, need exit to avoid mosaic issue, 11/19/2009 + + pCtx->sDecoderStatistics.uiIDRLostNum++; WelsLog (& (pCtx->sLogCtx), WELS_LOG_WARNING, "UpdateAccessUnit():::::Key frame lost.....CAN NOT find IDR from current AU."); pCtx->iErrorCode |= dsRefLost; diff --git a/codec/decoder/core/src/error_concealment.cpp b/codec/decoder/core/src/error_concealment.cpp index 78f29873..09f795ed 100644 --- a/codec/decoder/core/src/error_concealment.cpp +++ b/codec/decoder/core/src/error_concealment.cpp @@ -98,9 +98,10 @@ void DoErrorConSliceCopy (PWelsDecoderContext pCtx) { PPicture pDstPic = pCtx->pDec; PPicture pSrcPic = pCtx->pPreviousDecodedPictureInDpb; + int32_t iMbNum = pCtx->pSps->iMbWidth * pCtx->pSps->iMbHeight; //uint8_t *pDstData[3], *pSrcData[3]; bool* pMbCorrectlyDecodedFlag = pCtx->pCurDqLayer->pMbCorrectlyDecodedFlag; - + int32_t iMbEcedNum = 0; //Do slice copy late int32_t iMbXyIndex; uint8_t* pSrcData, *pDstData; @@ -110,6 +111,7 @@ void DoErrorConSliceCopy (PWelsDecoderContext pCtx) { for (int32_t iMbX = 0; iMbX < iMbWidth; ++iMbX) { iMbXyIndex = iMbY * iMbWidth + iMbX; if (!pMbCorrectlyDecodedFlag[iMbXyIndex]) { + iMbEcedNum++; if (pSrcPic != NULL) { iSrcStride = pSrcPic->iLinesize[0]; //Y component @@ -147,6 +149,9 @@ void DoErrorConSliceCopy (PWelsDecoderContext pCtx) { } //!pMbCorrectlyDecodedFlag[iMbXyIndex] } //iMbX } //iMbY + + pCtx->sDecoderStatistics.uiAvgEcRatio = (pCtx->sDecoderStatistics.uiAvgEcRatio * pCtx->sDecoderStatistics.uiEcFrameNum) + + ((iMbEcedNum * 100) / iMbNum) ; } diff --git a/codec/decoder/plus/src/welsDecoderExt.cpp b/codec/decoder/plus/src/welsDecoderExt.cpp index 230c1520..3ba0931a 100644 --- a/codec/decoder/plus/src/welsDecoderExt.cpp +++ b/codec/decoder/plus/src/welsDecoderExt.cpp @@ -53,6 +53,7 @@ #include "decoder_core.h" #include "error_concealment.h" +#include "measure_time.h" extern "C" { #include "decoder_core.h" #include "manage_dec_ref.h" @@ -301,8 +302,13 @@ long CWelsDecoder::SetOption (DECODER_OPTION eOptID, void* pOption) { m_pWelsTrace->SetTraceCallbackContext (ctx); } return cmResultSuccess; + } else if (eOptID == DECODER_OPTION_GET_STATISTICS) { + WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING, + "CWelsDecoder::SetOption():DECODER_OPTION_GET_STATISTICS: this option is get-only!"); + return cmInitParaError; } + return cmInitParaError; } @@ -358,6 +364,16 @@ long CWelsDecoder::GetOption (DECODER_OPTION eOptID, void* pOption) { iVal = (int) m_pDecContext->eErrorConMethod; * ((int*)pOption) = iVal; return cmResultSuccess; + } else if (DECODER_OPTION_GET_STATISTICS == eOptID) { // get decoder statistics info for real time debugging + SDecoderStatistics* pDecoderStatistics = (static_cast (pOption)); + + memcpy (pDecoderStatistics, &m_pDecContext->sDecoderStatistics, sizeof (SDecoderStatistics)); + + pDecoderStatistics->fAverageFrameSpeedInMs = (m_pDecContext->dDecTime) / + (m_pDecContext->sDecoderStatistics.uiDecodedFrameCount); + memset (&m_pDecContext->sDecoderStatistics, 0, sizeof (SDecoderStatistics)); + m_pDecContext->dDecTime = 0; + return cmResultSuccess; } return cmInitParaError; @@ -389,6 +405,8 @@ DECODING_STATE CWelsDecoder::DecodeFrame2 (const unsigned char* kpSrc, m_pDecContext->bInstantDecFlag = true; } + int64_t iStart, iEnd; + iStart = WelsTime(); ppDst[0] = ppDst[1] = ppDst[2] = NULL; m_pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding. m_pDecContext->iFeedbackVclNalInAu = FEEDBACK_UNKNOWN_NAL; //initialize @@ -439,11 +457,43 @@ DECODING_STATE CWelsDecoder::DecodeFrame2 (const unsigned char* kpSrc, if ((m_pDecContext->eErrorConMethod != ERROR_CON_DISABLE) && (pDstInfo->iBufferStatus == 1)) { //TODO after dec status updated m_pDecContext->iErrorCode |= dsDataErrorConcealed; + if (m_pDecContext->eErrorConMethod == ERROR_CON_FRAME_COPY) + + m_pDecContext->sDecoderStatistics.uiAvgEcRatio = (m_pDecContext->sDecoderStatistics.uiAvgEcRatio * + m_pDecContext->sDecoderStatistics.uiEcFrameNum) + 100; + + // + if ((m_pDecContext->sDecoderStatistics.uiWidth != pDstInfo->UsrData.sSystemBuffer.iWidth) + || (m_pDecContext->sDecoderStatistics.uiHeight != pDstInfo->UsrData.sSystemBuffer.iHeight)) { + m_pDecContext->sDecoderStatistics.uiResolutionChangeTimes++; + m_pDecContext->sDecoderStatistics.uiWidth = pDstInfo->UsrData.sSystemBuffer.iWidth; + m_pDecContext->sDecoderStatistics.uiHeight = pDstInfo->UsrData.sSystemBuffer.iHeight; + + } + m_pDecContext->sDecoderStatistics.uiDecodedFrameCount++; + m_pDecContext->sDecoderStatistics.uiEcFrameNum++; + m_pDecContext->sDecoderStatistics.uiAvgEcRatio = m_pDecContext->sDecoderStatistics.uiAvgEcRatio / + m_pDecContext->sDecoderStatistics.uiEcFrameNum; } + iEnd = WelsTime(); + m_pDecContext->dDecTime += (iEnd - iStart) / 1e3; return (DECODING_STATE) m_pDecContext->iErrorCode; } // else Error free, the current codec works well + if (pDstInfo->iBufferStatus == 1) { + + if ((m_pDecContext->sDecoderStatistics.uiWidth != pDstInfo->UsrData.sSystemBuffer.iWidth) + || (m_pDecContext->sDecoderStatistics.uiHeight != pDstInfo->UsrData.sSystemBuffer.iHeight)) { + m_pDecContext->sDecoderStatistics.uiResolutionChangeTimes++; + m_pDecContext->sDecoderStatistics.uiWidth = pDstInfo->UsrData.sSystemBuffer.iWidth; + m_pDecContext->sDecoderStatistics.uiHeight = pDstInfo->UsrData.sSystemBuffer.iHeight; + + } + m_pDecContext->sDecoderStatistics.uiDecodedFrameCount++; + } + iEnd = WelsTime(); + m_pDecContext->dDecTime += (iEnd - iStart) / 1e3; return dsErrorFree; } diff --git a/res/BA_MW_D_IDR_LOST.264 b/res/BA_MW_D_IDR_LOST.264 new file mode 100644 index 00000000..1967573e Binary files /dev/null and b/res/BA_MW_D_IDR_LOST.264 differ diff --git a/res/BA_MW_D_P_LOST.264 b/res/BA_MW_D_P_LOST.264 new file mode 100644 index 00000000..2d9642db Binary files /dev/null and b/res/BA_MW_D_P_LOST.264 differ diff --git a/res/Error_I_P.264 b/res/Error_I_P.264 new file mode 100644 index 00000000..fd3d2861 Binary files /dev/null and b/res/Error_I_P.264 differ diff --git a/test/decoder/DecUT_DecExt.cpp b/test/decoder/DecUT_DecExt.cpp index 12bd5fb7..e845c97a 100644 --- a/test/decoder/DecUT_DecExt.cpp +++ b/test/decoder/DecUT_DecExt.cpp @@ -30,6 +30,8 @@ class DecoderInterfaceTest : public ::testing::Test { void Uninit(); //Mock input data for test void MockPacketType (const EWelsNalUnitType eNalUnitType, const int iPacketLength); + //Decoder real bitstream + void DecoderBs (const char* sFileName); //Test Initialize/Uninitialize void TestInitUninit(); //DECODER_OPTION_DATAFORMAT @@ -56,6 +58,8 @@ class DecoderInterfaceTest : public ::testing::Test { void TestTraceCallback(); //DECODER_OPTION_TRACE_CALLBACK_CONTEXT void TestTraceCallbackContext(); + //DECODER_OPTION_GET_DECODER_STATICTIS + void TestGetDecStatistics(); //Do whole tests here void DecoderInterfaceAll(); @@ -104,6 +108,58 @@ void DecoderInterfaceTest::Uninit() { m_iBufLength = 0; } +void DecoderInterfaceTest::DecoderBs (const char* sFileName) { + + uint8_t* pBuf = NULL; + int32_t iBufPos = 0; + int32_t iFileSize; + int32_t i = 0; + int32_t iSliceSize; + int32_t iSliceIndex = 0; + int32_t iEndOfStreamFlag = 0; + FILE* pH264File; + uint8_t uiStartCode[4] = {0, 0, 0, 1}; + +#if defined(ANDROID_NDK) + std::string filename = std::string ("/sdcard/") + sFileName; + ASSERT_TRUE (pH264File = fopen (filename.c_str())); +#else + ASSERT_TRUE (pH264File = fopen (sFileName, "rb")); +#endif + fseek (pH264File, 0L, SEEK_END); + iFileSize = (int32_t) ftell (pH264File); + fseek (pH264File, 0L, SEEK_SET); + pBuf = new uint8_t[iFileSize + 4]; + fread (pBuf, 1, iFileSize, pH264File); + memcpy (pBuf + iFileSize, &uiStartCode[0], 4); //confirmed_safe_unsafe_usage + while (true) { + if (iBufPos >= iFileSize) { + iEndOfStreamFlag = true; + if (iEndOfStreamFlag) + m_pDec->SetOption (DECODER_OPTION_END_OF_STREAM, (void*)&iEndOfStreamFlag); + break; + } + for (i = 0; i < iFileSize; i++) { + if ((pBuf[iBufPos + i] == 0 && pBuf[iBufPos + i + 1] == 0 && pBuf[iBufPos + i + 2] == 0 && pBuf[iBufPos + i + 3] == 1 + && i > 0)) { + break; + } + } + iSliceSize = i; + m_pDec->DecodeFrame2 (pBuf + iBufPos, iSliceSize, m_pData, &m_sBufferInfo); + m_pDec->DecodeFrame2 (NULL, 0, m_pData, &m_sBufferInfo); + iBufPos += iSliceSize; + ++ iSliceIndex; + } + + fclose (pH264File); + if (pBuf) { + delete[] pBuf; + pBuf = NULL; + } + + +} //Mock input data for test void DecoderInterfaceTest::MockPacketType (const EWelsNalUnitType eNalUnitType, const int iPacketLength) { switch (eNalUnitType) { @@ -340,7 +396,89 @@ void DecoderInterfaceTest::TestTraceCallbackContext() { //TODO } +//DECODER_OPTION_GET_STATISTICS +void DecoderInterfaceTest::TestGetDecStatistics() { + CM_RETURN eRet; + SDecoderStatistics sDecStatic; + int32_t iError = 0; + Init(); + // setoption not support, + eRet = (CM_RETURN)m_pDec->SetOption (DECODER_OPTION_GET_STATISTICS, NULL); + EXPECT_EQ (eRet, cmInitParaError); + //EC on UT + iError = 2; + m_pDec->SetOption (DECODER_OPTION_ERROR_CON_IDC, &iError); + //Decoder error bs + DecoderBs ("res/Error_I_P.264"); + m_pDec->GetOption (DECODER_OPTION_GET_STATISTICS, &sDecStatic); + EXPECT_EQ (57, sDecStatic.uiAvgEcRatio); + EXPECT_EQ (5, sDecStatic.uiDecodedFrameCount); + EXPECT_EQ (288, sDecStatic.uiHeight); + EXPECT_EQ (1, sDecStatic.uiIDRRecvNum); + EXPECT_EQ (3, sDecStatic.uiResolutionChangeTimes); + EXPECT_EQ (352, sDecStatic.uiWidth); + EXPECT_EQ (4, sDecStatic.uiEcFrameNum); + EXPECT_EQ (2, sDecStatic.uiEcIDRNum); + EXPECT_EQ (0, sDecStatic.uiIDRLostNum); + Uninit(); + + //Decoder error bs when the first IDR lost + Init(); + iError = 2; + m_pDec->SetOption (DECODER_OPTION_ERROR_CON_IDC, &iError); + DecoderBs ("res/BA_MW_D_IDR_LOST.264"); + m_pDec->GetOption (DECODER_OPTION_GET_STATISTICS, &sDecStatic); + EXPECT_EQ (0, sDecStatic.uiAvgEcRatio); + EXPECT_EQ (97, sDecStatic.uiDecodedFrameCount); + EXPECT_EQ (144, sDecStatic.uiHeight); + EXPECT_EQ (3, sDecStatic.uiIDRRecvNum); + EXPECT_EQ (0, sDecStatic.uiEcIDRNum); + EXPECT_EQ (1, sDecStatic.uiResolutionChangeTimes); + EXPECT_EQ (176, sDecStatic.uiWidth); + EXPECT_EQ (27, sDecStatic.uiEcFrameNum); + EXPECT_EQ (1, sDecStatic.uiIDRLostNum); + Uninit(); + + //ecoder error bs when the first P lost + Init(); + iError = 2; + m_pDec->SetOption (DECODER_OPTION_ERROR_CON_IDC, &iError); + + DecoderBs ("res/BA_MW_D_P_LOST.264"); + + m_pDec->GetOption (DECODER_OPTION_GET_STATISTICS, &sDecStatic); + EXPECT_EQ (0, sDecStatic.uiAvgEcRatio); + EXPECT_EQ (99, sDecStatic.uiDecodedFrameCount); + EXPECT_EQ (144, sDecStatic.uiHeight); + EXPECT_EQ (4, sDecStatic.uiIDRRecvNum); + EXPECT_EQ (0, sDecStatic.uiEcIDRNum); + EXPECT_EQ (1, sDecStatic.uiResolutionChangeTimes); + EXPECT_EQ (176, sDecStatic.uiWidth); + EXPECT_EQ (28, sDecStatic.uiEcFrameNum); + EXPECT_EQ (0, sDecStatic.uiIDRLostNum); + Uninit(); + //EC enable + + //EC Off UT just correc bitstream + Init(); + iError = 0; + m_pDec->SetOption (DECODER_OPTION_ERROR_CON_IDC, &iError); + DecoderBs ("res/test_vd_1d.264"); + + m_pDec->GetOption (DECODER_OPTION_GET_STATISTICS, &sDecStatic); + + EXPECT_EQ (0, sDecStatic.uiAvgEcRatio); + EXPECT_EQ (9, sDecStatic.uiDecodedFrameCount); + EXPECT_EQ (192, sDecStatic.uiHeight); + EXPECT_EQ (1, sDecStatic.uiIDRRecvNum); + EXPECT_EQ (1, sDecStatic.uiResolutionChangeTimes); + EXPECT_EQ (320, sDecStatic.uiWidth); + EXPECT_EQ (0, sDecStatic.uiEcFrameNum); + EXPECT_EQ (0, sDecStatic.uiIDRLostNum); + Uninit(); + +} //TEST here for whole tests TEST_F (DecoderInterfaceTest, DecoderInterfaceAll) { @@ -370,6 +508,8 @@ TEST_F (DecoderInterfaceTest, DecoderInterfaceAll) { TestTraceCallback(); //DECODER_OPTION_TRACE_CALLBACK_CONTEXT TestTraceCallbackContext(); + //DECODER_OPTION_GET_STATISTICS + TestGetDecStatistics(); }