| /*! |
| * \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); |
| WAIT_EVENT (&pLastThreadCtx->sSliceDecodeStart, WELS_DEC_THREAD_WAIT_INFINITE); |
| 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) { |
| #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); |
| if (m_sReoderingStatus.iNumOfPicts) { |
| WAIT_EVENT (&m_sBufferingEvent, WELS_DEC_THREAD_WAIT_INFINITE); |
| RESET_EVENT (&m_sBufferingEvent); |
| RESET_EVENT (&m_sReleaseBufferEvent); |
| if (!m_sReoderingStatus.bHasBSlice) { |
| if (m_sReoderingStatus.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; |
| } |
| } |
| } |
| if (bEndOfStreamFlag && m_sReoderingStatus.iNumOfPicts > 0) { |
| if (!m_sReoderingStatus.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; |
| } |
| 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) { |
| m_sPictInfoList[i].iPicBuffIdx = pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb->iPicBuffIdx; |
| if (GetThreadCount (pCtx) <= 1) ++pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb->iRefCount; |
| } |
| m_iLastBufferedIdx = i; |
| pDstInfo->iBufferStatus = 0; |
| ++m_sReoderingStatus.iNumOfPicts; |
| if (i > m_sReoderingStatus.iLargestBufferedPicIndex) { |
| m_sReoderingStatus.iLargestBufferedPicIndex = i; |
| } |
| break; |
| } |
| } |
| } |
| |
| void CWelsDecoder::ReleaseBufferedReadyPictureReorder (PWelsDecoderContext pCtx, unsigned char** ppDst, |
| SBufferInfo* pDstInfo, bool isFlush) { |
| 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 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) |
| { |
| 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; |
| PPicture pPic = pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]; |
| --pPic->iRefCount; |
| if (pPic->iRefCount <= 0 && pPic->pSetUnRef) |
| pPic->pSetUnRef(pPic); |
| } |
| --m_sReoderingStatus.iNumOfPicts; |
| } |
| 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 = InitConstructAccessUnit (sThreadCtx.pCtx, &sThreadCtx.sDstInfo); |
| if (ERR_NONE != iErr) { |
| return (DECODING_STATE) (iRet | iErr); |
| } |
| if (sThreadCtx.pCtx->bNewSeqBegin) { |
| m_pPicBuff = sThreadCtx.pCtx->pPicBuff; |
| } 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)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; |
| } |
| } |
| |
| m_pDecThrCtxActive[m_DecCtxActiveCount++] = &m_pDecThrCtx[signal]; |
| 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)); |
| |
| ParseAccessUnit (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; |
| } |
| } |