diff --git a/codec/api/wels/codec_api.h b/codec/api/wels/codec_api.h index a1326c8f0..03d510bc2 100644 --- a/codec/api/wels/codec_api.h +++ b/codec/api/wels/codec_api.h @@ -45,6 +45,7 @@ typedef unsigned char bool; #include "codec_app_def.h" #include "codec_def.h" +#include #if defined(_WIN32) || defined(__cdecl) #define EXTAPI __cdecl @@ -451,6 +452,43 @@ class ISVCDecoder { int& iHeight, int& iColorFormat) = 0; + /** + * @brief parse bitstream and get motion vectors - skip frame reconstruction + * @param pSrc the h264 stream to be decoded + * @param iSrcLen the length of h264 stream + * @param ppDst buffer pointer of decoded data (YUV) + * @param pDstInfo information provided to API(width, height, etc.) + * @param MotionVectorSize size of the total motion vector for the given P frame. + * @param MotionVectorData Motion vector data.(ex: MotionX, MotionY, Xoffset, Yoffset) + * @return 0 - success; otherwise -failed; + */ + virtual DECODING_STATE EXTAPI ParseBitstreamGetMotionVectors (const unsigned char* kpSrc, + const int kiSrcLen, + unsigned char** ppDst, + SParserBsInfo* pDstInfo, + SBufferInfo* ppDecodeInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData) = 0; + + /** + @brief ParseBitstreamGetMotionVectors is used to parse the encoded bitstream and get the motion vectors for the given P frame. + * If bParseOnly mode is enabled then only motionVectorData and motionVectorSize is updated and ppDst is returned with NULL Value. + * If bParseOnly mode is disabled then along MotionVectorData and motionVectorSize the deocoded YUV buffer is updated in ppDst. + * @param pSrc the h264 stream to be decoded + * @param iSrcLen the length of h264 stream + * @param ppDst buffer pointer of decoded data (YUV) + * @param pDstInfo information provided to API(width, height, etc.) + * @param MotionVectorSize size of the total motion vector for the given P frame. + * @param MotionVectorData Motion vector data.(ex: MotionX, MotionY, Xoffset, Yoffset) + * @return 0 - success; otherwise -failed; + */ + virtual DECODING_STATE EXTAPI DecodeFrameGetMotionVectorsNoDelay (const unsigned char* pSrc, + const int iSrcLen, + unsigned char** ppDst, + SBufferInfo* pDstInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData) = 0; + /** * @brief Set option for decoder, detail option type, please refer to enumurate DECODER_OPTION. * @param pOption option for decoder such as OutDataFormat, Eos Flag, EC method, ... @@ -510,6 +548,21 @@ DECODING_STATE (*DecodeFrameNoDelay) (ISVCDecoder*, const unsigned char* pSrc, unsigned char** ppDst, SBufferInfo* pDstInfo); +DECODING_STATE (*ParseBitstreamGetMotionVectors) (const unsigned char* kpSrc, + const int kiSrcLen, + unsigned char** ppDst, + SParserBsInfo* pDstInfo, + SBufferInfo* ppDecodeInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData); + +DECODING_STATE (*DecodeFrameGetMotionVectorsNoDelay) (const unsigned char* pSrc, + const int iSrcLen, + unsigned char** ppDst, + SBufferInfo* pDstInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData); + DECODING_STATE (*DecodeFrame2) (ISVCDecoder*, const unsigned char* pSrc, const int iSrcLen, unsigned char** ppDst, diff --git a/codec/decoder/core/inc/decoder_context.h b/codec/decoder/core/inc/decoder_context.h index fe825058e..60d9e9fc4 100644 --- a/codec/decoder/core/inc/decoder_context.h +++ b/codec/decoder/core/inc/decoder_context.h @@ -513,6 +513,8 @@ typedef struct TagWelsDecoderContext { CMemoryAlign* pMemAlign; void* pThreadCtx; void* pLastThreadCtx; + int32_t mMotionVectorSize = 0; + int16_t* mMotionVectorData ; WELS_MUTEX* pCsDecoder; int16_t lastReadyHeightOffset[LIST_A][MAX_REF_PIC_COUNT]; //last ready reference MB offset PPictInfo pPictInfoList; diff --git a/codec/decoder/core/inc/parse_mb_syn_cabac.h b/codec/decoder/core/inc/parse_mb_syn_cabac.h index 8f31543ae..a006c403c 100644 --- a/codec/decoder/core/inc/parse_mb_syn_cabac.h +++ b/codec/decoder/core/inc/parse_mb_syn_cabac.h @@ -84,6 +84,7 @@ void UpdateP8x8DirectCabac (PDqLayer pCurDqLayer, int32_t iPartIdx); void UpdateP16x16DirectCabac (PDqLayer pCurDqLayer); void UpdateP8x8RefCacheIdxCabac (int8_t pRefIndex[LIST_A][30], const int16_t& iPartIdx, const int32_t& listIdx, const int8_t& iRef); +void UpdateMotionVector (PWelsDecoderContext pCtx , int16_t pMotionX, int16_t pMotionY, int16_t xOffset, int16_t yOffset); } //#pragma pack() #endif diff --git a/codec/decoder/core/src/decoder_core.cpp b/codec/decoder/core/src/decoder_core.cpp index 0835c1007..9d702c2c6 100644 --- a/codec/decoder/core/src/decoder_core.cpp +++ b/codec/decoder/core/src/decoder_core.cpp @@ -1587,6 +1587,9 @@ int32_t InitialDqLayersContext (PWelsDecoderContext pCtx, const int32_t kiMaxWid pCtx->sMb.pMbRefConcealedFlag[i] = (bool*) pMa->WelsMallocz (pCtx->sMb.iMbWidth * pCtx->sMb.iMbHeight * sizeof ( bool), "pCtx->pMbRefConcealedFlag[]"); + + pCtx->mMotionVectorData = (int16_t*) pMa->WelsMallocz (pCtx->iImgWidthInPixel * pCtx->iImgHeightInPixel * 8 , + "pCtx->pMbRefConcealedFlag[]"); // check memory block valid due above allocated.. WELS_VERIFY_RETURN_IF (ERR_INFO_OUT_OF_MEMORY, @@ -1645,6 +1648,13 @@ void UninitialDqLayersContext (PWelsDecoderContext pCtx) { continue; } + if (pCtx->mMotionVectorData) + { + pCtx->mMotionVectorData -= pCtx->mMotionVectorSize; + pMa->WelsFree (pCtx->mMotionVectorData, "pCtx->mMotionVectorData"); + pCtx->mMotionVectorData = nullptr; + } + if (pCtx->sMb.pMbType[i]) { pMa->WelsFree (pCtx->sMb.pMbType[i], "pCtx->sMb.pMbType[]"); diff --git a/codec/decoder/core/src/parse_mb_syn_cabac.cpp b/codec/decoder/core/src/parse_mb_syn_cabac.cpp index 177a5e82a..a7adc4b6a 100644 --- a/codec/decoder/core/src/parse_mb_syn_cabac.cpp +++ b/codec/decoder/core/src/parse_mb_syn_cabac.cpp @@ -517,6 +517,15 @@ int32_t ParseIntraPredModeChromaCabac (PWelsDecoderContext pCtx, uint8_t uiNeigh return ERR_NONE; } +void UpdateMotionVector (PWelsDecoderContext pCtx , int16_t pMotionX, int16_t pMotionY, int16_t xOffset, int16_t yOffset) { + pCtx->mMotionVectorSize += 4; + pCtx->mMotionVectorData[0] = pMotionX; + pCtx->mMotionVectorData[1] = pMotionY; + pCtx->mMotionVectorData[2] = xOffset; + pCtx->mMotionVectorData[3] = yOffset; + pCtx->mMotionVectorData += 4; +} + int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pNeighAvail, uint8_t* pNonZeroCount, int16_t pMotionVector[LIST_A][30][MV_A], int16_t pMvdCache[LIST_A][30][MV_A], int8_t pRefIndex[LIST_A][30]) { PSlice pSlice = &pCtx->pCurDqLayer->sLayerInfo.sSliceInLayer; @@ -535,6 +544,10 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN pRefCount[0] = pSliceHeader->uiRefCount[0]; pRefCount[1] = pSliceHeader->uiRefCount[1]; + int16_t iMBOffsetX = pCurDqLayer->iMbX << 4; + int16_t iMBOffsetY = pCurDqLayer->iMbY << 4; + int16_t iBlk8X, iBlk8Y, iBlk4X, iBlk4Y; + bool bIsPending = GetThreadCount (pCtx) > 1; switch (pCurDqLayer->pDec->pMbType[iMbXy]) { @@ -559,6 +572,7 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN pMv[0] += pMvd[0]; pMv[1] += pMvd[1]; WELS_CHECK_SE_BOTH_WARNING (pMv[1], iMinVmv, iMaxVmv, "vertical mv"); + UpdateMotionVector(pCtx, pMv[0], pMv[1], iMBOffsetX, iMBOffsetY); UpdateP16x16MotionInfo (pCurDqLayer, LIST_0, iRef[0], pMv); UpdateP16x16MvdCabac (pCurDqLayer, pMvd, LIST_0); } @@ -589,6 +603,15 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN pMv[0] += pMvd[0]; pMv[1] += pMvd[1]; WELS_CHECK_SE_BOTH_WARNING (pMv[1], iMinVmv, iMaxVmv, "vertical mv"); + if(!i) + { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iMBOffsetX, iMBOffsetY); + } + else + { + // comes after first 16x8 block - so add 8 to Y offset + UpdateMotionVector(pCtx, pMv[0], pMv[1], iMBOffsetX, iMBOffsetY + 8); + } UpdateP16x8MotionInfo (pCurDqLayer, pMotionVector, pRefIndex, LIST_0, iPartIdx, iRef[i], pMv); UpdateP16x8MvdCabac (pCurDqLayer, pMvdCache, iPartIdx, pMvd, LIST_0); } @@ -620,6 +643,14 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN pMv[0] += pMvd[0]; pMv[1] += pMvd[1]; WELS_CHECK_SE_BOTH_WARNING (pMv[1], iMinVmv, iMaxVmv, "vertical mv"); + if(i) + { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iMBOffsetX + 8, iMBOffsetY); + } + else + { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iMBOffsetX, iMBOffsetY); + } UpdateP8x16MotionInfo (pCurDqLayer, pMotionVector, pRefIndex, LIST_0, iPartIdx, iRef[i], pMv); UpdateP8x16MvdCabac (pCurDqLayer, pMvdCache, iPartIdx, pMvd, LIST_0); } @@ -661,6 +692,11 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN } //mv for (i = 0; i < 4; i++) { + int32_t iXOffset, iYOffset; + iBlk8X = (i & 1) << 3; + iBlk8Y = (i >> 1) << 3; + iXOffset = iMBOffsetX + iBlk8X; + iYOffset = iMBOffsetY + iBlk8Y; int8_t iPartCount = pSubPartCount[i]; uiSubMbType = pCurDqLayer->pSubMbType[iMbXy][i]; int16_t iPartIdx, iBlockW = pPartW[i]; @@ -680,6 +716,7 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN pMv[1] += pMvd[1]; WELS_CHECK_SE_BOTH_WARNING (pMv[1], iMinVmv, iMaxVmv, "vertical mv"); if (SUB_MB_TYPE_8x8 == uiSubMbType) { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iXOffset, iYOffset); ST32 ((pMv + 2), LD32 (pMv)); ST32 ((pMvd + 2), LD32 (pMvd)); ST64 (pCurDqLayer->pDec->pMv[0][iMbXy][iScan4Idx], LD64 (pMv)); @@ -691,6 +728,14 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN ST64 (pMvdCache[0][iCacheIdx ], LD64 (pMvd)); ST64 (pMvdCache[0][iCacheIdx + 6], LD64 (pMvd)); } else if (SUB_MB_TYPE_8x4 == uiSubMbType) { + if(j) + { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iXOffset, iYOffset + 4); + } + else + { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iXOffset, iYOffset); + } ST32 ((pMv + 2), LD32 (pMv)); ST32 ((pMvd + 2), LD32 (pMvd)); ST64 (pCurDqLayer->pDec->pMv[0][iMbXy][iScan4Idx ], LD64 (pMv)); @@ -698,6 +743,14 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN ST64 (pMotionVector[0][iCacheIdx ], LD64 (pMv)); ST64 (pMvdCache[0][iCacheIdx ], LD64 (pMvd)); } else if (SUB_MB_TYPE_4x8 == uiSubMbType) { + if(j) + { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iXOffset + 4, iYOffset); + } + else + { + UpdateMotionVector(pCtx, pMv[0], pMv[1], iXOffset, iYOffset); + } ST32 (pCurDqLayer->pDec->pMv[0][iMbXy][iScan4Idx ], LD32 (pMv)); ST32 (pCurDqLayer->pDec->pMv[0][iMbXy][iScan4Idx + 4], LD32 (pMv)); ST32 (pCurDqLayer->pMvd[0][iMbXy][iScan4Idx ], LD32 (pMvd)); @@ -707,6 +760,9 @@ int32_t ParseInterPMotionInfoCabac (PWelsDecoderContext pCtx, PWelsNeighAvail pN ST32 (pMvdCache[0][iCacheIdx ], LD32 (pMvd)); ST32 (pMvdCache[0][iCacheIdx + 6], LD32 (pMvd)); } else { //SUB_MB_TYPE_4x4 + iBlk4X = (j & 1) << 2; + iBlk4Y = (j >> 1) << 2; + UpdateMotionVector(pCtx, pMv[0], pMv[1], iXOffset + iBlk4X, iYOffset + iBlk4Y); ST32 (pCurDqLayer->pDec->pMv[0][iMbXy][iScan4Idx ], LD32 (pMv)); ST32 (pCurDqLayer->pMvd[0][iMbXy][iScan4Idx ], LD32 (pMvd)); ST32 (pMotionVector[0][iCacheIdx ], LD32 (pMv)); diff --git a/codec/decoder/plus/inc/welsDecoderExt.h b/codec/decoder/plus/inc/welsDecoderExt.h index f5abac3fe..a50962880 100644 --- a/codec/decoder/plus/inc/welsDecoderExt.h +++ b/codec/decoder/plus/inc/welsDecoderExt.h @@ -91,6 +91,21 @@ class CWelsDecoder : public ISVCDecoder { unsigned char** ppDst, SBufferInfo* pDstInfo); + virtual DECODING_STATE EXTAPI ParseBitstreamGetMotionVectors (const unsigned char* kpSrc, + const int kiSrcLen, + unsigned char** ppDst, + SParserBsInfo* pDstInfo, + SBufferInfo* ppDecodeInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData); + + virtual DECODING_STATE EXTAPI DecodeFrameGetMotionVectorsNoDelay (const unsigned char* pSrc, + const int iSrcLen, + unsigned char** ppDst, + SBufferInfo* pDstInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData); + virtual DECODING_STATE EXTAPI FlushFrame (unsigned char** ppDst, SBufferInfo* pDstInfo); diff --git a/codec/decoder/plus/src/welsDecoderExt.cpp b/codec/decoder/plus/src/welsDecoderExt.cpp index 08f2a2743..ff09be0c4 100644 --- a/codec/decoder/plus/src/welsDecoderExt.cpp +++ b/codec/decoder/plus/src/welsDecoderExt.cpp @@ -69,6 +69,7 @@ extern "C" { #include #include #include +#include #else #include #endif @@ -920,7 +921,151 @@ DECODING_STATE CWelsDecoder::DecodeFrame2 (const unsigned char* kpSrc, unsigned char** ppDst, SBufferInfo* pDstInfo) { PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; - return DecodeFrame2WithCtx (pDecContext, kpSrc, kiSrcLen, ppDst, pDstInfo); + auto state = DecodeFrame2WithCtx (pDecContext, kpSrc, kiSrcLen, ppDst, pDstInfo); + pDecContext->mMotionVectorData -= pDecContext->mMotionVectorSize; + pDecContext->mMotionVectorSize = 0; + return state; +} + +DECODING_STATE CWelsDecoder::ParseBitstreamGetMotionVectors (const unsigned char* kpSrc, + const int kiSrcLen, + unsigned char** ppDst, + SParserBsInfo* pDstInfo, + SBufferInfo* ppDecodeInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData) { + + 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, ppDst, ppDecodeInfo, pDstInfo); + + if(pDecContext->mMotionVectorSize) + { + *motionVectorSize = pDecContext->mMotionVectorSize; + pDecContext->mMotionVectorData -= pDecContext->mMotionVectorSize; + memcpy(*motionVectorData, pDecContext->mMotionVectorData, pDecContext->mMotionVectorSize * sizeof(int16_t)); + pDecContext->mMotionVectorSize = 0; + } + + 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::DecodeFrameGetMotionVectorsNoDelay (const unsigned char* kpSrc, + const int kiSrcLen, + unsigned char** ppDst, + SBufferInfo* pDstInfo, + int32_t* motionVectorSize, + int16_t** motionVectorData) { + int iRet = dsErrorFree; + if (m_iThreadCount >= 1) { + iRet = ThreadDecodeFrameInternal (kpSrc, kiSrcLen, ppDst, pDstInfo); + if (m_sReoderingStatus.iNumOfPicts) { + WAIT_EVENT (&m_sBufferingEvent, WELS_DEC_THREAD_WAIT_INFINITE); + RESET_EVENT (&m_sReleaseBufferEvent); + if (!m_sReoderingStatus.bHasBSlice) { + if (m_sReoderingStatus.iNumOfPicts > 1) { + ReleaseBufferedReadyPictureNoReorder (NULL, ppDst, pDstInfo); + } + } + else { + ReleaseBufferedReadyPictureReorder (NULL, ppDst, pDstInfo); + } + SET_EVENT(&m_sReleaseBufferEvent); + } + return (DECODING_STATE)iRet; + } + PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; + //pDecContext->pDstInfo = pDstInfo; + iRet = DecodeFrame2WithCtx (pDecContext, kpSrc, kiSrcLen, ppDst, pDstInfo); + + pDecContext = m_pDecThrCtx[0].pCtx; + //pDecContext->pDstInfo = pDstInfo; + iRet |= DecodeFrame2WithCtx (pDecContext, NULL, 0, ppDst, pDstInfo); + + if(pDecContext->mMotionVectorSize) + { + *motionVectorSize = pDecContext->mMotionVectorSize; + pDecContext->mMotionVectorData -= pDecContext->mMotionVectorSize; + memcpy(*motionVectorData, pDecContext->mMotionVectorData, pDecContext->mMotionVectorSize * sizeof(int16_t)); + pDecContext->mMotionVectorSize = 0; + } + + return (DECODING_STATE)iRet; } DECODING_STATE CWelsDecoder::FlushFrame (unsigned char** ppDst, diff --git a/test/api/cpp_interface_test.cpp b/test/api/cpp_interface_test.cpp index ec39a3fc6..a78f4a61f 100644 --- a/test/api/cpp_interface_test.cpp +++ b/test/api/cpp_interface_test.cpp @@ -82,6 +82,18 @@ struct SVCDecoderImpl : public ISVCDecoder { EXPECT_TRUE (gThis == this); return static_cast (4); } + virtual DECODING_STATE EXTAPI ParseBitstreamGetMotionVectors (const unsigned char* kpSrc, + const int kiSrcLen, unsigned char** ppDst, SParserBsInfo* pDstInfo, SBufferInfo* ppDecodeInfo, int32_t* motionVectorSize, + int16_t** motionVectorData) { + EXPECT_TRUE (gThis == this); + return static_cast (11); + } + virtual DECODING_STATE EXTAPI DecodeFrameGetMotionVectorsNoDelay (const unsigned char* pSrc, + const int iSrcLen, unsigned char** ppDst, SBufferInfo* pDstInfo,int32_t* motionVectorSize, + int16_t** motionVectorData) { + EXPECT_TRUE (gThis == this); + return static_cast (12); + } virtual DECODING_STATE EXTAPI DecodeFrame2 (const unsigned char* pSrc, const int iSrcLen, unsigned char** ppDst, SBufferInfo* pDstInfo) { EXPECT_TRUE (gThis == this);