/*! * \copy * Copyright (c) 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. * */ #include "wels_preprocess.h"#include "picture_handle.h"#include "encoder_context.h"#include "utils.h"#include "encoder.h" namespace { void ClearEndOfLinePadding (uint8_t* pData, int32_t iStride, int32_t iWidth, int32_t iHeight) { if (iWidth < iStride) { for (int32_t i = 0; i < iHeight; ++i) memset (pData + i * iStride + iWidth, 0, iStride - iWidth); }} } // anon ns. namespace WelsEnc { //***** entry API declaration ************************************************************************// int32_t WelsInitScaledPic (SWelsSvcCodingParam* pParam, Scaled_Picture* pScaledPic, CMemoryAlign* pMemoryAlign);bool JudgeNeedOfScaling (SWelsSvcCodingParam* pParam, Scaled_Picture* pScaledPic);void FreeScaledPic (Scaled_Picture* pScaledPic, CMemoryAlign* pMemoryAlign);void WelsMoveMemory_c (uint8_t* pDstY, uint8_t* pDstU, uint8_t* pDstV, int32_t iDstStrideY, int32_t iDstStrideUV, uint8_t* pSrcY, uint8_t* pSrcU, uint8_t* pSrcV, int32_t iSrcStrideY, int32_t iSrcStrideUV, int32_t iWidth, int32_t iHeight); //******* table definition ***********************************************************************//const uint8_t g_kuiRefTemporalIdx[MAX_TEMPORAL_LEVEL][MAX_GOP_SIZE] = { { 0, }, // 0 { 0, 0, }, // 1 { 0, 0, 0, 1, }, // 2 { 0, 0, 0, 2, 0, 1, 1, 2, }, // 3}; const int32_t g_kiPixMapSizeInBits = sizeof (uint8_t) * 8; inline void WelsUpdateSpatialIdxMap (sWelsEncCtx* pEncCtx, const int32_t iPos, SPicture* const pPic, const int32_t iDidx) { pEncCtx->sSpatialIndexMap[iPos].pSrc = pPic; pEncCtx->sSpatialIndexMap[iPos].iDid = iDidx;} /***************************************************************************** implement of the interface****************************************************************************/ CWelsPreProcess* CWelsPreProcess::CreatePreProcess (sWelsEncCtx* pEncCtx) { CWelsPreProcess* pPreProcess = NULL; switch (pEncCtx->pSvcParam->iUsageType) { case SCREEN_CONTENT_REAL_TIME: pPreProcess = WELS_NEW_OP (CWelsPreProcessScreen (pEncCtx), CWelsPreProcessScreen); break; default: pPreProcess = WELS_NEW_OP (CWelsPreProcessVideo (pEncCtx), CWelsPreProcessVideo); break; } WELS_VERIFY_RETURN_IF (NULL, NULL == pPreProcess) return pPreProcess;} CWelsPreProcess::CWelsPreProcess (sWelsEncCtx* pEncCtx) { m_pInterfaceVp = NULL; m_bInitDone = false; m_pEncCtx = pEncCtx; memset (&m_sScaledPicture, 0, sizeof (m_sScaledPicture)); memset (m_pSpatialPic, 0, sizeof (m_pSpatialPic)); memset (m_uiSpatialLayersInTemporal, 0, sizeof (m_uiSpatialLayersInTemporal)); memset (m_uiSpatialPicNum, 0, sizeof (m_uiSpatialPicNum));} CWelsPreProcess::~CWelsPreProcess() { FreeScaledPic (&m_sScaledPicture, m_pEncCtx->pMemAlign); WelsPreprocessDestroy();} int32_t CWelsPreProcess::WelsPreprocessCreate() { if (m_pInterfaceVp == NULL) { WelsCreateVpInterface ((void**) &m_pInterfaceVp, WELSVP_INTERFACE_VERION); if (!m_pInterfaceVp) goto exit; } else goto exit; return 0; exit: WelsPreprocessDestroy(); return 1;} int32_t CWelsPreProcess::WelsPreprocessDestroy() { WelsDestroyVpInterface (m_pInterfaceVp, WELSVP_INTERFACE_VERION); m_pInterfaceVp = NULL; return 0;} int32_t CWelsPreProcess::WelsPreprocessReset (sWelsEncCtx* pCtx, int32_t iWidth, int32_t iHeight) { int32_t iRet = -1; SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; //init source width and height pSvcParam->SUsedPicRect.iLeft = 0; pSvcParam->SUsedPicRect.iTop = 0; pSvcParam->SUsedPicRect.iWidth = iWidth; pSvcParam->SUsedPicRect.iHeight = iHeight; if ((iWidth < 16) || ((iHeight < 16))) { WelsLog (& (pCtx->sLogCtx), WELS_LOG_ERROR, "Don't support width(%d) or height(%d) which is less than 16 ", iWidth, iHeight); return iRet; } if (pCtx) { FreeScaledPic (&m_sScaledPicture, pCtx->pMemAlign); iRet = InitLastSpatialPictures (pCtx); iRet = WelsInitScaledPic (pCtx->pSvcParam, &m_sScaledPicture, pCtx->pMemAlign); } return iRet;} int32_t CWelsPreProcess::AllocSpatialPictures (sWelsEncCtx* pCtx, SWelsSvcCodingParam* pParam) { CMemoryAlign* pMa = pCtx->pMemAlign; const int32_t kiDlayerCount = pParam->iSpatialLayerNum; int32_t iDlayerIndex = 0; // spatial pictures iDlayerIndex = 0; do { const int32_t kiPicWidth = pParam->sSpatialLayers[iDlayerIndex].iVideoWidth; const int32_t kiPicHeight = pParam->sSpatialLayers[iDlayerIndex].iVideoHeight; const uint8_t kuiLayerInTemporal = 2 + WELS_MAX (pParam->sDependencyLayers[iDlayerIndex].iHighestTemporalId, 1); const uint8_t kuiRefNumInTemporal = kuiLayerInTemporal + pParam->iLTRRefNum; uint8_t i = 0; m_uiSpatialPicNum[iDlayerIndex] = kuiRefNumInTemporal; do { SPicture* pPic = AllocPicture (pMa, kiPicWidth, kiPicHeight, false, 0); WELS_VERIFY_RETURN_IF (1, (NULL == pPic)) m_pSpatialPic[iDlayerIndex][i] = pPic; ++ i; } while (i < kuiRefNumInTemporal); if (pParam->iUsageType == SCREEN_CONTENT_REAL_TIME) m_uiSpatialLayersInTemporal[iDlayerIndex] = 1; else m_uiSpatialLayersInTemporal[iDlayerIndex] = kuiLayerInTemporal; ++ iDlayerIndex; } while (iDlayerIndex < kiDlayerCount); return 0;} void CWelsPreProcess::FreeSpatialPictures (sWelsEncCtx* pCtx) { CMemoryAlign* pMa = pCtx->pMemAlign; int32_t j = 0; while (j < pCtx->pSvcParam->iSpatialLayerNum) { uint8_t i = 0; uint8_t uiRefNumInTemporal = m_uiSpatialPicNum[j]; while (i < uiRefNumInTemporal) { if (NULL != m_pSpatialPic[j][i]) { FreePicture (pMa, &m_pSpatialPic[j][i]); } ++ i; } m_uiSpatialLayersInTemporal[j] = 0; ++ j; }} int32_t CWelsPreProcess::BuildSpatialPicList (sWelsEncCtx* pCtx, const SSourcePicture* kpSrcPic) { SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; int32_t iSpatialNum = 0; int32_t iWidth = ((kpSrcPic->iPicWidth >> 1) << 1); int32_t iHeight = ((kpSrcPic->iPicHeight >> 1) << 1); if (!m_bInitDone) { if (WelsPreprocessCreate() != 0) return -1; if (WelsPreprocessReset (pCtx, iWidth, iHeight) != 0) return -1; m_iAvaliableRefInSpatialPicList = pSvcParam->iNumRefFrame; m_bInitDone = true; } else { if ((iWidth != pSvcParam->SUsedPicRect.iWidth) || (iHeight != pSvcParam->SUsedPicRect.iHeight)) { if (WelsPreprocessReset (pCtx, iWidth, iHeight) != 0) return -1; } } if (m_pInterfaceVp == NULL) return -1; pCtx->pVaa->bSceneChangeFlag = pCtx->pVaa->bIdrPeriodFlag = false; iSpatialNum = SingleLayerPreprocess (pCtx, kpSrcPic, &m_sScaledPicture); return iSpatialNum;} SPicture* CWelsPreProcess::GetBestRefPic (EUsageType iUsageType, bool bSceneLtr, EWelsSliceType eSliceType, int32_t kiDidx, int32_t iRefTemporalIdx) { assert (iUsageType == SCREEN_CONTENT_REAL_TIME); SVAAFrameInfoExt* pVaaExt = static_cast<SVAAFrameInfoExt*> (m_pEncCtx->pVaa); SRefInfoParam* BestRefCandidateParam = (bSceneLtr) ? (& (pVaaExt->sVaaLtrBestRefCandidate[0])) : (& (pVaaExt->sVaaStrBestRefCandidate[0])); return m_pSpatialPic[0][BestRefCandidateParam->iSrcListIdx]; }SPicture* CWelsPreProcess::GetBestRefPic (const int32_t kiDidx, const int32_t iRefTemporalIdx) { return m_pSpatialPic[kiDidx][iRefTemporalIdx];} int32_t CWelsPreProcess::AnalyzeSpatialPic (sWelsEncCtx* pCtx, const int32_t kiDidx) { SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; bool bNeededMbAq = (pSvcParam->bEnableAdaptiveQuant && (pCtx->eSliceType == P_SLICE)); bool bCalculateBGD = (pCtx->eSliceType == P_SLICE && pSvcParam->bEnableBackgroundDetection); SSpatialLayerInternal* pParamInternal = &pSvcParam->sDependencyLayers[kiDidx]; int32_t iCurTemporalIdx = m_uiSpatialLayersInTemporal[kiDidx] - 1; int32_t iRefTemporalIdx = (int32_t)g_kuiRefTemporalIdx[pSvcParam->iDecompStages][pParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; if (pCtx->uiTemporalId == 0 && pCtx->pLtr[pCtx->uiDependencyId].bReceivedT0LostFlag) iRefTemporalIdx = m_uiSpatialLayersInTemporal[kiDidx] + pCtx->pVaa->uiValidLongTermPicIdx; SPicture* pCurPic = m_pSpatialPic[kiDidx][iCurTemporalIdx]; bool bCalculateVar = (pSvcParam->iRCMode >= RC_BITRATE_MODE && pCtx->eSliceType == I_SLICE); if (pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) { SPicture* pRefPic = GetBestRefPic (pSvcParam->iUsageType, pCtx->bCurFrameMarkedAsSceneLtr, pCtx->eSliceType, kiDidx, iRefTemporalIdx); VaaCalculation (pCtx->pVaa, pCurPic, pRefPic, false, bCalculateVar, bCalculateBGD); if (pSvcParam->bEnableBackgroundDetection) { BackgroundDetection (pCtx->pVaa, pCurPic, pRefPic, bCalculateBGD && pRefPic->iPictureType != I_SLICE); } if (bNeededMbAq) { AdaptiveQuantCalculation (pCtx->pVaa, pCurPic, pRefPic); } } else { SPicture* pRefPic = GetBestRefPic (kiDidx, iRefTemporalIdx); SPicture* pLastPic = m_pLastSpatialPicture[kiDidx][0]; bool bCalculateSQDiff = ((pLastPic->pData[0] == pRefPic->pData[0]) && bNeededMbAq); VaaCalculation (pCtx->pVaa, pCurPic, pRefPic, bCalculateSQDiff, bCalculateVar, bCalculateBGD); if (pSvcParam->bEnableBackgroundDetection) { BackgroundDetection (pCtx->pVaa, pCurPic, pRefPic, bCalculateBGD && pRefPic->iPictureType != I_SLICE); } if (bNeededMbAq) { AdaptiveQuantCalculation (pCtx->pVaa, m_pLastSpatialPicture[kiDidx][1], m_pLastSpatialPicture[kiDidx][0]); } } return 0;} int32_t CWelsPreProcess::GetCurPicPosition (const int32_t kiDidx) { return (m_uiSpatialLayersInTemporal[kiDidx] - 1);} int32_t CWelsPreProcess::UpdateSpatialPictures (sWelsEncCtx* pCtx, SWelsSvcCodingParam* pParam, const int8_t iCurTid, const int32_t kiDidx) { if (pCtx->pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) return 0; WelsExchangeSpatialPictures (&m_pLastSpatialPicture[kiDidx][1], &m_pLastSpatialPicture[kiDidx][0]); const int32_t kiCurPos = GetCurPicPosition (kiDidx); if (iCurTid < kiCurPos || pParam->iDecompStages == 0) { if ((iCurTid >= MAX_TEMPORAL_LEVEL) || (kiCurPos > MAX_TEMPORAL_LEVEL)) { InitLastSpatialPictures (pCtx); return 1; } if (pCtx->bRefOfCurTidIsLtr[kiDidx][iCurTid]) { const int32_t kiAvailableLtrPos = m_uiSpatialLayersInTemporal[kiDidx] + pCtx->pVaa->uiMarkLongTermPicIdx; WelsExchangeSpatialPictures (&m_pSpatialPic[kiDidx][kiAvailableLtrPos], &m_pSpatialPic[kiDidx][iCurTid]); pCtx->bRefOfCurTidIsLtr[kiDidx][iCurTid] = false; } WelsExchangeSpatialPictures (&m_pSpatialPic[kiDidx][kiCurPos], &m_pSpatialPic[kiDidx][iCurTid]); } return 0;} /* * SingleLayerPreprocess: down sampling if applicable * @return: exact number of spatial layers need to encoder indeed */int32_t CWelsPreProcess::SingleLayerPreprocess (sWelsEncCtx* pCtx, const SSourcePicture* kpSrc, Scaled_Picture* pScaledPicture) { SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; int8_t iDependencyId = pSvcParam->iSpatialLayerNum - 1; SPicture* pSrcPic = NULL; // large SPicture* pDstPic = NULL; // small SSpatialLayerConfig* pDlayerParam = NULL; SSpatialLayerInternal* pDlayerParamInternal = NULL; int32_t iSpatialNum = 0; int32_t iSrcWidth = 0; int32_t iSrcHeight = 0; int32_t iTargetWidth = 0; int32_t iTargetHeight = 0; int32_t iTemporalId = 0; int32_t iClosestDid = iDependencyId; pDlayerParamInternal = &pSvcParam->sDependencyLayers[iDependencyId]; pDlayerParam = &pSvcParam->sSpatialLayers[iDependencyId]; iTargetWidth = pDlayerParam->iVideoWidth; iTargetHeight = pDlayerParam->iVideoHeight; iSrcWidth = pSvcParam->SUsedPicRect.iWidth; iSrcHeight = pSvcParam->SUsedPicRect.iHeight; if (pSvcParam->uiIntraPeriod) pCtx->pVaa->bIdrPeriodFlag = (1 + pDlayerParamInternal->iFrameIndex >= (int32_t)pSvcParam->uiIntraPeriod) ? true : false; pSrcPic = pScaledPicture->pScaledInputPicture ? pScaledPicture->pScaledInputPicture : GetCurrentOrigFrame ( iDependencyId); WelsMoveMemoryWrapper (pSvcParam, pSrcPic, kpSrc, iSrcWidth, iSrcHeight); if (pSvcParam->bEnableDenoise) BilateralDenoising (pSrcPic, iSrcWidth, iSrcHeight); // different scaling in between input picture and dst highest spatial picture. int32_t iShrinkWidth = iSrcWidth; int32_t iShrinkHeight = iSrcHeight; pDstPic = pSrcPic; if (pScaledPicture->pScaledInputPicture) { // for highest downsampling pDstPic = GetCurrentOrigFrame (iDependencyId); iShrinkWidth = pScaledPicture->iScaledWidth[iDependencyId]; iShrinkHeight = pScaledPicture->iScaledHeight[iDependencyId]; } DownsamplePadding (pSrcPic, pDstPic, iSrcWidth, iSrcHeight, iShrinkWidth, iShrinkHeight, iTargetWidth, iTargetHeight, false); if (pSvcParam->bEnableSceneChangeDetect && !pCtx->pVaa->bIdrPeriodFlag) { if (pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) { pCtx->pVaa->eSceneChangeIdc = (pDlayerParamInternal->bEncCurFrmAsIdrFlag ? LARGE_CHANGED_SCENE : DetectSceneChange (pDstPic)); pCtx->pVaa->bSceneChangeFlag = (LARGE_CHANGED_SCENE == pCtx->pVaa->eSceneChangeIdc); } else { if ((!pDlayerParamInternal->bEncCurFrmAsIdrFlag) && ! (pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1))) { SPicture* pRefPic = pCtx->pLtr[iDependencyId].bReceivedT0LostFlag ? m_pSpatialPic[iDependencyId][m_uiSpatialLayersInTemporal[iDependencyId] + pCtx->pVaa->uiValidLongTermPicIdx] : m_pLastSpatialPicture[iDependencyId][0]; //pCtx->pVaa->eSceneChangeIdc = DetectSceneChange (pDstPic, pRefPic); pCtx->pVaa->bSceneChangeFlag = GetSceneChangeFlag (DetectSceneChange (pDstPic, pRefPic)); } } } for (int32_t i = 0; i < pSvcParam->iSpatialLayerNum; i++) { pDlayerParamInternal = &pSvcParam->sDependencyLayers[i]; iTemporalId = pDlayerParamInternal->uiCodingIdx2TemporalId[pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; if (iTemporalId != INVALID_TEMPORAL_ID) { ++ iSpatialNum; } } pDlayerParamInternal = &pSvcParam->sDependencyLayers[iDependencyId]; iTemporalId = pDlayerParamInternal->uiCodingIdx2TemporalId[pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; int iActualSpatialNum = iSpatialNum - 1; if (iTemporalId != INVALID_TEMPORAL_ID) { WelsUpdateSpatialIdxMap (pCtx, iActualSpatialNum, pDstPic, iDependencyId); -- iActualSpatialNum; } m_pLastSpatialPicture[iDependencyId][1] = GetCurrentOrigFrame (iDependencyId); -- iDependencyId; // generate other spacial layer // pSrc is // -- padded input pic, if downsample should be applied to generate highest layer, [if] block above // -- highest layer, if no downsampling, [else] block above if (pSvcParam->iSpatialLayerNum > 1) { while (iDependencyId >= 0) { pDlayerParamInternal = &pSvcParam->sDependencyLayers[iDependencyId]; pDlayerParam = &pSvcParam->sSpatialLayers[iDependencyId]; SPicture* pSrcPic = m_pLastSpatialPicture[iClosestDid][1]; // large iTargetWidth = pDlayerParam->iVideoWidth; iTargetHeight = pDlayerParam->iVideoHeight; iTemporalId = pDlayerParamInternal->uiCodingIdx2TemporalId[pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; // down sampling performed int32_t iSrcWidth = pScaledPicture->iScaledWidth[iClosestDid]; int32_t iSrcHeight = pScaledPicture->iScaledHeight[iClosestDid]; pDstPic = GetCurrentOrigFrame (iDependencyId); // small iShrinkWidth = pScaledPicture->iScaledWidth[iDependencyId]; iShrinkHeight = pScaledPicture->iScaledHeight[iDependencyId]; DownsamplePadding (pSrcPic, pDstPic, iSrcWidth, iSrcHeight, iShrinkWidth, iShrinkHeight, iTargetWidth, iTargetHeight, true); if ((iTemporalId != INVALID_TEMPORAL_ID)) { WelsUpdateSpatialIdxMap (pCtx, iActualSpatialNum, pDstPic, iDependencyId); iActualSpatialNum--; } m_pLastSpatialPicture[iDependencyId][1] = pDstPic; iClosestDid = iDependencyId; -- iDependencyId; } } return iSpatialNum; } /*! * \brief Whether input picture need be scaled? */bool JudgeNeedOfScaling (SWelsSvcCodingParam* pParam, Scaled_Picture* pScaledPicture) { const int32_t kiInputPicWidth = pParam->SUsedPicRect.iWidth; const int32_t kiInputPicHeight = pParam->SUsedPicRect.iHeight; const int32_t kiDstPicWidth = pParam->sDependencyLayers[pParam->iSpatialLayerNum - 1].iActualWidth; const int32_t kiDstPicHeight = pParam->sDependencyLayers[pParam->iSpatialLayerNum - 1].iActualHeight; bool bNeedDownsampling = true; int32_t iSpatialIdx = pParam->iSpatialLayerNum - 1; if (kiDstPicWidth >= kiInputPicWidth && kiDstPicHeight >= kiInputPicHeight) { bNeedDownsampling = false; } for (; iSpatialIdx >= 0; iSpatialIdx --) { SSpatialLayerInternal* pCurLayer = &pParam->sDependencyLayers[iSpatialIdx]; int32_t iCurDstWidth = pCurLayer->iActualWidth; int32_t iCurDstHeight = pCurLayer->iActualHeight; int32_t iInputWidthXDstHeight = kiInputPicWidth * iCurDstHeight; int32_t iInputHeightXDstWidth = kiInputPicHeight * iCurDstWidth; if (iInputWidthXDstHeight > iInputHeightXDstWidth) { pScaledPicture->iScaledWidth[iSpatialIdx] = WELS_MAX (iCurDstWidth, 4); pScaledPicture->iScaledHeight[iSpatialIdx] = WELS_MAX (iInputHeightXDstWidth / kiInputPicWidth, 4); } else { pScaledPicture->iScaledWidth[iSpatialIdx] = WELS_MAX (iInputWidthXDstHeight / kiInputPicHeight, 4); pScaledPicture->iScaledHeight[iSpatialIdx] = WELS_MAX (iCurDstHeight, 4); }Lines 1–500 of 1450. Load more
/*! * \copy * Copyright (c) 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. * */ #include "wels_preprocess.h" #include "picture_handle.h" #include "encoder_context.h" #include "utils.h" #include "encoder.h" namespace { void ClearEndOfLinePadding (uint8_t* pData, int32_t iStride, int32_t iWidth, int32_t iHeight) { if (iWidth < iStride) { for (int32_t i = 0; i < iHeight; ++i) memset (pData + i * iStride + iWidth, 0, iStride - iWidth); } } } // anon ns. namespace WelsEnc { //***** entry API declaration ************************************************************************// int32_t WelsInitScaledPic (SWelsSvcCodingParam* pParam, Scaled_Picture* pScaledPic, CMemoryAlign* pMemoryAlign); bool JudgeNeedOfScaling (SWelsSvcCodingParam* pParam, Scaled_Picture* pScaledPic); void FreeScaledPic (Scaled_Picture* pScaledPic, CMemoryAlign* pMemoryAlign); void WelsMoveMemory_c (uint8_t* pDstY, uint8_t* pDstU, uint8_t* pDstV, int32_t iDstStrideY, int32_t iDstStrideUV, uint8_t* pSrcY, uint8_t* pSrcU, uint8_t* pSrcV, int32_t iSrcStrideY, int32_t iSrcStrideUV, int32_t iWidth, int32_t iHeight); //******* table definition ***********************************************************************// const uint8_t g_kuiRefTemporalIdx[MAX_TEMPORAL_LEVEL][MAX_GOP_SIZE] = { { 0, }, // 0 { 0, 0, }, // 1 { 0, 0, 0, 1, }, // 2 { 0, 0, 0, 2, 0, 1, 1, 2, }, // 3 }; const int32_t g_kiPixMapSizeInBits = sizeof (uint8_t) * 8; inline void WelsUpdateSpatialIdxMap (sWelsEncCtx* pEncCtx, const int32_t iPos, SPicture* const pPic, const int32_t iDidx) { pEncCtx->sSpatialIndexMap[iPos].pSrc = pPic; pEncCtx->sSpatialIndexMap[iPos].iDid = iDidx; } /*************************************************************************** * * implement of the interface * ***************************************************************************/ CWelsPreProcess* CWelsPreProcess::CreatePreProcess (sWelsEncCtx* pEncCtx) { CWelsPreProcess* pPreProcess = NULL; switch (pEncCtx->pSvcParam->iUsageType) { case SCREEN_CONTENT_REAL_TIME: pPreProcess = WELS_NEW_OP (CWelsPreProcessScreen (pEncCtx), CWelsPreProcessScreen); break; default: pPreProcess = WELS_NEW_OP (CWelsPreProcessVideo (pEncCtx), CWelsPreProcessVideo); break; } WELS_VERIFY_RETURN_IF (NULL, NULL == pPreProcess) return pPreProcess; } CWelsPreProcess::CWelsPreProcess (sWelsEncCtx* pEncCtx) { m_pInterfaceVp = NULL; m_bInitDone = false; m_pEncCtx = pEncCtx; memset (&m_sScaledPicture, 0, sizeof (m_sScaledPicture)); memset (m_pSpatialPic, 0, sizeof (m_pSpatialPic)); memset (m_uiSpatialLayersInTemporal, 0, sizeof (m_uiSpatialLayersInTemporal)); memset (m_uiSpatialPicNum, 0, sizeof (m_uiSpatialPicNum)); } CWelsPreProcess::~CWelsPreProcess() { FreeScaledPic (&m_sScaledPicture, m_pEncCtx->pMemAlign); WelsPreprocessDestroy(); } int32_t CWelsPreProcess::WelsPreprocessCreate() { if (m_pInterfaceVp == NULL) { WelsCreateVpInterface ((void**) &m_pInterfaceVp, WELSVP_INTERFACE_VERION); if (!m_pInterfaceVp) goto exit; } else goto exit; return 0; exit: WelsPreprocessDestroy(); return 1; } int32_t CWelsPreProcess::WelsPreprocessDestroy() { WelsDestroyVpInterface (m_pInterfaceVp, WELSVP_INTERFACE_VERION); m_pInterfaceVp = NULL; return 0; } int32_t CWelsPreProcess::WelsPreprocessReset (sWelsEncCtx* pCtx, int32_t iWidth, int32_t iHeight) { int32_t iRet = -1; SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; //init source width and height pSvcParam->SUsedPicRect.iLeft = 0; pSvcParam->SUsedPicRect.iTop = 0; pSvcParam->SUsedPicRect.iWidth = iWidth; pSvcParam->SUsedPicRect.iHeight = iHeight; if ((iWidth < 16) || ((iHeight < 16))) { WelsLog (& (pCtx->sLogCtx), WELS_LOG_ERROR, "Don't support width(%d) or height(%d) which is less than 16 ", iWidth, iHeight); return iRet; } if (pCtx) { FreeScaledPic (&m_sScaledPicture, pCtx->pMemAlign); iRet = InitLastSpatialPictures (pCtx); iRet = WelsInitScaledPic (pCtx->pSvcParam, &m_sScaledPicture, pCtx->pMemAlign); } return iRet; } int32_t CWelsPreProcess::AllocSpatialPictures (sWelsEncCtx* pCtx, SWelsSvcCodingParam* pParam) { CMemoryAlign* pMa = pCtx->pMemAlign; const int32_t kiDlayerCount = pParam->iSpatialLayerNum; int32_t iDlayerIndex = 0; // spatial pictures iDlayerIndex = 0; do { const int32_t kiPicWidth = pParam->sSpatialLayers[iDlayerIndex].iVideoWidth; const int32_t kiPicHeight = pParam->sSpatialLayers[iDlayerIndex].iVideoHeight; const uint8_t kuiLayerInTemporal = 2 + WELS_MAX (pParam->sDependencyLayers[iDlayerIndex].iHighestTemporalId, 1); const uint8_t kuiRefNumInTemporal = kuiLayerInTemporal + pParam->iLTRRefNum; uint8_t i = 0; m_uiSpatialPicNum[iDlayerIndex] = kuiRefNumInTemporal; do { SPicture* pPic = AllocPicture (pMa, kiPicWidth, kiPicHeight, false, 0); WELS_VERIFY_RETURN_IF (1, (NULL == pPic)) m_pSpatialPic[iDlayerIndex][i] = pPic; ++ i; } while (i < kuiRefNumInTemporal); if (pParam->iUsageType == SCREEN_CONTENT_REAL_TIME) m_uiSpatialLayersInTemporal[iDlayerIndex] = 1; else m_uiSpatialLayersInTemporal[iDlayerIndex] = kuiLayerInTemporal; ++ iDlayerIndex; } while (iDlayerIndex < kiDlayerCount); return 0; } void CWelsPreProcess::FreeSpatialPictures (sWelsEncCtx* pCtx) { CMemoryAlign* pMa = pCtx->pMemAlign; int32_t j = 0; while (j < pCtx->pSvcParam->iSpatialLayerNum) { uint8_t i = 0; uint8_t uiRefNumInTemporal = m_uiSpatialPicNum[j]; while (i < uiRefNumInTemporal) { if (NULL != m_pSpatialPic[j][i]) { FreePicture (pMa, &m_pSpatialPic[j][i]); } ++ i; } m_uiSpatialLayersInTemporal[j] = 0; ++ j; } } int32_t CWelsPreProcess::BuildSpatialPicList (sWelsEncCtx* pCtx, const SSourcePicture* kpSrcPic) { SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; int32_t iSpatialNum = 0; int32_t iWidth = ((kpSrcPic->iPicWidth >> 1) << 1); int32_t iHeight = ((kpSrcPic->iPicHeight >> 1) << 1); if (!m_bInitDone) { if (WelsPreprocessCreate() != 0) return -1; if (WelsPreprocessReset (pCtx, iWidth, iHeight) != 0) return -1; m_iAvaliableRefInSpatialPicList = pSvcParam->iNumRefFrame; m_bInitDone = true; } else { if ((iWidth != pSvcParam->SUsedPicRect.iWidth) || (iHeight != pSvcParam->SUsedPicRect.iHeight)) { if (WelsPreprocessReset (pCtx, iWidth, iHeight) != 0) return -1; } } if (m_pInterfaceVp == NULL) return -1; pCtx->pVaa->bSceneChangeFlag = pCtx->pVaa->bIdrPeriodFlag = false; iSpatialNum = SingleLayerPreprocess (pCtx, kpSrcPic, &m_sScaledPicture); return iSpatialNum; } SPicture* CWelsPreProcess::GetBestRefPic (EUsageType iUsageType, bool bSceneLtr, EWelsSliceType eSliceType, int32_t kiDidx, int32_t iRefTemporalIdx) { assert (iUsageType == SCREEN_CONTENT_REAL_TIME); SVAAFrameInfoExt* pVaaExt = static_cast<SVAAFrameInfoExt*> (m_pEncCtx->pVaa); SRefInfoParam* BestRefCandidateParam = (bSceneLtr) ? (& (pVaaExt->sVaaLtrBestRefCandidate[0])) : (& (pVaaExt->sVaaStrBestRefCandidate[0])); return m_pSpatialPic[0][BestRefCandidateParam->iSrcListIdx]; } SPicture* CWelsPreProcess::GetBestRefPic (const int32_t kiDidx, const int32_t iRefTemporalIdx) { return m_pSpatialPic[kiDidx][iRefTemporalIdx]; } int32_t CWelsPreProcess::AnalyzeSpatialPic (sWelsEncCtx* pCtx, const int32_t kiDidx) { SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; bool bNeededMbAq = (pSvcParam->bEnableAdaptiveQuant && (pCtx->eSliceType == P_SLICE)); bool bCalculateBGD = (pCtx->eSliceType == P_SLICE && pSvcParam->bEnableBackgroundDetection); SSpatialLayerInternal* pParamInternal = &pSvcParam->sDependencyLayers[kiDidx]; int32_t iCurTemporalIdx = m_uiSpatialLayersInTemporal[kiDidx] - 1; int32_t iRefTemporalIdx = (int32_t)g_kuiRefTemporalIdx[pSvcParam->iDecompStages][pParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; if (pCtx->uiTemporalId == 0 && pCtx->pLtr[pCtx->uiDependencyId].bReceivedT0LostFlag) iRefTemporalIdx = m_uiSpatialLayersInTemporal[kiDidx] + pCtx->pVaa->uiValidLongTermPicIdx; SPicture* pCurPic = m_pSpatialPic[kiDidx][iCurTemporalIdx]; bool bCalculateVar = (pSvcParam->iRCMode >= RC_BITRATE_MODE && pCtx->eSliceType == I_SLICE); if (pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) { SPicture* pRefPic = GetBestRefPic (pSvcParam->iUsageType, pCtx->bCurFrameMarkedAsSceneLtr, pCtx->eSliceType, kiDidx, iRefTemporalIdx); VaaCalculation (pCtx->pVaa, pCurPic, pRefPic, false, bCalculateVar, bCalculateBGD); if (pSvcParam->bEnableBackgroundDetection) { BackgroundDetection (pCtx->pVaa, pCurPic, pRefPic, bCalculateBGD && pRefPic->iPictureType != I_SLICE); } if (bNeededMbAq) { AdaptiveQuantCalculation (pCtx->pVaa, pCurPic, pRefPic); } } else { SPicture* pRefPic = GetBestRefPic (kiDidx, iRefTemporalIdx); SPicture* pLastPic = m_pLastSpatialPicture[kiDidx][0]; bool bCalculateSQDiff = ((pLastPic->pData[0] == pRefPic->pData[0]) && bNeededMbAq); VaaCalculation (pCtx->pVaa, pCurPic, pRefPic, bCalculateSQDiff, bCalculateVar, bCalculateBGD); if (pSvcParam->bEnableBackgroundDetection) { BackgroundDetection (pCtx->pVaa, pCurPic, pRefPic, bCalculateBGD && pRefPic->iPictureType != I_SLICE); } if (bNeededMbAq) { AdaptiveQuantCalculation (pCtx->pVaa, m_pLastSpatialPicture[kiDidx][1], m_pLastSpatialPicture[kiDidx][0]); } } return 0; } int32_t CWelsPreProcess::GetCurPicPosition (const int32_t kiDidx) { return (m_uiSpatialLayersInTemporal[kiDidx] - 1); } int32_t CWelsPreProcess::UpdateSpatialPictures (sWelsEncCtx* pCtx, SWelsSvcCodingParam* pParam, const int8_t iCurTid, const int32_t kiDidx) { if (pCtx->pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) return 0; WelsExchangeSpatialPictures (&m_pLastSpatialPicture[kiDidx][1], &m_pLastSpatialPicture[kiDidx][0]); const int32_t kiCurPos = GetCurPicPosition (kiDidx); if (iCurTid < kiCurPos || pParam->iDecompStages == 0) { if ((iCurTid >= MAX_TEMPORAL_LEVEL) || (kiCurPos > MAX_TEMPORAL_LEVEL)) { InitLastSpatialPictures (pCtx); return 1; } if (pCtx->bRefOfCurTidIsLtr[kiDidx][iCurTid]) { const int32_t kiAvailableLtrPos = m_uiSpatialLayersInTemporal[kiDidx] + pCtx->pVaa->uiMarkLongTermPicIdx; WelsExchangeSpatialPictures (&m_pSpatialPic[kiDidx][kiAvailableLtrPos], &m_pSpatialPic[kiDidx][iCurTid]); pCtx->bRefOfCurTidIsLtr[kiDidx][iCurTid] = false; } WelsExchangeSpatialPictures (&m_pSpatialPic[kiDidx][kiCurPos], &m_pSpatialPic[kiDidx][iCurTid]); } return 0; } /* * SingleLayerPreprocess: down sampling if applicable * @return: exact number of spatial layers need to encoder indeed */ int32_t CWelsPreProcess::SingleLayerPreprocess (sWelsEncCtx* pCtx, const SSourcePicture* kpSrc, Scaled_Picture* pScaledPicture) { SWelsSvcCodingParam* pSvcParam = pCtx->pSvcParam; int8_t iDependencyId = pSvcParam->iSpatialLayerNum - 1; SPicture* pSrcPic = NULL; // large SPicture* pDstPic = NULL; // small SSpatialLayerConfig* pDlayerParam = NULL; SSpatialLayerInternal* pDlayerParamInternal = NULL; int32_t iSpatialNum = 0; int32_t iSrcWidth = 0; int32_t iSrcHeight = 0; int32_t iTargetWidth = 0; int32_t iTargetHeight = 0; int32_t iTemporalId = 0; int32_t iClosestDid = iDependencyId; pDlayerParamInternal = &pSvcParam->sDependencyLayers[iDependencyId]; pDlayerParam = &pSvcParam->sSpatialLayers[iDependencyId]; iTargetWidth = pDlayerParam->iVideoWidth; iTargetHeight = pDlayerParam->iVideoHeight; iSrcWidth = pSvcParam->SUsedPicRect.iWidth; iSrcHeight = pSvcParam->SUsedPicRect.iHeight; if (pSvcParam->uiIntraPeriod) pCtx->pVaa->bIdrPeriodFlag = (1 + pDlayerParamInternal->iFrameIndex >= (int32_t)pSvcParam->uiIntraPeriod) ? true : false; pSrcPic = pScaledPicture->pScaledInputPicture ? pScaledPicture->pScaledInputPicture : GetCurrentOrigFrame ( iDependencyId); WelsMoveMemoryWrapper (pSvcParam, pSrcPic, kpSrc, iSrcWidth, iSrcHeight); if (pSvcParam->bEnableDenoise) BilateralDenoising (pSrcPic, iSrcWidth, iSrcHeight); // different scaling in between input picture and dst highest spatial picture. int32_t iShrinkWidth = iSrcWidth; int32_t iShrinkHeight = iSrcHeight; pDstPic = pSrcPic; if (pScaledPicture->pScaledInputPicture) { // for highest downsampling pDstPic = GetCurrentOrigFrame (iDependencyId); iShrinkWidth = pScaledPicture->iScaledWidth[iDependencyId]; iShrinkHeight = pScaledPicture->iScaledHeight[iDependencyId]; } DownsamplePadding (pSrcPic, pDstPic, iSrcWidth, iSrcHeight, iShrinkWidth, iShrinkHeight, iTargetWidth, iTargetHeight, false); if (pSvcParam->bEnableSceneChangeDetect && !pCtx->pVaa->bIdrPeriodFlag) { if (pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) { pCtx->pVaa->eSceneChangeIdc = (pDlayerParamInternal->bEncCurFrmAsIdrFlag ? LARGE_CHANGED_SCENE : DetectSceneChange (pDstPic)); pCtx->pVaa->bSceneChangeFlag = (LARGE_CHANGED_SCENE == pCtx->pVaa->eSceneChangeIdc); } else { if ((!pDlayerParamInternal->bEncCurFrmAsIdrFlag) && ! (pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1))) { SPicture* pRefPic = pCtx->pLtr[iDependencyId].bReceivedT0LostFlag ? m_pSpatialPic[iDependencyId][m_uiSpatialLayersInTemporal[iDependencyId] + pCtx->pVaa->uiValidLongTermPicIdx] : m_pLastSpatialPicture[iDependencyId][0]; //pCtx->pVaa->eSceneChangeIdc = DetectSceneChange (pDstPic, pRefPic); pCtx->pVaa->bSceneChangeFlag = GetSceneChangeFlag (DetectSceneChange (pDstPic, pRefPic)); } } } for (int32_t i = 0; i < pSvcParam->iSpatialLayerNum; i++) { pDlayerParamInternal = &pSvcParam->sDependencyLayers[i]; iTemporalId = pDlayerParamInternal->uiCodingIdx2TemporalId[pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; if (iTemporalId != INVALID_TEMPORAL_ID) { ++ iSpatialNum; } } pDlayerParamInternal = &pSvcParam->sDependencyLayers[iDependencyId]; iTemporalId = pDlayerParamInternal->uiCodingIdx2TemporalId[pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; int iActualSpatialNum = iSpatialNum - 1; if (iTemporalId != INVALID_TEMPORAL_ID) { WelsUpdateSpatialIdxMap (pCtx, iActualSpatialNum, pDstPic, iDependencyId); -- iActualSpatialNum; } m_pLastSpatialPicture[iDependencyId][1] = GetCurrentOrigFrame (iDependencyId); -- iDependencyId; // generate other spacial layer // pSrc is // -- padded input pic, if downsample should be applied to generate highest layer, [if] block above // -- highest layer, if no downsampling, [else] block above if (pSvcParam->iSpatialLayerNum > 1) { while (iDependencyId >= 0) { pDlayerParamInternal = &pSvcParam->sDependencyLayers[iDependencyId]; pDlayerParam = &pSvcParam->sSpatialLayers[iDependencyId]; SPicture* pSrcPic = m_pLastSpatialPicture[iClosestDid][1]; // large iTargetWidth = pDlayerParam->iVideoWidth; iTargetHeight = pDlayerParam->iVideoHeight; iTemporalId = pDlayerParamInternal->uiCodingIdx2TemporalId[pDlayerParamInternal->iCodingIndex & (pSvcParam->uiGopSize - 1)]; // down sampling performed int32_t iSrcWidth = pScaledPicture->iScaledWidth[iClosestDid]; int32_t iSrcHeight = pScaledPicture->iScaledHeight[iClosestDid]; pDstPic = GetCurrentOrigFrame (iDependencyId); // small iShrinkWidth = pScaledPicture->iScaledWidth[iDependencyId]; iShrinkHeight = pScaledPicture->iScaledHeight[iDependencyId]; DownsamplePadding (pSrcPic, pDstPic, iSrcWidth, iSrcHeight, iShrinkWidth, iShrinkHeight, iTargetWidth, iTargetHeight, true); if ((iTemporalId != INVALID_TEMPORAL_ID)) { WelsUpdateSpatialIdxMap (pCtx, iActualSpatialNum, pDstPic, iDependencyId); iActualSpatialNum--; } m_pLastSpatialPicture[iDependencyId][1] = pDstPic; iClosestDid = iDependencyId; -- iDependencyId; } } return iSpatialNum; } /*! * \brief Whether input picture need be scaled? */ bool JudgeNeedOfScaling (SWelsSvcCodingParam* pParam, Scaled_Picture* pScaledPicture) { const int32_t kiInputPicWidth = pParam->SUsedPicRect.iWidth; const int32_t kiInputPicHeight = pParam->SUsedPicRect.iHeight; const int32_t kiDstPicWidth = pParam->sDependencyLayers[pParam->iSpatialLayerNum - 1].iActualWidth; const int32_t kiDstPicHeight = pParam->sDependencyLayers[pParam->iSpatialLayerNum - 1].iActualHeight; bool bNeedDownsampling = true; int32_t iSpatialIdx = pParam->iSpatialLayerNum - 1; if (kiDstPicWidth >= kiInputPicWidth && kiDstPicHeight >= kiInputPicHeight) { bNeedDownsampling = false; } for (; iSpatialIdx >= 0; iSpatialIdx --) { SSpatialLayerInternal* pCurLayer = &pParam->sDependencyLayers[iSpatialIdx]; int32_t iCurDstWidth = pCurLayer->iActualWidth; int32_t iCurDstHeight = pCurLayer->iActualHeight; int32_t iInputWidthXDstHeight = kiInputPicWidth * iCurDstHeight; int32_t iInputHeightXDstWidth = kiInputPicHeight * iCurDstWidth; if (iInputWidthXDstHeight > iInputHeightXDstWidth) { pScaledPicture->iScaledWidth[iSpatialIdx] = WELS_MAX (iCurDstWidth, 4); pScaledPicture->iScaledHeight[iSpatialIdx] = WELS_MAX (iInputHeightXDstWidth / kiInputPicWidth, 4); } else { pScaledPicture->iScaledWidth[iSpatialIdx] = WELS_MAX (iInputWidthXDstHeight / kiInputPicHeight, 4); pScaledPicture->iScaledHeight[iSpatialIdx] = WELS_MAX (iCurDstHeight, 4); }