void RcTraceVGopBitrate (sWelsEncCtx* pEncCtx) { const int32_t kiDid = pEncCtx->uiDependencyId; SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[kiDid]; if (pWelsSvcRc->iFrameCodedInVGop) { const int32_t kiHighestTid = pEncCtx->pSvcParam->sDependencyLayers[kiDid].iHighestTemporalId; SRCTemporal* pTOverRc = pWelsSvcRc->pTemporalOverRc; int32_t iVGopBitrate = 0; int32_t iTotalBits = pWelsSvcRc->iPaddingBitrateStat; int32_t iTid = 0; while (iTid <= kiHighestTid) { iTotalBits += pTOverRc[iTid].iGopBitsDq; ++ iTid; } int32_t iFrameInVGop = pWelsSvcRc->iFrameCodedInVGop + pWelsSvcRc->iSkipFrameInVGop; if (0 != iFrameInVGop) iVGopBitrate = WELS_ROUND(iTotalBits / iFrameInVGop * pWelsSvcRc->fFrameRate); #ifdef _TEST_TEMP_Rc_ fprintf (fp_vgop, "%d\n", WELS_ROUND((double)iTotalBits / iFrameInVGop)); #endif WelsLog (pEncCtx, WELS_LOG_INFO, "[Rc] VGOPbitrate%d: %d \n", kiDid, iVGopBitrate); if (iTotalBits > 0) { iTid = 0; while (iTid <= kiHighestTid) { WelsLog (pEncCtx, WELS_LOG_INFO, "T%d=%8.3f \n", iTid, (double) (pTOverRc[iTid].iGopBitsDq / iTotalBits)); ++ iTid; } } } }
void RcVBufferCalculationSkip (sWelsEncCtx* pEncCtx) { SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; SRCTemporal* pTOverRc = pWelsSvcRc->pTemporalOverRc; const int32_t kiOutputBits = WELS_ROUND(pWelsSvcRc->dBitsPerFrame); //condition 1: whole pBuffer fullness pWelsSvcRc->iBufferFullnessSkip += (pWelsSvcRc->iFrameDqBits - kiOutputBits); //condition 2: VGOP bits constraint const int32_t kiVGopBits = WELS_ROUND(pWelsSvcRc->dBitsPerFrame * VGOP_SIZE); int32_t iVGopBitsPred = 0; for (int32_t i = pWelsSvcRc->iFrameCodedInVGop + 1; i < VGOP_SIZE; i++) iVGopBitsPred += pTOverRc[pWelsSvcRc->iTlOfFrames[i]].iMinBitsTl; iVGopBitsPred -= pWelsSvcRc->iRemainingBits; double dIncPercent = iVGopBitsPred * 100.0 / kiVGopBits - (double)VGOP_BITS_PERCENTAGE_DIFF; if ((pWelsSvcRc->iBufferFullnessSkip > pWelsSvcRc->iBufferSizeSkip && pWelsSvcRc->iAverageFrameQp > pWelsSvcRc->iSkipQpValue) || (dIncPercent > pWelsSvcRc->iRcVaryPercentage)) { pEncCtx->iSkipFrameFlag = 1; pWelsSvcRc->iBufferFullnessSkip = pWelsSvcRc->iBufferFullnessSkip - kiOutputBits; #ifdef FRAME_INFO_OUTPUT fprintf (stderr, "skip one frame\n"); #endif } if (pWelsSvcRc->iBufferFullnessSkip < 0) pWelsSvcRc->iBufferFullnessSkip = 0; if (pEncCtx->iSkipFrameFlag == 1) { pWelsSvcRc->iRemainingBits += WELS_ROUND(pWelsSvcRc->dBitsPerFrame); pWelsSvcRc->iSkipFrameNum++; pWelsSvcRc->iSkipFrameInVGop++; } }
void RcVBufferCalculationPadding (sWelsEncCtx* pEncCtx) { SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; const int32_t kiOutputBits = WELS_ROUND(pWelsSvcRc->dBitsPerFrame); const int32_t kiBufferThreshold = WELS_ROUND(PADDING_THRESHOLD * (-pWelsSvcRc->iBufferSizePadding)); pWelsSvcRc->iBufferFullnessPadding += (pWelsSvcRc->iFrameDqBits - kiOutputBits); if (pWelsSvcRc->iBufferFullnessPadding < kiBufferThreshold) { pWelsSvcRc->iPaddingSize = -pWelsSvcRc->iBufferFullnessPadding; pWelsSvcRc->iPaddingSize >>= 3; // /8 pWelsSvcRc->iBufferFullnessPadding = 0; } else
void RcCalculateIdrQp (sWelsEncCtx* pEncCtx) { SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; //obtain the idr qp using previous idr complexity if (pWelsSvcRc->iNumberMbFrame != pWelsSvcRc->iIntraMbCount) { pWelsSvcRc->iIntraComplexity = WELS_ROUND((double)pWelsSvcRc->iIntraComplexity * pWelsSvcRc->iNumberMbFrame / pWelsSvcRc->iIntraMbCount); } pWelsSvcRc->iInitialQp = WELS_ROUND(RcConvertQStep2Qp ((double)pWelsSvcRc->iIntraComplexity / pWelsSvcRc->iTargetBits)); pWelsSvcRc->iInitialQp = WELS_ROUND(WELS_CLIP3 (pWelsSvcRc->iInitialQp, MIN_IDR_QP, MAX_IDR_QP)); pEncCtx->iGlobalQp = pWelsSvcRc->iInitialQp; pWelsSvcRc->dQStep = RcConvertQp2QStep (pEncCtx->iGlobalQp); pWelsSvcRc->iLastCalculatedQScale = pEncCtx->iGlobalQp; }
void RcDecideTargetBits (sWelsEncCtx* pEncCtx) { SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; SRCTemporal* pTOverRc = &pWelsSvcRc->pTemporalOverRc[pEncCtx->uiTemporalId]; pWelsSvcRc->iCurrentBitsLevel = BITS_NORMAL; //allocate bits if (pEncCtx->eSliceType == I_SLICE) { pWelsSvcRc->iTargetBits = WELS_ROUND(pWelsSvcRc->dBitsPerFrame * IDR_BITRATE_RATIO); } else { pWelsSvcRc->iTargetBits = WELS_ROUND(pWelsSvcRc->iRemainingBits * pTOverRc->dTlayerWeight / pWelsSvcRc->dRemainingWeights); if ((pWelsSvcRc->iTargetBits <= 0) && (pEncCtx->pSvcParam->iRCMode == RC_LOW_BW_MODE)) { pWelsSvcRc->iCurrentBitsLevel = BITS_EXCEEDED; } else if ((pWelsSvcRc->iTargetBits <= pTOverRc->iMinBitsTl) && (pEncCtx->pSvcParam->iRCMode == RC_LOW_BW_MODE)) { pWelsSvcRc->iCurrentBitsLevel = BITS_LIMITED; } pWelsSvcRc->iTargetBits = WELS_CLIP3 (pWelsSvcRc->iTargetBits, pTOverRc->iMinBitsTl, pTOverRc->iMaxBitsTl); } pWelsSvcRc->dRemainingWeights -= pTOverRc->dTlayerWeight; }
void RcUpdateBitrateFps (sWelsEncCtx* pEncCtx) { SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; SRCTemporal* pTOverRc = pWelsSvcRc->pTemporalOverRc; SDLayerParam* pDLayerParam = &pEncCtx->pSvcParam->sDependencyLayers[pEncCtx->uiDependencyId]; const int32_t kiGopSize = (1 << pDLayerParam->iDecompositionStages); const int32_t kiHighestTid = pDLayerParam->iHighestTemporalId; double input_dBitsPerFrame = pDLayerParam->iSpatialBitrate / pDLayerParam->fInputFrameRate; const int32_t kiGopBits = WELS_ROUND (input_dBitsPerFrame * kiGopSize); int32_t i; pWelsSvcRc->iBitRate = pDLayerParam->iSpatialBitrate; pWelsSvcRc->fFrameRate = pDLayerParam->fInputFrameRate; double dTargetVaryRange = FRAME_iTargetBits_VARY_RANGE * (1.0 - pWelsSvcRc->dRcVaryRatio); double dMinBitsRatio = 1.0 - dTargetVaryRange; double dMaxBitsRatio = 1.0 + FRAME_iTargetBits_VARY_RANGE;//dTargetVaryRange; for (i = 0; i <= kiHighestTid; i++) { const double kdConstraitBits = kiGopBits * pTOverRc[i].dTlayerWeight; pTOverRc[i].iMinBitsTl = WELS_ROUND(kdConstraitBits * dMinBitsRatio); pTOverRc[i].iMaxBitsTl = WELS_ROUND(kdConstraitBits * dMaxBitsRatio); } //When bitrate is changed, pBuffer size should be updated pWelsSvcRc->iBufferSizeSkip = WELS_ROUND(pWelsSvcRc->iBitRate * pWelsSvcRc->dSkipBufferRatio); pWelsSvcRc->iBufferSizePadding = WELS_ROUND(pWelsSvcRc->iBitRate * PADDING_BUFFER_RATIO); //change remaining bits if (pWelsSvcRc->dBitsPerFrame > 0.1) pWelsSvcRc->iRemainingBits = WELS_ROUND(pWelsSvcRc->iRemainingBits * input_dBitsPerFrame / pWelsSvcRc->dBitsPerFrame); pWelsSvcRc->dBitsPerFrame = input_dBitsPerFrame; }
void WelsRcFrameDelayJudge (void* pCtx) { sWelsEncCtx* pEncCtx = (sWelsEncCtx*)pCtx; SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; SSpatialLayerConfig* pDLayerParam = &pEncCtx->pSvcParam->sSpatialLayers[pEncCtx->uiDependencyId]; SSpatialLayerInternal* pDLayerParamInternal = &pEncCtx->pSvcParam->sDependencyLayers[pEncCtx->uiDependencyId]; int32_t iSentBits = WELS_ROUND (pDLayerParam->iSpatialBitrate / pDLayerParamInternal->fOutputFrameRate); pWelsSvcRc->bSkipFlag = false; if (pWelsSvcRc->iBufferFullnessSkip > pWelsSvcRc->iBufferSizeSkip) { pWelsSvcRc->bSkipFlag = true; pWelsSvcRc->iBufferFullnessSkip -= iSentBits; pWelsSvcRc->iBufferFullnessSkip = WELS_MAX (pWelsSvcRc->iBufferFullnessSkip, 0); } }
void RcInitVGop (sWelsEncCtx* pEncCtx) { const int32_t kiDid = pEncCtx->uiDependencyId; SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[kiDid]; SRCTemporal* pTOverRc = pWelsSvcRc->pTemporalOverRc; const int32_t kiHighestTid = pEncCtx->pSvcParam->sDependencyLayers[kiDid].iHighestTemporalId; pWelsSvcRc->iRemainingBits = WELS_ROUND(VGOP_SIZE * pWelsSvcRc->dBitsPerFrame); pWelsSvcRc->dRemainingWeights = pWelsSvcRc->iGopNumberInVGop; pWelsSvcRc->iFrameCodedInVGop = 0; pWelsSvcRc->iGopIndexInVGop = 0; for (int32_t i = 0; i <= kiHighestTid; ++ i) pTOverRc[i].iGopBitsDq = 0; pWelsSvcRc->iSkipFrameInVGop = 0; }
void RcInitSliceInformation (sWelsEncCtx* pEncCtx) { SSliceCtx* pCurSliceCtx = pEncCtx->pCurDqLayer->pSliceEncCtx; SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; SRCSlicing* pSOverRc = &pWelsSvcRc->pSlicingOverRc[0]; const int32_t kiSliceNum = pCurSliceCtx->iSliceNumInFrame; const double kdBitsPerMb = (double)pWelsSvcRc->iTargetBits / pWelsSvcRc->iNumberMbFrame; for (int32_t i = 0; i < kiSliceNum; i++) { pSOverRc->iStartMbSlice = pSOverRc->iEndMbSlice = pCurSliceCtx->pFirstMbInSlice[i]; pSOverRc->iEndMbSlice += (pCurSliceCtx->pCountMbNumInSlice[i] - 1); pSOverRc->iTotalQpSlice = 0; pSOverRc->iTotalMbSlice = 0; pSOverRc->iTargetBitsSlice = WELS_ROUND(kdBitsPerMb * pCurSliceCtx->pCountMbNumInSlice[i]); pSOverRc->iFrameBitsSlice = 0; pSOverRc->iGomBitsSlice = 0; ++ pSOverRc; } }
void RcUpdateBitrateFps (sWelsEncCtx* pEncCtx) { SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; SRCTemporal* pTOverRc = pWelsSvcRc->pTemporalOverRc; SSpatialLayerConfig* pDLayerParam = &pEncCtx->pSvcParam->sSpatialLayers[pEncCtx->uiDependencyId]; SSpatialLayerInternal* pDLayerParamInternal = &pEncCtx->pSvcParam->sDependencyLayers[pEncCtx->uiDependencyId]; const int32_t kiGopSize = (1 << pDLayerParamInternal->iDecompositionStages); const int32_t kiHighestTid = pDLayerParamInternal->iHighestTemporalId; int32_t input_iBitsPerFrame = WELS_ROUND (pDLayerParam->iSpatialBitrate * INT_MULTIPLY / pDLayerParamInternal->fInputFrameRate); const int32_t kiGopBits = WELS_DIV_ROUND (input_iBitsPerFrame * kiGopSize, INT_MULTIPLY); int32_t i; pWelsSvcRc->iBitRate = pDLayerParam->iSpatialBitrate; pWelsSvcRc->fFrameRate = pDLayerParamInternal->fInputFrameRate; int32_t iTargetVaryRange = FRAME_iTargetBits_VARY_RANGE * (MAX_BITS_VARY_PERCENTAGE - pWelsSvcRc->iRcVaryRatio); int32_t iMinBitsRatio = (MAX_BITS_VARY_PERCENTAGE) * INT_MULTIPLY - iTargetVaryRange; int32_t iMaxBitsRatio = (MAX_BITS_VARY_PERCENTAGE) * (INT_MULTIPLY + FRAME_iTargetBits_VARY_RANGE); for (i = 0; i <= kiHighestTid; i++) { const int64_t kdConstraitBits = kiGopBits * pTOverRc[i].iTlayerWeight; pTOverRc[i].iMinBitsTl = WELS_DIV_ROUND (kdConstraitBits * iMinBitsRatio, INT_MULTIPLY * MAX_BITS_VARY_PERCENTAGE * WEIGHT_MULTIPLY); pTOverRc[i].iMaxBitsTl = WELS_DIV_ROUND (kdConstraitBits * iMaxBitsRatio, INT_MULTIPLY * MAX_BITS_VARY_PERCENTAGE * WEIGHT_MULTIPLY); } //When bitrate is changed, pBuffer size should be updated pWelsSvcRc->iBufferSizeSkip = WELS_DIV_ROUND (pWelsSvcRc->iBitRate * pWelsSvcRc->iSkipBufferRatio, INT_MULTIPLY); pWelsSvcRc->iBufferSizePadding = WELS_DIV_ROUND (pWelsSvcRc->iBitRate * PADDING_BUFFER_RATIO, INT_MULTIPLY); //change remaining bits if (pWelsSvcRc->iBitsPerFrame > REMAIN_BITS_TH) pWelsSvcRc->iRemainingBits = pWelsSvcRc->iRemainingBits * input_iBitsPerFrame / pWelsSvcRc->iBitsPerFrame; pWelsSvcRc->iBitsPerFrame = input_iBitsPerFrame; }
void RcCalculatePictureQp (sWelsEncCtx* pEncCtx) { SWelsSvcRc* pWelsSvcRc = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId]; int32_t iTl = pEncCtx->uiTemporalId; SRCTemporal* pTOverRc = &pWelsSvcRc->pTemporalOverRc[iTl]; int32_t iLumaQp = 0; if (0 == pTOverRc->iPFrameNum) { iLumaQp = pWelsSvcRc->iInitialQp; } else if (pWelsSvcRc->iCurrentBitsLevel == BITS_EXCEEDED) { iLumaQp = MAX_LOW_BR_QP; //limit QP int32_t iLastIdxCodecInVGop = pWelsSvcRc->iFrameCodedInVGop - 1; if (iLastIdxCodecInVGop < 0) iLastIdxCodecInVGop += VGOP_SIZE; int32_t iTlLast = pWelsSvcRc->iTlOfFrames[iLastIdxCodecInVGop]; int32_t iDeltaQpTemporal = iTl - iTlLast; if (0 == iTlLast && iTl > 0) iDeltaQpTemporal += 3; else if (0 == iTl && iTlLast > 0) iDeltaQpTemporal -= 3; iLumaQp = WELS_CLIP3 (iLumaQp, pWelsSvcRc->iLastCalculatedQScale - pWelsSvcRc->iFrameDeltaQpLower + iDeltaQpTemporal, pWelsSvcRc->iLastCalculatedQScale + pWelsSvcRc->iFrameDeltaQpUpper + iDeltaQpTemporal); iLumaQp = WELS_CLIP3 (iLumaQp, GOM_MIN_QP_MODE, MAX_LOW_BR_QP); pWelsSvcRc->dQStep = RcConvertQp2QStep (iLumaQp); pWelsSvcRc->iLastCalculatedQScale = iLumaQp; if (pEncCtx->pSvcParam->bEnableAdaptiveQuant) { iLumaQp = WELS_ROUND(iLumaQp - pEncCtx->pVaa->sAdaptiveQuantParam.dAverMotionTextureIndexToDeltaQp); } pEncCtx->iGlobalQp = iLumaQp; return; } else { double dCmplxRatio = (double)pEncCtx->pVaa->sComplexityAnalysisParam.iFrameComplexity / pTOverRc->iFrameCmplxMean; dCmplxRatio = WELS_CLIP3 (dCmplxRatio, 1.0 - FRAME_CMPLX_RATIO_RANGE, 1.0 + FRAME_CMPLX_RATIO_RANGE); pWelsSvcRc->dQStep = pTOverRc->dLinearCmplx * dCmplxRatio / pWelsSvcRc->iTargetBits; iLumaQp = WELS_ROUND(RcConvertQStep2Qp (pWelsSvcRc->dQStep)); //limit QP int32_t iLastIdxCodecInVGop = pWelsSvcRc->iFrameCodedInVGop - 1; if (iLastIdxCodecInVGop < 0) iLastIdxCodecInVGop += VGOP_SIZE; int32_t iTlLast = pWelsSvcRc->iTlOfFrames[iLastIdxCodecInVGop]; int32_t iDeltaQpTemporal = iTl - iTlLast; if (0 == iTlLast && iTl > 0) iDeltaQpTemporal += 3; else if (0 == iTl && iTlLast > 0) iDeltaQpTemporal -= 3; iLumaQp = WELS_CLIP3 (iLumaQp, pWelsSvcRc->iLastCalculatedQScale - pWelsSvcRc->iFrameDeltaQpLower + iDeltaQpTemporal, pWelsSvcRc->iLastCalculatedQScale + pWelsSvcRc->iFrameDeltaQpUpper + iDeltaQpTemporal); } iLumaQp = WELS_CLIP3 (iLumaQp, GOM_MIN_QP_MODE, GOM_MAX_QP_MODE); pWelsSvcRc->dQStep = RcConvertQp2QStep (iLumaQp); pWelsSvcRc->iLastCalculatedQScale = iLumaQp; #ifndef _NOT_USE_AQ_FOR_TEST_ if (pEncCtx->pSvcParam->bEnableAdaptiveQuant) { iLumaQp = WELS_ROUND(iLumaQp - pEncCtx->pVaa->sAdaptiveQuantParam.dAverMotionTextureIndexToDeltaQp); if (pEncCtx->pSvcParam->iRCMode != RC_LOW_BW_MODE) iLumaQp = WELS_CLIP3 (iLumaQp, pWelsSvcRc->iMinQp, pWelsSvcRc->iMaxQp); } #endif pEncCtx->iGlobalQp = iLumaQp; }
void DynamicAdjustSlicing (sWelsEncCtx* pCtx, SDqLayer* pCurDqLayer, void* pComplexRatio, int32_t iCurDid) { SSliceCtx* pSliceCtx = pCurDqLayer->pSliceEncCtx; const int32_t kiCountSliceNum = pSliceCtx->iSliceNumInFrame; const int32_t kiCountNumMb = pSliceCtx->iMbNumInFrame; int32_t iMinimalMbNum = pSliceCtx->iMbWidth; // in theory we need only 1 SMB, here let it as one SMB row required int32_t iMaximalMbNum = 0; // dynamically assign later float* pSliceComplexRatio = (float*)pComplexRatio; int32_t iMbNumLeft = kiCountNumMb; int32_t iRunLen[MAX_THREADS_NUM] = {0}; int32_t iSliceIdx = 0; int32_t iNumMbInEachGom = 0; SWelsSvcRc* pWelsSvcRc = &pCtx->pWelsSvcRc[iCurDid]; if (pCtx->pSvcParam->iRCMode != RC_OFF_MODE) { iNumMbInEachGom = pWelsSvcRc->iNumberMbGom; if (iNumMbInEachGom <= 0) { WelsLog (pCtx, WELS_LOG_ERROR, "[MT] DynamicAdjustSlicing(), invalid iNumMbInEachGom= %d from RC, iDid= %d, iCountNumMb= %d\n", iNumMbInEachGom, iCurDid, kiCountNumMb); return; } // do not adjust in case no extra iNumMbInEachGom based left for slicing adjustment, // extra MB of non integrated GOM assigned at the last pSlice in default, keep up on early initial result. if (iNumMbInEachGom * kiCountSliceNum >= kiCountNumMb) { return; } iMinimalMbNum = iNumMbInEachGom; } if (kiCountSliceNum < 2 || (kiCountSliceNum & 0x01)) // we need suppose uiSliceNum is even for multiple threading return; iMaximalMbNum = kiCountNumMb - (kiCountSliceNum - 1) * iMinimalMbNum; WelsEmms(); MT_TRACE_LOG (pCtx, WELS_LOG_DEBUG, "[MT] DynamicAdjustSlicing(), iDid= %d, iCountNumMb= %d\n", iCurDid, kiCountNumMb); iSliceIdx = 0; while (iSliceIdx + 1 < kiCountSliceNum) { int32_t iNumMbAssigning = WELS_ROUND(kiCountNumMb * pSliceComplexRatio[iSliceIdx]); // GOM boundary aligned if (pCtx->pSvcParam->iRCMode != RC_OFF_MODE) { iNumMbAssigning = WELS_ROUND (1.0f * iNumMbAssigning / iNumMbInEachGom) * iNumMbInEachGom; } // make sure one GOM at least in each pSlice for safe if (iNumMbAssigning < iMinimalMbNum) iNumMbAssigning = iMinimalMbNum; else if (iNumMbAssigning > iMaximalMbNum) iNumMbAssigning = iMaximalMbNum; assert (iNumMbAssigning > 0); iMbNumLeft -= iNumMbAssigning; if (iMbNumLeft <= 0) { // error due to we can not support slice_skip now yet, do not adjust this time assert (0); return; } iRunLen[iSliceIdx] = iNumMbAssigning; MT_TRACE_LOG (pCtx, WELS_LOG_DEBUG, "[MT] DynamicAdjustSlicing(), uiSliceIdx= %d, pSliceComplexRatio= %.2f, slice_run_org= %d, slice_run_adj= %d\n", iSliceIdx, pSliceComplexRatio[iSliceIdx], pSliceCtx->pCountMbNumInSlice[iSliceIdx], iNumMbAssigning); ++ iSliceIdx; iMaximalMbNum = iMbNumLeft - (kiCountSliceNum - iSliceIdx - 1) * iMinimalMbNum; // get maximal num_mb in left parts } iRunLen[iSliceIdx] = iMbNumLeft; MT_TRACE_LOG (pCtx, WELS_LOG_DEBUG, "[MT] DynamicAdjustSlicing(), iSliceIdx= %d, pSliceComplexRatio= %.2f, slice_run_org= %d, slice_run_adj= %d\n", iSliceIdx, pSliceComplexRatio[iSliceIdx], pSliceCtx->pCountMbNumInSlice[iSliceIdx], iMbNumLeft); if (DynamicAdjustSlicePEncCtxAll (pSliceCtx, iRunLen) == 0) { const int32_t kiThreadNum = pCtx->pSvcParam->iCountThreadsNum; int32_t iThreadIdx = 0; do { WelsEventSignal (&pCtx->pSliceThreading->pUpdateMbListEvent[iThreadIdx]); WelsEventSignal (&pCtx->pSliceThreading->pThreadMasterEvent[iThreadIdx]); ++ iThreadIdx; } while (iThreadIdx < kiThreadNum); WelsMultipleEventsWaitAllBlocking (kiThreadNum, &pCtx->pSliceThreading->pFinUpdateMbListEvent[0]); } }