| /*! | |
| * \copy | |
| * Copyright (c) 2009-2013, Cisco Systems | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * | |
| * * Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * | |
| * * Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in | |
| * the documentation and/or other materials provided with the | |
| * distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| * POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * | |
| * welsDecoderExt.cpp | |
| * | |
| * Abstract | |
| * Cisco OpenH264 decoder extension utilization | |
| * | |
| * History | |
| * 3/12/2009 Created | |
| * | |
| * | |
| ************************************************************************/ | |
| //#include <assert.h> | |
| #include "welsDecoderExt.h" | |
| #include "welsCodecTrace.h" | |
| #include "codec_def.h" | |
| #include "typedefs.h" | |
| #include "memory_align.h" | |
| #include "utils.h" | |
| #include "version.h" | |
| //#include "macros.h" | |
| #include "decoder.h" | |
| #include "decoder_core.h" | |
| #include "manage_dec_ref.h" | |
| #include "error_concealment.h" | |
| #include "measure_time.h" | |
| extern "C" { | |
| #include "decoder_core.h" | |
| #include "manage_dec_ref.h" | |
| } | |
| #include "error_code.h" | |
| #include "crt_util_safe_x.h" // Safe CRT routines like util for cross platforms | |
| #include <time.h> | |
| #if defined(_WIN32) /*&& defined(_DEBUG)*/ | |
| #include <windows.h> | |
| #include <stdio.h> | |
| #include <stdarg.h> | |
| #include <sys/types.h> | |
| #include <malloc.h> | |
| #else | |
| #include <sys/time.h> | |
| #endif | |
| namespace WelsDec { | |
| ////////////////////////////////////////////////////////////////////// | |
| // Construction/Destruction | |
| ////////////////////////////////////////////////////////////////////// | |
| /*************************************************************************** | |
| * Description: | |
| * class CWelsDecoder constructor function, do initialization and | |
| * alloc memory required | |
| * | |
| * Input parameters: none | |
| * | |
| * return: none | |
| ***************************************************************************/ | |
| DECLARE_PROCTHREAD (pThrProcInit, p) { | |
| SWelsDecThreadInfo* sThreadInfo = (SWelsDecThreadInfo*)p; | |
| #if defined(WIN32) | |
| _alloca (WELS_DEC_MAX_THREAD_STACK_SIZE * (sThreadInfo->uiThrNum + 1)); | |
| #endif | |
| return sThreadInfo->pThrProcMain (p); | |
| } | |
| static DECODING_STATE ConstructAccessUnit (CWelsDecoder* pWelsDecoder, PWelsDecoderThreadCTX pThrCtx) { | |
| int iRet = dsErrorFree; | |
| //WelsMutexLock (&pWelsDecoder->m_csDecoder); | |
| if (pThrCtx->pCtx->pLastThreadCtx != NULL) { | |
| PWelsDecoderThreadCTX pLastThreadCtx = (PWelsDecoderThreadCTX) (pThrCtx->pCtx->pLastThreadCtx); | |
| if (WAIT_EVENT (&pLastThreadCtx->sSliceDecodeStart, WELS_DEC_THREAD_WAIT_TIMEOUT_MS) != WELS_DEC_THREAD_WAIT_SIGNALED) { | |
| pThrCtx->pCtx->iErrorCode |= dsRefLost; | |
| return (DECODING_STATE)GENERATE_ERROR_NO (ERR_LEVEL_SLICE_DATA, ERR_INFO_REFERENCE_PIC_LOST); | |
| } | |
| RESET_EVENT (&pLastThreadCtx->sSliceDecodeStart); | |
| } | |
| pThrCtx->pDec = NULL; | |
| if (GetThreadCount (pThrCtx->pCtx) > 1) { | |
| RESET_EVENT (&pThrCtx->sSliceDecodeFinish); | |
| } | |
| iRet |= pWelsDecoder->DecodeFrame2WithCtx (pThrCtx->pCtx, NULL, 0, pThrCtx->ppDst, &pThrCtx->sDstInfo); | |
| //WelsMutexUnlock (&pWelsDecoder->m_csDecoder); | |
| return (DECODING_STATE)iRet; | |
| } | |
| DECLARE_PROCTHREAD (pThrProcFrame, p) { | |
| SWelsDecoderThreadCTX* pThrCtx = (SWelsDecoderThreadCTX*)p; | |
| while (1) { | |
| RELEASE_SEMAPHORE (pThrCtx->sThreadInfo.sIsBusy); | |
| RELEASE_SEMAPHORE (&pThrCtx->sThreadInfo.sIsIdle); | |
| WAIT_SEMAPHORE (&pThrCtx->sThreadInfo.sIsActivated, WELS_DEC_THREAD_WAIT_INFINITE); | |
| if (pThrCtx->sThreadInfo.uiCommand == WELS_DEC_THREAD_COMMAND_RUN) { | |
| CWelsDecoder* pWelsDecoder = (CWelsDecoder*)pThrCtx->threadCtxOwner; | |
| ConstructAccessUnit (pWelsDecoder, pThrCtx); | |
| } else if (pThrCtx->sThreadInfo.uiCommand == WELS_DEC_THREAD_COMMAND_ABORT) { | |
| break; | |
| } | |
| } | |
| return 0; | |
| } | |
| CWelsDecoder::CWelsDecoder (void) | |
| : m_pWelsTrace (NULL), | |
| m_uiDecodeTimeStamp (0), | |
| m_bIsBaseline (false), | |
| m_iCpuCount (1), | |
| m_iThreadCount (0), | |
| m_iCtxCount (1), | |
| m_pPicBuff (NULL), | |
| m_bParamSetsLostFlag (false), | |
| m_bFreezeOutput (false), | |
| m_DecCtxActiveCount (0), | |
| m_pDecThrCtx (NULL), | |
| m_pLastDecThrCtx (NULL), | |
| m_iLastBufferedIdx (0), | |
| m_iStreamSeqNum (0) { | |
| memset (&m_sReoderingStatus, 0, sizeof (m_sReoderingStatus)); | |
| m_sReoderingStatus.iMinPOC = IMinInt32; | |
| for (int32_t i = 0; i < 16; ++i) { | |
| memset (&m_sPictInfoList[i], 0, sizeof (m_sPictInfoList[i])); | |
| m_sPictInfoList[i].iPOC = IMinInt32; | |
| } | |
| #ifdef OUTPUT_BIT_STREAM | |
| char chFileName[1024] = { 0 }; //for .264 | |
| int iBufUsed = 0; | |
| int iBufLeft = 1023; | |
| int iCurUsed; | |
| char chFileNameSize[1024] = { 0 }; //for .len | |
| int iBufUsedSize = 0; | |
| int iBufLeftSize = 1023; | |
| int iCurUsedSize; | |
| #endif//OUTPUT_BIT_STREAM | |
| m_pWelsTrace = new welsCodecTrace(); | |
| if (m_pWelsTrace != NULL) { | |
| m_pWelsTrace->SetCodecInstance (this); | |
| m_pWelsTrace->SetTraceLevel (WELS_LOG_ERROR); | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::CWelsDecoder() entry"); | |
| } | |
| ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, true); | |
| m_iCpuCount = GetCPUCount(); | |
| if (m_iCpuCount > WELS_DEC_MAX_NUM_CPU) { | |
| m_iCpuCount = WELS_DEC_MAX_NUM_CPU; | |
| } | |
| m_pDecThrCtx = new SWelsDecoderThreadCTX[m_iCtxCount]; | |
| memset (m_pDecThrCtx, 0, sizeof (SWelsDecoderThreadCTX)*m_iCtxCount); | |
| for (int32_t i = 0; i < WELS_DEC_MAX_NUM_CPU; ++i) { | |
| m_pDecThrCtxActive[i] = NULL; | |
| } | |
| #ifdef OUTPUT_BIT_STREAM | |
| SWelsTime sCurTime; | |
| WelsGetTimeOfDay (&sCurTime); | |
| iCurUsed = WelsSnprintf (chFileName, iBufLeft, "bs_0x%p_", (void*)this); | |
| iCurUsedSize = WelsSnprintf (chFileNameSize, iBufLeftSize, "size_0x%p_", (void*)this); | |
| iBufUsed += iCurUsed; | |
| iBufLeft -= iCurUsed; | |
| if (iBufLeft > 0) { | |
| iCurUsed = WelsStrftime (&chFileName[iBufUsed], iBufLeft, "%y%m%d%H%M%S", &sCurTime); | |
| iBufUsed += iCurUsed; | |
| iBufLeft -= iCurUsed; | |
| } | |
| iBufUsedSize += iCurUsedSize; | |
| iBufLeftSize -= iCurUsedSize; | |
| if (iBufLeftSize > 0) { | |
| iCurUsedSize = WelsStrftime (&chFileNameSize[iBufUsedSize], iBufLeftSize, "%y%m%d%H%M%S", &sCurTime); | |
| iBufUsedSize += iCurUsedSize; | |
| iBufLeftSize -= iCurUsedSize; | |
| } | |
| if (iBufLeft > 0) { | |
| iCurUsed = WelsSnprintf (&chFileName[iBufUsed], iBufLeft, ".%03.3u.264", WelsGetMillisecond (&sCurTime)); | |
| iBufUsed += iCurUsed; | |
| iBufLeft -= iCurUsed; | |
| } | |
| if (iBufLeftSize > 0) { | |
| iCurUsedSize = WelsSnprintf (&chFileNameSize[iBufUsedSize], iBufLeftSize, ".%03.3u.len", | |
| WelsGetMillisecond (&sCurTime)); | |
| iBufUsedSize += iCurUsedSize; | |
| iBufLeftSize -= iCurUsedSize; | |
| } | |
| m_pFBS = WelsFopen (chFileName, "wb"); | |
| m_pFBSSize = WelsFopen (chFileNameSize, "wb"); | |
| #endif//OUTPUT_BIT_STREAM | |
| } | |
| /*************************************************************************** | |
| * Description: | |
| * class CWelsDecoder destructor function, destroy allocced memory | |
| * | |
| * Input parameters: none | |
| * | |
| * return: none | |
| ***************************************************************************/ | |
| CWelsDecoder::~CWelsDecoder() { | |
| if (m_pWelsTrace != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::~CWelsDecoder()"); | |
| } | |
| CloseDecoderThreads(); | |
| UninitDecoder(); | |
| #ifdef OUTPUT_BIT_STREAM | |
| if (m_pFBS) { | |
| WelsFclose (m_pFBS); | |
| m_pFBS = NULL; | |
| } | |
| if (m_pFBSSize) { | |
| WelsFclose (m_pFBSSize); | |
| m_pFBSSize = NULL; | |
| } | |
| #endif//OUTPUT_BIT_STREAM | |
| if (m_pWelsTrace != NULL) { | |
| delete m_pWelsTrace; | |
| m_pWelsTrace = NULL; | |
| } | |
| if (m_pDecThrCtx != NULL) { | |
| delete[] m_pDecThrCtx; | |
| m_pDecThrCtx = NULL; | |
| } | |
| } | |
| long CWelsDecoder::Initialize (const SDecodingParam* pParam) { | |
| int iRet = ERR_NONE; | |
| if (m_pWelsTrace == NULL) { | |
| return cmMallocMemeError; | |
| } | |
| if (pParam == NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "CWelsDecoder::Initialize(), invalid input argument."); | |
| return cmInitParaError; | |
| } | |
| // H.264 decoder initialization,including memory allocation,then open it ready to decode | |
| iRet = InitDecoder (pParam); | |
| if (iRet) | |
| return iRet; | |
| return cmResultSuccess; | |
| } | |
| long CWelsDecoder::Uninitialize() { | |
| UninitDecoder(); | |
| return ERR_NONE; | |
| } | |
| void CWelsDecoder::UninitDecoder (void) { | |
| for (int32_t i = 0; i < m_iCtxCount; ++i) { | |
| if (m_pDecThrCtx[i].pCtx != NULL) { | |
| if (i > 0) { | |
| WelsResetRefPicWithoutUnRef (m_pDecThrCtx[i].pCtx); | |
| } | |
| UninitDecoderCtx (m_pDecThrCtx[i].pCtx); | |
| } | |
| } | |
| } | |
| void CWelsDecoder::OpenDecoderThreads() { | |
| if (m_iThreadCount >= 1) { | |
| m_uiDecodeTimeStamp = 0; | |
| CREATE_SEMAPHORE (&m_sIsBusy, m_iThreadCount, m_iThreadCount, NULL); | |
| WelsMutexInit (&m_csDecoder); | |
| CREATE_EVENT (&m_sBufferingEvent, 1, 0, NULL); | |
| SET_EVENT (&m_sBufferingEvent); | |
| CREATE_EVENT (&m_sReleaseBufferEvent, 1, 0, NULL); | |
| SET_EVENT (&m_sReleaseBufferEvent); | |
| for (int32_t i = 0; i < m_iThreadCount; ++i) { | |
| m_pDecThrCtx[i].sThreadInfo.uiThrMaxNum = m_iThreadCount; | |
| m_pDecThrCtx[i].sThreadInfo.uiThrNum = i; | |
| m_pDecThrCtx[i].sThreadInfo.uiThrStackSize = WELS_DEC_MAX_THREAD_STACK_SIZE; | |
| m_pDecThrCtx[i].sThreadInfo.pThrProcMain = pThrProcFrame; | |
| m_pDecThrCtx[i].sThreadInfo.sIsBusy = &m_sIsBusy; | |
| m_pDecThrCtx[i].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_RUN; | |
| m_pDecThrCtx[i].threadCtxOwner = this; | |
| m_pDecThrCtx[i].kpSrc = NULL; | |
| m_pDecThrCtx[i].kiSrcLen = 0; | |
| m_pDecThrCtx[i].ppDst = NULL; | |
| m_pDecThrCtx[i].pDec = NULL; | |
| CREATE_EVENT (&m_pDecThrCtx[i].sImageReady, 1, 0, NULL); | |
| CREATE_EVENT (&m_pDecThrCtx[i].sSliceDecodeStart, 1, 0, NULL); | |
| CREATE_EVENT (&m_pDecThrCtx[i].sSliceDecodeFinish, 1, 0, NULL); | |
| CREATE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle, 0, 1, NULL); | |
| CREATE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated, 0, 1, NULL); | |
| CREATE_THREAD (&m_pDecThrCtx[i].sThreadInfo.sThrHandle, pThrProcInit, (void*) (& (m_pDecThrCtx[i]))); | |
| } | |
| } | |
| } | |
| void CWelsDecoder::CloseDecoderThreads() { | |
| if (m_iThreadCount >= 1) { | |
| SET_EVENT (&m_sReleaseBufferEvent); | |
| for (int32_t i = 0; i < m_iThreadCount; i++) { //waiting the completion begun slices | |
| WAIT_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); | |
| m_pDecThrCtx[i].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_ABORT; | |
| RELEASE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated); | |
| WAIT_THREAD (&m_pDecThrCtx[i].sThreadInfo.sThrHandle); | |
| CLOSE_EVENT (&m_pDecThrCtx[i].sImageReady); | |
| CLOSE_EVENT (&m_pDecThrCtx[i].sSliceDecodeStart); | |
| CLOSE_EVENT (&m_pDecThrCtx[i].sSliceDecodeFinish); | |
| CLOSE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle); | |
| CLOSE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated); | |
| } | |
| WelsMutexDestroy (&m_csDecoder); | |
| CLOSE_EVENT (&m_sBufferingEvent); | |
| CLOSE_EVENT (&m_sReleaseBufferEvent); | |
| CLOSE_SEMAPHORE (&m_sIsBusy); | |
| } | |
| } | |
| void CWelsDecoder::UninitDecoderCtx (PWelsDecoderContext& pCtx) { | |
| if (pCtx != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::UninitDecoderCtx(), openh264 codec version = %s.", | |
| VERSION_NUMBER); | |
| WelsEndDecoder (pCtx); | |
| if (pCtx->pMemAlign != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, | |
| "CWelsDecoder::UninitDecoder(), verify memory usage (%d bytes) after free..", | |
| pCtx->pMemAlign->WelsGetMemoryUsage()); | |
| delete pCtx->pMemAlign; | |
| pCtx->pMemAlign = NULL; | |
| } | |
| if (NULL != pCtx) { | |
| WelsFree (pCtx, "m_pDecContext"); | |
| pCtx = NULL; | |
| } | |
| if (m_iCtxCount <= 1) m_pDecThrCtx[0].pCtx = NULL; | |
| } | |
| } | |
| // the return value of this function is not suitable, it need report failure info to upper layer. | |
| int32_t CWelsDecoder::InitDecoder (const SDecodingParam* pParam) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, | |
| "CWelsDecoder::init_decoder(), openh264 codec version = %s, ParseOnly = %d", | |
| VERSION_NUMBER, (int32_t)pParam->bParseOnly); | |
| if (m_iThreadCount >= 1 && pParam->bParseOnly) { | |
| m_iThreadCount = 0; | |
| } | |
| OpenDecoderThreads(); | |
| //reset decoder context | |
| memset (&m_sDecoderStatistics, 0, sizeof (SDecoderStatistics)); | |
| memset (&m_sLastDecPicInfo, 0, sizeof (SWelsLastDecPicInfo)); | |
| memset (&m_sVlcTable, 0, sizeof (SVlcTable)); | |
| UninitDecoder(); | |
| WelsDecoderLastDecPicInfoDefaults (m_sLastDecPicInfo); | |
| for (int32_t i = 0; i < m_iCtxCount; ++i) { | |
| InitDecoderCtx (m_pDecThrCtx[i].pCtx, pParam); | |
| if (m_iThreadCount >= 1) { | |
| m_pDecThrCtx[i].pCtx->pThreadCtx = &m_pDecThrCtx[i]; | |
| } | |
| } | |
| m_bParamSetsLostFlag = false; | |
| m_bFreezeOutput = false; | |
| return cmResultSuccess; | |
| } | |
| // the return value of this function is not suitable, it need report failure info to upper layer. | |
| int32_t CWelsDecoder::InitDecoderCtx (PWelsDecoderContext& pCtx, const SDecodingParam* pParam) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, | |
| "CWelsDecoder::init_decoder(), openh264 codec version = %s, ParseOnly = %d", | |
| VERSION_NUMBER, (int32_t)pParam->bParseOnly); | |
| //reset decoder context | |
| UninitDecoderCtx (pCtx); | |
| pCtx = (PWelsDecoderContext)WelsMallocz (sizeof (SWelsDecoderContext), "m_pDecContext"); | |
| if (NULL == pCtx) | |
| return cmMallocMemeError; | |
| int32_t iCacheLineSize = 16; // on chip cache line size in byte | |
| pCtx->pMemAlign = new CMemoryAlign (iCacheLineSize); | |
| WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == pCtx->pMemAlign), UninitDecoderCtx (pCtx)) | |
| if (m_iCtxCount <= 1) m_pDecThrCtx[0].pCtx = pCtx; | |
| //fill in default value into context | |
| pCtx->pLastDecPicInfo = &m_sLastDecPicInfo; | |
| pCtx->pDecoderStatistics = &m_sDecoderStatistics; | |
| pCtx->pVlcTable = &m_sVlcTable; | |
| pCtx->pPictInfoList = m_sPictInfoList; | |
| pCtx->pPictReoderingStatus = &m_sReoderingStatus; | |
| pCtx->pCsDecoder = &m_csDecoder; | |
| pCtx->pStreamSeqNum = &m_iStreamSeqNum; | |
| WelsDecoderDefaults (pCtx, &m_pWelsTrace->m_sLogCtx); | |
| WelsDecoderSpsPpsDefaults (pCtx->sSpsPpsCtx); | |
| //check param and update decoder context | |
| pCtx->pParam = (SDecodingParam*)pCtx->pMemAlign->WelsMallocz (sizeof (SDecodingParam), | |
| "SDecodingParam"); | |
| WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == pCtx->pParam), UninitDecoderCtx (pCtx)); | |
| int32_t iRet = DecoderConfigParam (pCtx, pParam); | |
| WELS_VERIFY_RETURN_IFNEQ (iRet, cmResultSuccess); | |
| //init decoder | |
| WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, WelsInitDecoder (pCtx, &m_pWelsTrace->m_sLogCtx), | |
| UninitDecoderCtx (pCtx)) | |
| pCtx->pPicBuff = NULL; | |
| return cmResultSuccess; | |
| } | |
| int32_t CWelsDecoder::ResetDecoder (PWelsDecoderContext& pCtx) { | |
| // TBC: need to be modified when context and trace point are null | |
| if (m_iThreadCount >= 1) { | |
| ThreadResetDecoder (pCtx); | |
| } else { | |
| if (pCtx != NULL && m_pWelsTrace != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "ResetDecoder(), context error code is %d", | |
| pCtx->iErrorCode); | |
| SDecodingParam sPrevParam; | |
| memcpy (&sPrevParam, pCtx->pParam, sizeof (SDecodingParam)); | |
| WELS_VERIFY_RETURN_PROC_IF (cmInitParaError, InitDecoderCtx (pCtx, &sPrevParam), | |
| UninitDecoderCtx (pCtx)); | |
| } else if (m_pWelsTrace != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "ResetDecoder() failed as decoder context null"); | |
| } | |
| ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, false); | |
| } | |
| return ERR_INFO_UNINIT; | |
| } | |
| int32_t CWelsDecoder::ThreadResetDecoder (PWelsDecoderContext& pCtx) { | |
| // TBC: need to be modified when context and trace point are null | |
| SDecodingParam sPrevParam; | |
| if (pCtx != NULL && m_pWelsTrace != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "ResetDecoder(), context error code is %d", pCtx->iErrorCode); | |
| memcpy (&sPrevParam, pCtx->pParam, sizeof (SDecodingParam)); | |
| ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, true); | |
| CloseDecoderThreads(); | |
| UninitDecoder(); | |
| InitDecoder (&sPrevParam); | |
| } else if (m_pWelsTrace != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "ResetDecoder() failed as decoder context null"); | |
| } | |
| return ERR_INFO_UNINIT; | |
| } | |
| /* | |
| * Set Option | |
| */ | |
| long CWelsDecoder::SetOption (DECODER_OPTION eOptID, void* pOption) { | |
| int iVal = 0; | |
| if (eOptID == DECODER_OPTION_NUM_OF_THREADS) { | |
| if (pOption != NULL) { | |
| int32_t threadCount = * ((int32_t*)pOption); | |
| if (threadCount < 0) threadCount = 0; | |
| if (threadCount > m_iCpuCount) { | |
| threadCount = m_iCpuCount; | |
| } | |
| if (threadCount > 3) { | |
| threadCount = 3; | |
| } | |
| if (threadCount != m_iThreadCount) { | |
| m_iThreadCount = threadCount; | |
| if (m_pDecThrCtx != NULL) { | |
| delete [] m_pDecThrCtx; | |
| m_iCtxCount = m_iThreadCount == 0 ? 1 : m_iThreadCount; | |
| m_pDecThrCtx = new SWelsDecoderThreadCTX[m_iCtxCount]; | |
| memset (m_pDecThrCtx, 0, sizeof (SWelsDecoderThreadCTX)*m_iCtxCount); | |
| } | |
| } | |
| } | |
| return cmResultSuccess; | |
| } | |
| for (int32_t i = 0; i < m_iCtxCount; ++i) { | |
| PWelsDecoderContext pDecContext = m_pDecThrCtx[i].pCtx; | |
| if (pDecContext == NULL && eOptID != DECODER_OPTION_TRACE_LEVEL && | |
| eOptID != DECODER_OPTION_TRACE_CALLBACK && eOptID != DECODER_OPTION_TRACE_CALLBACK_CONTEXT) | |
| return dsInitialOptExpected; | |
| if (eOptID == DECODER_OPTION_END_OF_STREAM) { // Indicate bit-stream of the final frame to be decoded | |
| if (pOption == NULL) | |
| return cmInitParaError; | |
| iVal = * ((int*)pOption); // boolean value for whether enabled End Of Stream flag | |
| if (pDecContext == NULL) return dsInitialOptExpected; | |
| pDecContext->bEndOfStreamFlag = iVal ? true : false; | |
| if (iVal && m_iThreadCount >= 1) | |
| SET_EVENT (&m_sReleaseBufferEvent); | |
| return cmResultSuccess; | |
| } else if (eOptID == DECODER_OPTION_ERROR_CON_IDC) { // Indicate error concealment status | |
| if (pOption == NULL) | |
| return cmInitParaError; | |
| if (pDecContext == NULL) return dsInitialOptExpected; | |
| iVal = * ((int*)pOption); // int value for error concealment idc | |
| iVal = WELS_CLIP3 (iVal, (int32_t)ERROR_CON_DISABLE, (int32_t)ERROR_CON_SLICE_MV_COPY_CROSS_IDR_FREEZE_RES_CHANGE); | |
| if ((pDecContext->pParam->bParseOnly) && (iVal != (int32_t)ERROR_CON_DISABLE)) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, | |
| "CWelsDecoder::SetOption for ERROR_CON_IDC = %d not allowd for parse only!.", iVal); | |
| return cmInitParaError; | |
| } | |
| pDecContext->pParam->eEcActiveIdc = (ERROR_CON_IDC)iVal; | |
| InitErrorCon (pDecContext); | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, | |
| "CWelsDecoder::SetOption for ERROR_CON_IDC = %d.", iVal); | |
| return cmResultSuccess; | |
| } else if (eOptID == DECODER_OPTION_TRACE_LEVEL) { | |
| if (m_pWelsTrace) { | |
| uint32_t level = * ((uint32_t*)pOption); | |
| m_pWelsTrace->SetTraceLevel (level); | |
| } | |
| return cmResultSuccess; | |
| } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK) { | |
| if (m_pWelsTrace) { | |
| WelsTraceCallback callback = * ((WelsTraceCallback*)pOption); | |
| m_pWelsTrace->SetTraceCallback (callback); | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, | |
| "CWelsDecoder::SetOption():DECODER_OPTION_TRACE_CALLBACK callback = %p.", | |
| callback); | |
| } | |
| return cmResultSuccess; | |
| } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK_CONTEXT) { | |
| if (m_pWelsTrace) { | |
| void* ctx = * ((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; | |
| } else if (eOptID == DECODER_OPTION_STATISTICS_LOG_INTERVAL) { | |
| if (pOption) { | |
| if (pDecContext == NULL) return dsInitialOptExpected; | |
| pDecContext->pDecoderStatistics->iStatisticsLogInterval = (* ((unsigned int*)pOption)); | |
| return cmResultSuccess; | |
| } | |
| } else if (eOptID == DECODER_OPTION_GET_SAR_INFO) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING, | |
| "CWelsDecoder::SetOption():DECODER_OPTION_GET_SAR_INFO: this option is get-only!"); | |
| return cmInitParaError; | |
| } | |
| } | |
| return cmInitParaError; | |
| } | |
| /* | |
| * Get Option | |
| */ | |
| long CWelsDecoder::GetOption (DECODER_OPTION eOptID, void* pOption) { | |
| int iVal = 0; | |
| if (DECODER_OPTION_NUM_OF_THREADS == eOptID) { | |
| * ((int*)pOption) = m_iThreadCount; | |
| return cmResultSuccess; | |
| } | |
| PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; | |
| if (pDecContext == NULL) | |
| return cmInitExpected; | |
| if (pOption == NULL) | |
| return cmInitParaError; | |
| if (DECODER_OPTION_END_OF_STREAM == eOptID) { | |
| iVal = pDecContext->bEndOfStreamFlag; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } | |
| #ifdef LONG_TERM_REF | |
| else if (DECODER_OPTION_IDR_PIC_ID == eOptID) { | |
| iVal = pDecContext->uiCurIdrPicId; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_FRAME_NUM == eOptID) { | |
| iVal = pDecContext->iFrameNum; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_LTR_MARKING_FLAG == eOptID) { | |
| iVal = pDecContext->bCurAuContainLtrMarkSeFlag; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_LTR_MARKED_FRAME_NUM == eOptID) { | |
| iVal = pDecContext->iFrameNumOfAuMarkedLtr; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } | |
| #endif | |
| else if (DECODER_OPTION_VCL_NAL == eOptID) { //feedback whether or not have VCL NAL in current AU | |
| iVal = pDecContext->iFeedbackVclNalInAu; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_TEMPORAL_ID == eOptID) { //if have VCL NAL in current AU, then feedback the temporal ID | |
| iVal = pDecContext->iFeedbackTidInAu; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_IS_REF_PIC == eOptID) { | |
| iVal = pDecContext->iFeedbackNalRefIdc; | |
| if (iVal > 0) | |
| iVal = 1; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_ERROR_CON_IDC == eOptID) { | |
| iVal = (int)pDecContext->pParam->eEcActiveIdc; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_GET_STATISTICS == eOptID) { // get decoder statistics info for real time debugging | |
| SDecoderStatistics* pDecoderStatistics = (static_cast<SDecoderStatistics*> (pOption)); | |
| memcpy (pDecoderStatistics, pDecContext->pDecoderStatistics, sizeof (SDecoderStatistics)); | |
| if (pDecContext->pDecoderStatistics->uiDecodedFrameCount != 0) { //not original status | |
| pDecoderStatistics->fAverageFrameSpeedInMs = (float) (pDecContext->dDecTime) / | |
| (pDecContext->pDecoderStatistics->uiDecodedFrameCount); | |
| pDecoderStatistics->fActualAverageFrameSpeedInMs = (float) (pDecContext->dDecTime) / | |
| (pDecContext->pDecoderStatistics->uiDecodedFrameCount + pDecContext->pDecoderStatistics->uiFreezingIDRNum + | |
| pDecContext->pDecoderStatistics->uiFreezingNonIDRNum); | |
| } | |
| return cmResultSuccess; | |
| } else if (eOptID == DECODER_OPTION_STATISTICS_LOG_INTERVAL) { | |
| if (pOption) { | |
| iVal = pDecContext->pDecoderStatistics->iStatisticsLogInterval; | |
| * ((unsigned int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } | |
| } else if (DECODER_OPTION_GET_SAR_INFO == eOptID) { //get decoder SAR info in VUI | |
| PVuiSarInfo pVuiSarInfo = (static_cast<PVuiSarInfo> (pOption)); | |
| memset (pVuiSarInfo, 0, sizeof (SVuiSarInfo)); | |
| if (!pDecContext->pSps) { | |
| return cmInitExpected; | |
| } else { | |
| pVuiSarInfo->uiSarWidth = pDecContext->pSps->sVui.uiSarWidth; | |
| pVuiSarInfo->uiSarHeight = pDecContext->pSps->sVui.uiSarHeight; | |
| pVuiSarInfo->bOverscanAppropriateFlag = pDecContext->pSps->sVui.bOverscanAppropriateFlag; | |
| return cmResultSuccess; | |
| } | |
| } else if (DECODER_OPTION_PROFILE == eOptID) { | |
| if (!pDecContext->pSps) { | |
| return cmInitExpected; | |
| } | |
| iVal = (int)pDecContext->pSps->uiProfileIdc; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_LEVEL == eOptID) { | |
| if (!pDecContext->pSps) { | |
| return cmInitExpected; | |
| } | |
| iVal = (int)pDecContext->pSps->uiLevelIdc; | |
| * ((int*)pOption) = iVal; | |
| return cmResultSuccess; | |
| } else if (DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER == eOptID) { | |
| for (int32_t activeThread = 0; activeThread < m_DecCtxActiveCount; ++activeThread) { | |
| WAIT_SEMAPHORE (&m_pDecThrCtxActive[activeThread]->sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); | |
| RELEASE_SEMAPHORE (&m_pDecThrCtxActive[activeThread]->sThreadInfo.sIsIdle); | |
| } | |
| * ((int*)pOption) = m_sReoderingStatus.iNumOfPicts; | |
| return cmResultSuccess; | |
| } | |
| return cmInitParaError; | |
| } | |
| DECODING_STATE CWelsDecoder::DecodeFrameNoDelay (const unsigned char* kpSrc, | |
| const int kiSrcLen, | |
| unsigned char** ppDst, | |
| SBufferInfo* pDstInfo) { | |
| int iRet = dsErrorFree; | |
| if (m_iThreadCount >= 1) { | |
| SET_EVENT (&m_sReleaseBufferEvent); | |
| iRet = ThreadDecodeFrameInternal (kpSrc, kiSrcLen, ppDst, pDstInfo); | |
| // The worker publishes into the shared reorder queue via | |
| // BufferingReadyPicture() while holding m_csDecoder, so every access to that | |
| // queue state on the caller side must take the same lock. The lock must not | |
| // be held across WAIT_EVENT() or the Release* dequeue (which re-locks), so | |
| // snapshot the shared counters under the lock instead of reading them raw. | |
| WelsMutexLock (&m_csDecoder); | |
| int32_t iNumOfPicts = m_sReoderingStatus.iNumOfPicts; | |
| WelsMutexUnlock (&m_csDecoder); | |
| if (iNumOfPicts) { | |
| WAIT_EVENT (&m_sBufferingEvent, WELS_DEC_THREAD_WAIT_INFINITE); | |
| RESET_EVENT (&m_sBufferingEvent); | |
| RESET_EVENT (&m_sReleaseBufferEvent); | |
| WelsMutexLock (&m_csDecoder); | |
| const bool bHasBSlice = m_sReoderingStatus.bHasBSlice; | |
| iNumOfPicts = m_sReoderingStatus.iNumOfPicts; | |
| WelsMutexUnlock (&m_csDecoder); | |
| if (!bHasBSlice) { | |
| if (iNumOfPicts > 1) { | |
| ReleaseBufferedReadyPictureNoReorder (NULL, ppDst, pDstInfo); | |
| } | |
| } | |
| else { | |
| ReleaseBufferedReadyPictureReorder (NULL, ppDst, pDstInfo); | |
| } | |
| } | |
| return (DECODING_STATE)iRet; | |
| } | |
| //SBufferInfo sTmpBufferInfo; | |
| //unsigned char* ppTmpDst[3] = {NULL, NULL, NULL}; | |
| iRet = (int)DecodeFrame2 (kpSrc, kiSrcLen, ppDst, pDstInfo); | |
| //memcpy (&sTmpBufferInfo, pDstInfo, sizeof (SBufferInfo)); | |
| //ppTmpDst[0] = ppDst[0]; | |
| //ppTmpDst[1] = ppDst[1]; | |
| //ppTmpDst[2] = ppDst[2]; | |
| iRet |= DecodeFrame2 (NULL, 0, ppDst, pDstInfo); | |
| //if ((pDstInfo->iBufferStatus == 0) && (sTmpBufferInfo.iBufferStatus == 1)) { | |
| //memcpy (pDstInfo, &sTmpBufferInfo, sizeof (SBufferInfo)); | |
| //ppDst[0] = ppTmpDst[0]; | |
| //ppDst[1] = ppTmpDst[1]; | |
| //ppDst[2] = ppTmpDst[2]; | |
| //} | |
| return (DECODING_STATE)iRet; | |
| } | |
| DECODING_STATE CWelsDecoder::DecodeFrame2WithCtx (PWelsDecoderContext pDecContext, const unsigned char* kpSrc, | |
| const int kiSrcLen, | |
| unsigned char** ppDst, | |
| SBufferInfo* pDstInfo) { | |
| if (pDecContext == NULL || pDecContext->pParam == NULL) { | |
| if (m_pWelsTrace != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "Call DecodeFrame2 without Initialize.\n"); | |
| } | |
| return dsInitialOptExpected; | |
| } | |
| if (pDecContext->pParam->bParseOnly) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "bParseOnly should be false for this API calling! \n"); | |
| pDecContext->iErrorCode |= dsInvalidArgument; | |
| return dsInvalidArgument; | |
| } | |
| if (CheckBsBuffer (pDecContext, kiSrcLen)) { | |
| if (ResetDecoder(pDecContext)) { | |
| if (pDstInfo) pDstInfo->iBufferStatus = 0; | |
| return dsOutOfMemory; | |
| } | |
| return dsErrorFree; | |
| } | |
| if (kiSrcLen > 0 && kpSrc != NULL) { | |
| #ifdef OUTPUT_BIT_STREAM | |
| if (m_pFBS) { | |
| WelsFwrite (kpSrc, sizeof (unsigned char), kiSrcLen, m_pFBS); | |
| WelsFflush (m_pFBS); | |
| } | |
| if (m_pFBSSize) { | |
| WelsFwrite (&kiSrcLen, sizeof (int), 1, m_pFBSSize); | |
| WelsFflush (m_pFBSSize); | |
| } | |
| #endif//OUTPUT_BIT_STREAM | |
| pDecContext->bEndOfStreamFlag = false; | |
| if (GetThreadCount (pDecContext) <= 0) { | |
| pDecContext->uiDecodingTimeStamp = ++m_uiDecodeTimeStamp; | |
| } | |
| } else { | |
| //For application MODE, the error detection should be added for safe. | |
| //But for CONSOLE MODE, when decoding LAST AU, kiSrcLen==0 && kpSrc==NULL. | |
| pDecContext->bEndOfStreamFlag = true; | |
| pDecContext->bInstantDecFlag = true; | |
| } | |
| int64_t iStart, iEnd; | |
| iStart = WelsTime(); | |
| if (GetThreadCount (pDecContext) <= 1) { | |
| ppDst[0] = ppDst[1] = ppDst[2] = NULL; | |
| } | |
| pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding. | |
| pDecContext->iFeedbackVclNalInAu = FEEDBACK_UNKNOWN_NAL; //initialize | |
| unsigned long long uiInBsTimeStamp = pDstInfo->uiInBsTimeStamp; | |
| if (GetThreadCount (pDecContext) <= 1) { | |
| memset (pDstInfo, 0, sizeof (SBufferInfo)); | |
| } | |
| pDstInfo->uiInBsTimeStamp = uiInBsTimeStamp; | |
| #ifdef LONG_TERM_REF | |
| pDecContext->bReferenceLostAtT0Flag = false; //initialize for LTR | |
| pDecContext->bCurAuContainLtrMarkSeFlag = false; | |
| pDecContext->iFrameNumOfAuMarkedLtr = 0; | |
| pDecContext->iFrameNum = -1; //initialize | |
| #endif | |
| if (GetThreadCount (pDecContext) >= 1) { | |
| WAIT_EVENT (&m_sReleaseBufferEvent, WELS_DEC_THREAD_WAIT_INFINITE); | |
| } | |
| pDecContext->iFeedbackTidInAu = -1; //initialize | |
| pDecContext->iFeedbackNalRefIdc = -1; //initialize | |
| if (pDstInfo) { | |
| pDstInfo->uiOutYuvTimeStamp = 0; | |
| pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp; | |
| } else { | |
| pDecContext->uiTimeStamp = 0; | |
| } | |
| WelsDecodeBs (pDecContext, kpSrc, kiSrcLen, ppDst, | |
| pDstInfo, NULL); //iErrorCode has been modified in this function | |
| pDecContext->bInstantDecFlag = false; //reset no-delay flag | |
| if (pDecContext->iErrorCode) { | |
| EWelsNalUnitType eNalType = | |
| NAL_UNIT_UNSPEC_0; //for NBR, IDR frames are expected to decode as followed if error decoding an IDR currently | |
| eNalType = pDecContext->sCurNalHead.eNalUnitType; | |
| if (pDecContext->iErrorCode & dsOutOfMemory) { | |
| if (ResetDecoder (pDecContext)) { | |
| if (pDstInfo) pDstInfo->iBufferStatus = 0; | |
| return dsOutOfMemory; | |
| } | |
| return dsErrorFree; | |
| } | |
| if (pDecContext->iErrorCode & dsRefListNullPtrs) { | |
| if (ResetDecoder (pDecContext)) { | |
| if (pDstInfo) pDstInfo->iBufferStatus = 0; | |
| return dsRefListNullPtrs; | |
| } | |
| return dsErrorFree; | |
| } | |
| //for AVC bitstream (excluding AVC with temporal scalability, including TP), as long as error occur, SHOULD notify upper layer key frame loss. | |
| if ((IS_PARAM_SETS_NALS (eNalType) || NAL_UNIT_CODED_SLICE_IDR == eNalType) || | |
| (VIDEO_BITSTREAM_AVC == pDecContext->eVideoType)) { | |
| if (pDecContext->pParam->eEcActiveIdc == ERROR_CON_DISABLE) { | |
| #ifdef LONG_TERM_REF | |
| pDecContext->bParamSetsLostFlag = true; | |
| #else | |
| pDecContext->bReferenceLostAtT0Flag = true; | |
| #endif | |
| } | |
| } | |
| if (pDecContext->bPrintFrameErrorTraceFlag) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "decode failed, failure type:%d \n", | |
| pDecContext->iErrorCode); | |
| pDecContext->bPrintFrameErrorTraceFlag = false; | |
| } else { | |
| pDecContext->iIgnoredErrorInfoPacketCount++; | |
| if (pDecContext->iIgnoredErrorInfoPacketCount == INT_MAX) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING, "continuous error reached INT_MAX! Restart as 0."); | |
| pDecContext->iIgnoredErrorInfoPacketCount = 0; | |
| } | |
| } | |
| if ((pDecContext->pParam->eEcActiveIdc != ERROR_CON_DISABLE) && (pDstInfo->iBufferStatus == 1)) { | |
| //TODO after dec status updated | |
| pDecContext->iErrorCode |= dsDataErrorConcealed; | |
| pDecContext->pDecoderStatistics->uiDecodedFrameCount++; | |
| if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t | |
| ResetDecStatNums (pDecContext->pDecoderStatistics); | |
| pDecContext->pDecoderStatistics->uiDecodedFrameCount++; | |
| } | |
| int32_t iMbConcealedNum = pDecContext->iMbEcedNum + pDecContext->iMbEcedPropNum; | |
| pDecContext->pDecoderStatistics->uiAvgEcRatio = pDecContext->iMbNum == 0 ? | |
| (pDecContext->pDecoderStatistics->uiAvgEcRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) : (( | |
| pDecContext->pDecoderStatistics->uiAvgEcRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) + (( | |
| iMbConcealedNum * 100) / pDecContext->iMbNum)); | |
| pDecContext->pDecoderStatistics->uiAvgEcPropRatio = pDecContext->iMbNum == 0 ? | |
| (pDecContext->pDecoderStatistics->uiAvgEcPropRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) : (( | |
| pDecContext->pDecoderStatistics->uiAvgEcPropRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) + (( | |
| pDecContext->iMbEcedPropNum * 100) / pDecContext->iMbNum)); | |
| pDecContext->pDecoderStatistics->uiEcFrameNum += (iMbConcealedNum == 0 ? 0 : 1); | |
| pDecContext->pDecoderStatistics->uiAvgEcRatio = pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 : | |
| pDecContext->pDecoderStatistics->uiAvgEcRatio / pDecContext->pDecoderStatistics->uiEcFrameNum; | |
| pDecContext->pDecoderStatistics->uiAvgEcPropRatio = pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 : | |
| pDecContext->pDecoderStatistics->uiAvgEcPropRatio / pDecContext->pDecoderStatistics->uiEcFrameNum; | |
| } | |
| iEnd = WelsTime(); | |
| pDecContext->dDecTime += (iEnd - iStart) / 1e3; | |
| OutputStatisticsLog (*pDecContext->pDecoderStatistics); | |
| if (GetThreadCount (pDecContext) >= 1) { | |
| BufferingReadyPicture (pDecContext, ppDst, pDstInfo); | |
| SET_EVENT (&m_sBufferingEvent); | |
| } else { | |
| ReorderPicturesInDisplay (pDecContext, ppDst, pDstInfo); | |
| } | |
| return (DECODING_STATE)pDecContext->iErrorCode; | |
| } | |
| // else Error free, the current codec works well | |
| if (pDstInfo->iBufferStatus == 1) { | |
| pDecContext->pDecoderStatistics->uiDecodedFrameCount++; | |
| if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t | |
| ResetDecStatNums (pDecContext->pDecoderStatistics); | |
| pDecContext->pDecoderStatistics->uiDecodedFrameCount++; | |
| } | |
| OutputStatisticsLog (*pDecContext->pDecoderStatistics); | |
| } | |
| iEnd = WelsTime(); | |
| pDecContext->dDecTime += (iEnd - iStart) / 1e3; | |
| if (GetThreadCount (pDecContext) >= 1) { | |
| BufferingReadyPicture (pDecContext, ppDst, pDstInfo); | |
| SET_EVENT (&m_sBufferingEvent); | |
| } else { | |
| ReorderPicturesInDisplay (pDecContext, ppDst, pDstInfo); | |
| } | |
| return dsErrorFree; | |
| } | |
| DECODING_STATE CWelsDecoder::DecodeFrame2 (const unsigned char* kpSrc, | |
| const int kiSrcLen, | |
| unsigned char** ppDst, | |
| SBufferInfo* pDstInfo) { | |
| PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; | |
| return DecodeFrame2WithCtx (pDecContext, kpSrc, kiSrcLen, ppDst, pDstInfo); | |
| } | |
| DECODING_STATE CWelsDecoder::FlushFrame (unsigned char** ppDst, | |
| SBufferInfo* pDstInfo) { | |
| bool bEndOfStreamFlag = true; | |
| if (m_iThreadCount <= 1) { | |
| for (int32_t j = 0; j < m_iCtxCount; ++j) { | |
| if (!m_pDecThrCtx[j].pCtx->bEndOfStreamFlag) { | |
| bEndOfStreamFlag = false; | |
| } | |
| } | |
| } | |
| // Read the shared reorder-queue counters under m_csDecoder in | |
| // threaded mode (the worker mutates them in BufferingReadyPicture()); the | |
| // Release* dequeue re-locks internally, so do not hold the lock across it. | |
| if (m_iThreadCount >= 1) WelsMutexLock (&m_csDecoder); | |
| const int32_t iNumOfPicts = m_sReoderingStatus.iNumOfPicts; | |
| const bool bHasBSlice = m_sReoderingStatus.bHasBSlice; | |
| if (m_iThreadCount >= 1) WelsMutexUnlock (&m_csDecoder); | |
| if (bEndOfStreamFlag && iNumOfPicts > 0) { | |
| if (!bHasBSlice) { | |
| ReleaseBufferedReadyPictureNoReorder (NULL, ppDst, pDstInfo); | |
| } | |
| else { | |
| ReleaseBufferedReadyPictureReorder (NULL, ppDst, pDstInfo, true); | |
| } | |
| } | |
| return dsErrorFree; | |
| } | |
| void CWelsDecoder::OutputStatisticsLog (SDecoderStatistics& sDecoderStatistics) { | |
| if ((sDecoderStatistics.uiDecodedFrameCount > 0) && (sDecoderStatistics.iStatisticsLogInterval > 0) | |
| && ((sDecoderStatistics.uiDecodedFrameCount % sDecoderStatistics.iStatisticsLogInterval) == 0)) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, | |
| "DecoderStatistics: uiWidth=%d, uiHeight=%d, fAverageFrameSpeedInMs=%.1f, fActualAverageFrameSpeedInMs=%.1f, \ | |
| uiDecodedFrameCount=%d, uiResolutionChangeTimes=%d, uiIDRCorrectNum=%d, \ | |
| uiAvgEcRatio=%d, uiAvgEcPropRatio=%d, uiEcIDRNum=%d, uiEcFrameNum=%d, \ | |
| uiIDRLostNum=%d, uiFreezingIDRNum=%d, uiFreezingNonIDRNum=%d, iAvgLumaQp=%d, \ | |
| iSpsReportErrorNum=%d, iSubSpsReportErrorNum=%d, iPpsReportErrorNum=%d, iSpsNoExistNalNum=%d, iSubSpsNoExistNalNum=%d, iPpsNoExistNalNum=%d, \ | |
| uiProfile=%d, uiLevel=%d, \ | |
| iCurrentActiveSpsId=%d, iCurrentActivePpsId=%d,", | |
| sDecoderStatistics.uiWidth, | |
| sDecoderStatistics.uiHeight, | |
| sDecoderStatistics.fAverageFrameSpeedInMs, | |
| sDecoderStatistics.fActualAverageFrameSpeedInMs, | |
| sDecoderStatistics.uiDecodedFrameCount, | |
| sDecoderStatistics.uiResolutionChangeTimes, | |
| sDecoderStatistics.uiIDRCorrectNum, | |
| sDecoderStatistics.uiAvgEcRatio, | |
| sDecoderStatistics.uiAvgEcPropRatio, | |
| sDecoderStatistics.uiEcIDRNum, | |
| sDecoderStatistics.uiEcFrameNum, | |
| sDecoderStatistics.uiIDRLostNum, | |
| sDecoderStatistics.uiFreezingIDRNum, | |
| sDecoderStatistics.uiFreezingNonIDRNum, | |
| sDecoderStatistics.iAvgLumaQp, | |
| sDecoderStatistics.iSpsReportErrorNum, | |
| sDecoderStatistics.iSubSpsReportErrorNum, | |
| sDecoderStatistics.iPpsReportErrorNum, | |
| sDecoderStatistics.iSpsNoExistNalNum, | |
| sDecoderStatistics.iSubSpsNoExistNalNum, | |
| sDecoderStatistics.iPpsNoExistNalNum, | |
| sDecoderStatistics.uiProfile, | |
| sDecoderStatistics.uiLevel, | |
| sDecoderStatistics.iCurrentActiveSpsId, | |
| sDecoderStatistics.iCurrentActivePpsId); | |
| } | |
| } | |
| void CWelsDecoder::BufferingReadyPicture (PWelsDecoderContext pCtx, unsigned char** ppDst, | |
| SBufferInfo* pDstInfo) { | |
| if (pDstInfo->iBufferStatus == 0) { | |
| return; | |
| } | |
| // Publish into the shared reorder queue (m_sPictInfoList / | |
| // m_sReoderingStatus) under m_csDecoder so the caller-side Release* dequeue | |
| // cannot observe or mutate a half-updated slot concurrently. | |
| if (m_iThreadCount >= 1) WelsMutexLock (&m_csDecoder); | |
| m_bIsBaseline = pCtx->pSps->uiProfileIdc == 66 || pCtx->pSps->uiProfileIdc == 83; | |
| if (!m_bIsBaseline) { | |
| if (pCtx->pSliceHeader->eSliceType == B_SLICE) { | |
| m_sReoderingStatus.bHasBSlice = true; | |
| } | |
| } | |
| for (int32_t i = 0; i < 16; ++i) { | |
| if (m_sPictInfoList[i].iPOC == IMinInt32) { | |
| memcpy (&m_sPictInfoList[i].sBufferInfo, pDstInfo, sizeof (SBufferInfo)); | |
| m_sPictInfoList[i].iPOC = pCtx->pSliceHeader->iPicOrderCntLsb; | |
| m_sPictInfoList[i].iSeqNum = pCtx->iSeqNum; | |
| m_sPictInfoList[i].uiDecodingTimeStamp = pCtx->uiDecodingTimeStamp; | |
| if (pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb != NULL) { | |
| PPicture pPrevPic = (GetThreadCount (pCtx) > 1 && pCtx->pThreadCtx != NULL) | |
| ? ((PWelsDecoderThreadCTX)pCtx->pThreadCtx)->pPreviousDecodedPictureInDpb | |
| : pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb; | |
| if (pPrevPic != NULL) { | |
| m_sPictInfoList[i].iPicBuffIdx = pPrevPic->iPicBuffIdx; | |
| if (GetThreadCount (pCtx) <= 1) ++pPrevPic->iRefCount; | |
| } | |
| } | |
| m_iLastBufferedIdx = i; | |
| pDstInfo->iBufferStatus = 0; | |
| ++m_sReoderingStatus.iNumOfPicts; | |
| if (i > m_sReoderingStatus.iLargestBufferedPicIndex) { | |
| m_sReoderingStatus.iLargestBufferedPicIndex = i; | |
| } | |
| break; | |
| } | |
| } | |
| if (m_iThreadCount >= 1) WelsMutexUnlock (&m_csDecoder); | |
| } | |
| void CWelsDecoder::ReleaseBufferedReadyPictureReorder (PWelsDecoderContext pCtx, unsigned char** ppDst, | |
| SBufferInfo* pDstInfo, bool isFlush) { | |
| // Serialize the full dequeue against the worker's | |
| // BufferingReadyPicture() publication on the shared m_sPictInfoList / | |
| // m_sReoderingStatus queue state. | |
| if (m_iThreadCount >= 1) WelsMutexLock (&m_csDecoder); | |
| PPicBuff pPicBuff = pCtx ? pCtx->pPicBuff : m_pPicBuff; | |
| if (pCtx == NULL && m_iThreadCount <= 1) { | |
| pCtx = m_pDecThrCtx[0].pCtx; | |
| } | |
| if (m_sReoderingStatus.iNumOfPicts > 0) { | |
| m_sReoderingStatus.iMinPOC = IMinInt32; | |
| int32_t firstValidIdx = -1; | |
| for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { | |
| if (m_sReoderingStatus.iMinPOC == IMinInt32 && m_sPictInfoList[i].iPOC > IMinInt32) { | |
| m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC; | |
| m_sReoderingStatus.iMinSeqNum = m_sPictInfoList[i].iSeqNum; | |
| m_sReoderingStatus.iPictInfoIndex = i; | |
| firstValidIdx = i; | |
| break; | |
| } | |
| } | |
| for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { | |
| if (i == firstValidIdx) continue; | |
| if (m_sPictInfoList[i].iPOC > IMinInt32 | |
| && ((m_sPictInfoList[i].iSeqNum == m_sReoderingStatus.iMinSeqNum) ? (m_sPictInfoList[i].iPOC < m_sReoderingStatus.iMinPOC) : (m_sPictInfoList[i].iSeqNum - m_sReoderingStatus.iMinSeqNum < 0))) { | |
| m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC; | |
| m_sReoderingStatus.iMinSeqNum = m_sPictInfoList[i].iSeqNum; | |
| m_sReoderingStatus.iPictInfoIndex = i; | |
| } | |
| } | |
| } | |
| if (m_sReoderingStatus.iMinPOC > IMinInt32) { | |
| bool isReady = true; | |
| if (!isFlush) { | |
| int32_t iLastPOC = pCtx != NULL ? pCtx->pSliceHeader->iPicOrderCntLsb : m_sPictInfoList[m_iLastBufferedIdx].iPOC; | |
| int32_t iLastSeqNum = pCtx != NULL ? pCtx->iSeqNum : m_sPictInfoList[m_iLastBufferedIdx].iSeqNum; | |
| isReady = (m_sReoderingStatus.iLastWrittenPOC > IMinInt32 | |
| && m_sReoderingStatus.iMinPOC - m_sReoderingStatus.iLastWrittenPOC <= 1) | |
| || m_sReoderingStatus.iMinPOC < iLastPOC | |
| || m_sReoderingStatus.iMinSeqNum - iLastSeqNum < 0; | |
| } | |
| if (isReady) { | |
| m_sReoderingStatus.iLastWrittenPOC = m_sReoderingStatus.iMinPOC; | |
| m_sReoderingStatus.iLastWrittenSeqNum = m_sReoderingStatus.iMinSeqNum; | |
| #if defined (_DEBUG) | |
| #ifdef _MOTION_VECTOR_DUMP_ | |
| fprintf (stderr, "Output POC: #%d uiDecodingTimeStamp=%d\n", m_sReoderingStatus.iLastWrittenPOC, | |
| m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].uiDecodingTimeStamp); | |
| #endif | |
| #endif | |
| memcpy (pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof (SBufferInfo)); | |
| ppDst[0] = pDstInfo->pDst[0]; | |
| ppDst[1] = pDstInfo->pDst[1]; | |
| ppDst[2] = pDstInfo->pDst[2]; | |
| m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32; | |
| int32_t iPicBuffIdx = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx; | |
| if (pPicBuff != NULL) { | |
| if (iPicBuffIdx >= 0 && iPicBuffIdx < pPicBuff->iCapacity) | |
| { | |
| PPicture pPic = pPicBuff->ppPic[iPicBuffIdx]; | |
| --pPic->iRefCount; | |
| if (pPic->iRefCount <= 0 && pPic->pSetUnRef) | |
| pPic->pSetUnRef(pPic); | |
| } | |
| } | |
| m_sReoderingStatus.iMinPOC = IMinInt32; | |
| --m_sReoderingStatus.iNumOfPicts; | |
| } | |
| } | |
| if (m_iThreadCount >= 1) WelsMutexUnlock (&m_csDecoder); | |
| } | |
| //if there is no b-frame, no ordering based on values of POCs is necessary. | |
| //The function is added to force to avoid picture reordering because some h.264 streams do not follow H.264 POC specifications. | |
| void CWelsDecoder::ReleaseBufferedReadyPictureNoReorder(PWelsDecoderContext pCtx, unsigned char** ppDst, SBufferInfo* pDstInfo) | |
| { | |
| // Serialize the full dequeue against the worker's | |
| // BufferingReadyPicture() publication on the shared m_sPictInfoList / | |
| // m_sReoderingStatus queue state. | |
| if (m_iThreadCount >= 1) WelsMutexLock (&m_csDecoder); | |
| int32_t firstValidIdx = -1; | |
| uint32_t uiDecodingTimeStamp = 0; | |
| for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { | |
| if (m_sPictInfoList[i].iPOC != IMinInt32) { | |
| uiDecodingTimeStamp = m_sPictInfoList[i].uiDecodingTimeStamp; | |
| m_sReoderingStatus.iPictInfoIndex = i; | |
| firstValidIdx = i; | |
| break; | |
| } | |
| } | |
| for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { | |
| if (i == firstValidIdx) continue; | |
| if (m_sPictInfoList[i].iPOC != IMinInt32 && m_sPictInfoList[i].uiDecodingTimeStamp < uiDecodingTimeStamp) { | |
| uiDecodingTimeStamp = m_sPictInfoList[i].uiDecodingTimeStamp; | |
| m_sReoderingStatus.iPictInfoIndex = i; | |
| } | |
| } | |
| if (uiDecodingTimeStamp > 0) { | |
| #if defined (_DEBUG) | |
| #ifdef _MOTION_VECTOR_DUMP_ | |
| fprintf(stderr, "Output POC: #%d uiDecodingTimeStamp=%d\n", m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC, | |
| uiDecodingTimeStamp); | |
| #endif | |
| #endif | |
| m_sReoderingStatus.iLastWrittenPOC = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC; | |
| m_sReoderingStatus.iLastWrittenSeqNum = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iSeqNum; | |
| memcpy(pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof(SBufferInfo)); | |
| ppDst[0] = pDstInfo->pDst[0]; | |
| ppDst[1] = pDstInfo->pDst[1]; | |
| ppDst[2] = pDstInfo->pDst[2]; | |
| m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32; | |
| if (pCtx || m_pPicBuff) { | |
| PPicBuff pPicBuff = pCtx ? pCtx->pPicBuff : m_pPicBuff; | |
| int32_t iPicBuffIdx = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx; | |
| if (pPicBuff != NULL && iPicBuffIdx >= 0 && iPicBuffIdx < pPicBuff->iCapacity) { | |
| PPicture pPic = pPicBuff->ppPic[iPicBuffIdx]; | |
| --pPic->iRefCount; | |
| if (pPic->iRefCount <= 0 && pPic->pSetUnRef) | |
| pPic->pSetUnRef(pPic); | |
| } | |
| } | |
| --m_sReoderingStatus.iNumOfPicts; | |
| } | |
| if (m_iThreadCount >= 1) WelsMutexUnlock (&m_csDecoder); | |
| return; | |
| } | |
| DECODING_STATE CWelsDecoder::ReorderPicturesInDisplay(PWelsDecoderContext pDecContext, unsigned char** ppDst, | |
| SBufferInfo* pDstInfo) { | |
| DECODING_STATE iRet = dsErrorFree; | |
| if (pDecContext->pSps != NULL) { | |
| m_bIsBaseline = pDecContext->pSps->uiProfileIdc == 66 || pDecContext->pSps->uiProfileIdc == 83; | |
| if (!m_bIsBaseline) { | |
| if (pDstInfo->iBufferStatus == 1) { | |
| if (pDecContext->pSliceHeader->eSliceType == B_SLICE && | |
| ((pDecContext->iSeqNum == m_sReoderingStatus.iLastWrittenSeqNum) ? | |
| (pDecContext->pSliceHeader->iPicOrderCntLsb <= m_sReoderingStatus.iLastWrittenPOC + 2) : | |
| (pDecContext->iSeqNum - m_sReoderingStatus.iLastWrittenSeqNum == 1 && pDecContext->pSliceHeader->iPicOrderCntLsb == 0))) { | |
| m_sReoderingStatus.iLastWrittenPOC = pDecContext->pSliceHeader->iPicOrderCntLsb; | |
| m_sReoderingStatus.iLastWrittenSeqNum = pDecContext->iSeqNum; | |
| //issue #3478, use b-slice type to determine correct picture order as the first priority as POC order is not as reliable as based on b-slice | |
| ppDst[0] = pDstInfo->pDst[0]; | |
| ppDst[1] = pDstInfo->pDst[1]; | |
| ppDst[2] = pDstInfo->pDst[2]; | |
| #if defined (_DEBUG) | |
| #ifdef _MOTION_VECTOR_DUMP_ | |
| fprintf (stderr, "Output POC: #%d uiDecodingTimeStamp=%d\n", pDecContext->pSliceHeader->iPicOrderCntLsb, | |
| pDecContext->uiDecodingTimeStamp); | |
| #endif | |
| #endif | |
| return iRet; | |
| } | |
| BufferingReadyPicture(pDecContext, ppDst, pDstInfo); | |
| if (!m_sReoderingStatus.bHasBSlice && m_sReoderingStatus.iNumOfPicts > 1) { | |
| ReleaseBufferedReadyPictureNoReorder (pDecContext, ppDst, pDstInfo); | |
| } | |
| else { | |
| ReleaseBufferedReadyPictureReorder (pDecContext, ppDst, pDstInfo); | |
| } | |
| } | |
| } | |
| } | |
| return iRet; | |
| } | |
| DECODING_STATE CWelsDecoder::DecodeParser (const unsigned char* kpSrc, const int kiSrcLen, SParserBsInfo* pDstInfo) { | |
| PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; | |
| if (pDecContext == NULL || pDecContext->pParam == NULL) { | |
| if (m_pWelsTrace != NULL) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "Call DecodeParser without Initialize.\n"); | |
| } | |
| return dsInitialOptExpected; | |
| } | |
| if (!pDecContext->pParam->bParseOnly) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "bParseOnly should be true for this API calling! \n"); | |
| pDecContext->iErrorCode |= dsInvalidArgument; | |
| return dsInvalidArgument; | |
| } | |
| int64_t iEnd, iStart = WelsTime(); | |
| if (CheckBsBuffer (pDecContext, kiSrcLen)) { | |
| if (ResetDecoder (pDecContext)) | |
| return dsOutOfMemory; | |
| return dsErrorFree; | |
| } | |
| if (kiSrcLen > 0 && kpSrc != NULL) { | |
| #ifdef OUTPUT_BITSTREAM | |
| if (m_pFBS) { | |
| WelsFwrite (kpSrc, sizeof (unsigned char), kiSrcLen, m_pFBS); | |
| WelsFflush (m_pFBS); | |
| } | |
| #endif//OUTPUT_BIT_STREAM | |
| pDecContext->bEndOfStreamFlag = false; | |
| } else { | |
| //For application MODE, the error detection should be added for safe. | |
| //But for CONSOLE MODE, when decoding LAST AU, kiSrcLen==0 && kpSrc==NULL. | |
| pDecContext->bEndOfStreamFlag = true; | |
| pDecContext->bInstantDecFlag = true; | |
| } | |
| pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding. | |
| pDecContext->pParam->eEcActiveIdc = ERROR_CON_DISABLE; //add protection to disable EC here. | |
| pDecContext->iFeedbackNalRefIdc = -1; //initialize | |
| if (!pDecContext->bFramePending) { //frame complete | |
| pDecContext->pParserBsInfo->iNalNum = 0; | |
| memset (pDecContext->pParserBsInfo->pNalLenInByte, 0, MAX_NAL_UNITS_IN_LAYER); | |
| } | |
| pDstInfo->iNalNum = 0; | |
| pDstInfo->iSpsWidthInPixel = pDstInfo->iSpsHeightInPixel = 0; | |
| if (pDstInfo) { | |
| pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp; | |
| pDstInfo->uiOutBsTimeStamp = 0; | |
| } else { | |
| pDecContext->uiTimeStamp = 0; | |
| } | |
| WelsDecodeBs (pDecContext, kpSrc, kiSrcLen, NULL, NULL, pDstInfo); | |
| if (pDecContext->iErrorCode & dsOutOfMemory) { | |
| if (ResetDecoder (pDecContext)) | |
| return dsOutOfMemory; | |
| return dsErrorFree; | |
| } | |
| if (!pDecContext->bFramePending && pDecContext->pParserBsInfo->iNalNum) { | |
| memcpy (pDstInfo, pDecContext->pParserBsInfo, sizeof (SParserBsInfo)); | |
| if (pDecContext->iErrorCode == ERR_NONE) { //update statistics: decoding frame count | |
| pDecContext->pDecoderStatistics->uiDecodedFrameCount++; | |
| if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t | |
| ResetDecStatNums (pDecContext->pDecoderStatistics); | |
| pDecContext->pDecoderStatistics->uiDecodedFrameCount++; | |
| } | |
| } | |
| } | |
| pDecContext->bInstantDecFlag = false; //reset no-delay flag | |
| if (pDecContext->iErrorCode && pDecContext->bPrintFrameErrorTraceFlag) { | |
| WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "decode failed, failure type:%d \n", pDecContext->iErrorCode); | |
| pDecContext->bPrintFrameErrorTraceFlag = false; | |
| } | |
| iEnd = WelsTime(); | |
| pDecContext->dDecTime += (iEnd - iStart) / 1e3; | |
| return (DECODING_STATE)pDecContext->iErrorCode; | |
| } | |
| DECODING_STATE CWelsDecoder::DecodeFrame (const unsigned char* kpSrc, | |
| const int kiSrcLen, | |
| unsigned char** ppDst, | |
| int* pStride, | |
| int& iWidth, | |
| int& iHeight) { | |
| DECODING_STATE eDecState = dsErrorFree; | |
| SBufferInfo DstInfo; | |
| memset (&DstInfo, 0, sizeof (SBufferInfo)); | |
| DstInfo.UsrData.sSystemBuffer.iStride[0] = pStride[0]; | |
| DstInfo.UsrData.sSystemBuffer.iStride[1] = pStride[1]; | |
| DstInfo.UsrData.sSystemBuffer.iWidth = iWidth; | |
| DstInfo.UsrData.sSystemBuffer.iHeight = iHeight; | |
| eDecState = DecodeFrame2 (kpSrc, kiSrcLen, ppDst, &DstInfo); | |
| if (eDecState == dsErrorFree) { | |
| pStride[0] = DstInfo.UsrData.sSystemBuffer.iStride[0]; | |
| pStride[1] = DstInfo.UsrData.sSystemBuffer.iStride[1]; | |
| iWidth = DstInfo.UsrData.sSystemBuffer.iWidth; | |
| iHeight = DstInfo.UsrData.sSystemBuffer.iHeight; | |
| } | |
| return eDecState; | |
| } | |
| DECODING_STATE CWelsDecoder::DecodeFrameEx (const unsigned char* kpSrc, | |
| const int kiSrcLen, | |
| unsigned char* pDst, | |
| int iDstStride, | |
| int& iDstLen, | |
| int& iWidth, | |
| int& iHeight, | |
| int& iColorFormat) { | |
| DECODING_STATE state = dsErrorFree; | |
| return state; | |
| } | |
| DECODING_STATE CWelsDecoder::ParseAccessUnit (SWelsDecoderThreadCTX& sThreadCtx) { | |
| sThreadCtx.pCtx->bHasNewSps = false; | |
| sThreadCtx.pCtx->bParamSetsLostFlag = m_bParamSetsLostFlag; | |
| sThreadCtx.pCtx->bFreezeOutput = m_bFreezeOutput; | |
| sThreadCtx.pCtx->uiDecodingTimeStamp = ++m_uiDecodeTimeStamp; | |
| bool bPicBuffChanged = false; | |
| if (m_pLastDecThrCtx != NULL && sThreadCtx.pCtx->sSpsPpsCtx.iSeqId < m_pLastDecThrCtx->pCtx->sSpsPpsCtx.iSeqId) { | |
| CopySpsPps (m_pLastDecThrCtx->pCtx, sThreadCtx.pCtx); | |
| sThreadCtx.pCtx->iPicQueueNumber = m_pLastDecThrCtx->pCtx->iPicQueueNumber; | |
| if (sThreadCtx.pCtx->pPicBuff != m_pPicBuff) { | |
| bPicBuffChanged = true; | |
| sThreadCtx.pCtx->pPicBuff = m_pPicBuff; | |
| sThreadCtx.pCtx->bHaveGotMemory = m_pPicBuff != NULL; | |
| sThreadCtx.pCtx->iImgWidthInPixel = m_pLastDecThrCtx->pCtx->iImgWidthInPixel; | |
| sThreadCtx.pCtx->iImgHeightInPixel = m_pLastDecThrCtx->pCtx->iImgHeightInPixel; | |
| } | |
| } | |
| //if threadCount > 1, then each thread must contain exact one complete frame. | |
| if (GetThreadCount (sThreadCtx.pCtx) > 1) { | |
| sThreadCtx.pCtx->pAccessUnitList->uiAvailUnitsNum = 0; | |
| sThreadCtx.pCtx->pAccessUnitList->uiActualUnitsNum = 0; | |
| } | |
| int32_t iRet = DecodeFrame2WithCtx (sThreadCtx.pCtx, sThreadCtx.kpSrc, sThreadCtx.kiSrcLen, sThreadCtx.ppDst, | |
| &sThreadCtx.sDstInfo); | |
| int32_t iErr = WelsDecodeInitAccessUnitStart (sThreadCtx.pCtx, &sThreadCtx.sDstInfo); | |
| if (ERR_NONE != iErr) { | |
| return (DECODING_STATE) (iRet | iErr); | |
| } | |
| if (sThreadCtx.pCtx->bNewSeqBegin) { | |
| if (GetThreadCount (sThreadCtx.pCtx) > 1) { | |
| // Wait for older workers before replacing shared DPB storage. | |
| for (int32_t i = 0; i < m_DecCtxActiveCount; ++i) { | |
| if (m_pDecThrCtxActive[i] != NULL && m_pDecThrCtxActive[i] != &sThreadCtx) { | |
| WAIT_SEMAPHORE (&m_pDecThrCtxActive[i]->sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); | |
| RELEASE_SEMAPHORE (&m_pDecThrCtxActive[i]->sThreadInfo.sIsIdle); | |
| } | |
| } | |
| sThreadCtx.pCtx->pLastThreadCtx = NULL; | |
| } | |
| iErr = AllocPicBuffOnNewSeqBegin (sThreadCtx.pCtx); | |
| if (ERR_NONE != iErr) { | |
| return (DECODING_STATE) (iRet | iErr); | |
| } | |
| m_pPicBuff = sThreadCtx.pCtx->pPicBuff; | |
| // Keep sibling contexts from carrying stale DPB references. | |
| for (int32_t i = 0; i < m_iCtxCount; ++i) { | |
| if (&m_pDecThrCtx[i] != &sThreadCtx && m_pDecThrCtx[i].pCtx != NULL) { | |
| m_pDecThrCtx[i].pCtx->pPicBuff = m_pPicBuff; | |
| m_pDecThrCtx[i].pCtx->bHaveGotMemory = sThreadCtx.pCtx->bHaveGotMemory; | |
| m_pDecThrCtx[i].pCtx->iPicQueueNumber = sThreadCtx.pCtx->iPicQueueNumber; | |
| m_pDecThrCtx[i].pCtx->iImgWidthInPixel = sThreadCtx.pCtx->iImgWidthInPixel; | |
| m_pDecThrCtx[i].pCtx->iImgHeightInPixel = sThreadCtx.pCtx->iImgHeightInPixel; | |
| m_pDecThrCtx[i].pCtx->pDec = NULL; | |
| m_pDecThrCtx[i].pCtx->pLastThreadCtx = NULL; | |
| iErr = InitialDqLayersContext (m_pDecThrCtx[i].pCtx, sThreadCtx.pCtx->iImgWidthInPixel, | |
| sThreadCtx.pCtx->iImgHeightInPixel); | |
| if (ERR_NONE != iErr) { | |
| return (DECODING_STATE) (iRet | iErr); | |
| } | |
| } | |
| } | |
| } else if (bPicBuffChanged) { | |
| InitialDqLayersContext (sThreadCtx.pCtx, sThreadCtx.pCtx->pSps->iMbWidth << 4, sThreadCtx.pCtx->pSps->iMbHeight << 4); | |
| } | |
| if (!sThreadCtx.pCtx->bNewSeqBegin && m_pLastDecThrCtx != NULL) { | |
| sThreadCtx.pCtx->sFrameCrop = m_pLastDecThrCtx->pCtx->pSps->sFrameCrop; | |
| } | |
| m_bParamSetsLostFlag = sThreadCtx.pCtx->bNewSeqBegin ? false : sThreadCtx.pCtx->bParamSetsLostFlag; | |
| m_bFreezeOutput = sThreadCtx.pCtx->bNewSeqBegin ? false : sThreadCtx.pCtx->bFreezeOutput; | |
| return (DECODING_STATE) (iRet | iErr); | |
| } | |
| /* | |
| * Run decoding picture in separate thread. | |
| */ | |
| int CWelsDecoder::ThreadDecodeFrameInternal (const unsigned char* kpSrc, const int kiSrcLen, unsigned char** ppDst, | |
| SBufferInfo* pDstInfo) { | |
| int state = dsErrorFree; | |
| int32_t i, j; | |
| int32_t signal = 0; | |
| //serial using of threads | |
| if (m_DecCtxActiveCount < m_iThreadCount) { | |
| signal = m_DecCtxActiveCount; | |
| } else { | |
| signal = m_pDecThrCtxActive[0]->sThreadInfo.uiThrNum; | |
| } | |
| WAIT_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); | |
| for (i = 0; i < m_DecCtxActiveCount; ++i) { | |
| if (m_pDecThrCtxActive[i] == &m_pDecThrCtx[signal]) { | |
| m_pDecThrCtxActive[i] = NULL; | |
| for (j = i; j < m_DecCtxActiveCount - 1; j++) { | |
| m_pDecThrCtxActive[j] = m_pDecThrCtxActive[j + 1]; | |
| m_pDecThrCtxActive[j + 1] = NULL; | |
| } | |
| --m_DecCtxActiveCount; | |
| break; | |
| } | |
| } | |
| if (m_pLastDecThrCtx != NULL) { | |
| m_pDecThrCtx[signal].pCtx->pLastThreadCtx = m_pLastDecThrCtx; | |
| } | |
| m_pDecThrCtx[signal].kpSrc = const_cast<uint8_t*> (kpSrc); | |
| m_pDecThrCtx[signal].kiSrcLen = kiSrcLen; | |
| m_pDecThrCtx[signal].ppDst = ppDst; | |
| memcpy (&m_pDecThrCtx[signal].sDstInfo, pDstInfo, sizeof (SBufferInfo)); | |
| state = ParseAccessUnit (m_pDecThrCtx[signal]); | |
| if (state != dsErrorFree) { | |
| RELEASE_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsIdle); | |
| return state; | |
| } | |
| if (m_iThreadCount > 1 && m_pDecThrCtx[signal].pCtx->pAccessUnitList->uiAvailUnitsNum == 0) { | |
| RELEASE_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsIdle); | |
| return state; | |
| } | |
| m_pDecThrCtxActive[m_DecCtxActiveCount++] = &m_pDecThrCtx[signal]; | |
| if (m_iThreadCount > 1) { | |
| m_pLastDecThrCtx = &m_pDecThrCtx[signal]; | |
| } | |
| m_pDecThrCtx[signal].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_RUN; | |
| RELEASE_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsActivated); | |
| // wait early picture | |
| if (m_DecCtxActiveCount >= m_iThreadCount) { | |
| WAIT_SEMAPHORE (&m_pDecThrCtxActive[0]->sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); | |
| RELEASE_SEMAPHORE (&m_pDecThrCtxActive[0]->sThreadInfo.sIsIdle); | |
| } | |
| return state; | |
| } | |
| } // namespace WelsDec | |
| using namespace WelsDec; | |
| /* | |
| * WelsGetDecoderCapability | |
| * @return: DecCapability information | |
| */ | |
| int WelsGetDecoderCapability (SDecoderCapability* pDecCapability) { | |
| memset (pDecCapability, 0, sizeof (SDecoderCapability)); | |
| pDecCapability->iProfileIdc = 66; //Baseline | |
| pDecCapability->iProfileIop = 0xE0; //11100000b | |
| pDecCapability->iLevelIdc = 32; //level_idc = 3.2 | |
| pDecCapability->iMaxMbps = 216000; //from level_idc = 3.2 | |
| pDecCapability->iMaxFs = 5120; //from level_idc = 3.2 | |
| pDecCapability->iMaxCpb = 20000; //from level_idc = 3.2 | |
| pDecCapability->iMaxDpb = 20480; //from level_idc = 3.2 | |
| pDecCapability->iMaxBr = 20000; //from level_idc = 3.2 | |
| pDecCapability->bRedPicCap = 0; //not support redundant pic | |
| return ERR_NONE; | |
| } | |
| /* WINAPI is indeed in prefix due to sync to application layer callings!! */ | |
| /* | |
| * WelsCreateDecoder | |
| * @return: success in return 0, otherwise failed. | |
| */ | |
| long WelsCreateDecoder (ISVCDecoder** ppDecoder) { | |
| if (NULL == ppDecoder) { | |
| return ERR_INVALID_PARAMETERS; | |
| } | |
| *ppDecoder = new CWelsDecoder(); | |
| if (NULL == *ppDecoder) { | |
| return ERR_MALLOC_FAILED; | |
| } | |
| return ERR_NONE; | |
| } | |
| /* | |
| * WelsDestroyDecoder | |
| */ | |
| void WelsDestroyDecoder (ISVCDecoder* pDecoder) { | |
| if (NULL != pDecoder) { | |
| delete (CWelsDecoder*)pDecoder; | |
| } | |
| } |