[x265] [PATCH 2 of 3] Implementation for Intra-refresh

santhoshini at multicorewareinc.com santhoshini at multicorewareinc.com
Mon Sep 7 14:30:29 CEST 2015


# HG changeset patch
# User Santhoshini Sekar<santhoshini at multicorewareinc.com>
# Date 1441625035 -19800
#      Mon Sep 07 16:53:55 2015 +0530
# Node ID d91760d89cbd0e6f124fab60d7e4d684299b89a1
# Parent  2ba81f60f111c12a8f668ece13b23123702f2582
Implementation for Intra-refresh

diff -r 2ba81f60f111 -r d91760d89cbd source/common/cudata.cpp
--- a/source/common/cudata.cpp	Mon Sep 07 15:20:53 2015 +0530
+++ b/source/common/cudata.cpp	Mon Sep 07 16:53:55 2015 +0530
@@ -1813,6 +1813,14 @@
     int16_t ymax = (int16_t)((m_slice->m_sps->picHeightInLumaSamples + offset - m_cuPelY - 1) << mvshift);
     int16_t ymin = -(int16_t)((g_maxCUSize + offset + m_cuPelY - 1) << mvshift);
 
+    if (m_encData->m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)
+    {
+        int maxX = (m_slice->m_refFrameList[0][0]->m_encData->m_pir->pirEndPelX * 16 - 3) << mvshift;
+        int maxMv = maxX - 4* 16 * outMV.x;
+        if (maxMv > 0 && m_cuPelX < m_encData->m_pir->pirStartPelX)
+            xmax = X265_MIN(xmax, (int16_t)maxX);
+    }
+
     outMV.x = X265_MIN(xmax, X265_MAX(xmin, outMV.x));
     outMV.y = X265_MIN(ymax, X265_MAX(ymin, outMV.y));
 }
diff -r 2ba81f60f111 -r d91760d89cbd source/common/cudata.h
--- a/source/common/cudata.h	Mon Sep 07 15:20:53 2015 +0530
+++ b/source/common/cudata.h	Mon Sep 07 16:53:55 2015 +0530
@@ -208,6 +208,7 @@
     const CUData* m_cuAbove;          // pointer to above neighbor CTU
     const CUData* m_cuLeft;           // pointer to left neighbor CTU
 
+    int           bForceIntra;        // For Periodic Intra Refresh.Supported only in P-frames
     CUData();
 
     void     initialize(const CUDataMemPool& dataPool, uint32_t depth, int csp, int instance);
diff -r 2ba81f60f111 -r d91760d89cbd source/encoder/analysis.cpp
--- a/source/encoder/analysis.cpp	Mon Sep 07 15:20:53 2015 +0530
+++ b/source/encoder/analysis.cpp	Mon Sep 07 16:53:55 2015 +0530
@@ -170,6 +170,9 @@
     }
     else
     {
+        ctu.bForceIntra = m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+                          ctu.m_cuPelX / g_maxCUSize >= frame.m_encData->m_pir->pirStartPelX && ctu.m_cuPelX / g_maxCUSize < frame.m_encData->m_pir->pirEndPelX;
+
         if (!m_param->rdLevel)
         {
             /* In RD Level 0/1, copy source pixels into the reconstructed block so
@@ -828,7 +831,7 @@
     bool splitIntra = true;
     uint32_t splitRefs[4] = { 0, 0, 0, 0 };
     /* Step 1. Evaluate Merge/Skip candidates for likely early-outs */
-    if (mightNotSplit && depth >= minDepth)
+    if (mightNotSplit && depth >= minDepth && !parentCTU.bForceIntra)
     {
         /* Compute Merge Cost */
         md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);
@@ -909,7 +912,7 @@
         if (m_slice->m_pps->bUseDQP && depth <= m_slice->m_pps->maxCuDQPDepth && m_slice->m_pps->maxCuDQPDepth != 0)
             setLambdaFromQP(parentCTU, qp);
 
-        if (!earlyskip)
+        if (!earlyskip && !parentCTU.bForceIntra)
         {
             uint32_t refMasks[2];
             refMasks[0] = allSplitRefs;
@@ -1119,7 +1122,21 @@
                     }
                 }
             }
-        } // !earlyskip
+        }
+        if (!earlyskip && parentCTU.bForceIntra)
+        {
+            ProfileCounter(parentCTU, totalIntraCU[cuGeom.depth]);
+            md.pred[PRED_INTRA].cu.initSubCU(parentCTU, cuGeom, qp);
+            checkIntra(md.pred[PRED_INTRA], cuGeom, SIZE_2Nx2N, NULL, NULL);
+            checkBestMode(md.pred[PRED_INTRA], depth);
+
+            if (cuGeom.log2CUSize == 3 && m_slice->m_sps->quadtreeTULog2MinSize < 3)
+            {
+                md.pred[PRED_INTRA_NxN].cu.initSubCU(parentCTU, cuGeom, qp);
+                checkIntra(md.pred[PRED_INTRA_NxN], cuGeom, SIZE_NxN, NULL, NULL);
+                checkBestMode(md.pred[PRED_INTRA_NxN], depth);
+            }
+        }// !earlyskip
 
         if (m_bTryLossless)
             tryLossless(cuGeom);
@@ -1189,23 +1206,36 @@
     {
         uint8_t* reuseDepth  = &m_reuseInterDataCTU->depth[parentCTU.m_cuAddr * parentCTU.m_numPartitions];
         uint8_t* reuseModes  = &m_reuseInterDataCTU->modes[parentCTU.m_cuAddr * parentCTU.m_numPartitions];
-        if (mightNotSplit && depth == reuseDepth[zOrder] && zOrder == cuGeom.absPartIdx && reuseModes[zOrder] == MODE_SKIP)
+        if (!parentCTU.bForceIntra)
         {
-            md.pred[PRED_SKIP].cu.initSubCU(parentCTU, cuGeom, qp);
-            md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);
-            checkMerge2Nx2N_rd5_6(md.pred[PRED_SKIP], md.pred[PRED_MERGE], cuGeom, true);
+            if (mightNotSplit && depth == reuseDepth[zOrder] && zOrder == cuGeom.absPartIdx && reuseModes[zOrder] == MODE_SKIP)
+            {
+                md.pred[PRED_SKIP].cu.initSubCU(parentCTU, cuGeom, qp);
+                md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);
+                checkMerge2Nx2N_rd5_6(md.pred[PRED_SKIP], md.pred[PRED_MERGE], cuGeom, true);
 
-            if (m_bTryLossless)
-                tryLossless(cuGeom);
+                if (m_bTryLossless)
+                    tryLossless(cuGeom);
 
-            if (mightSplit)
-                addSplitFlagCost(*md.bestMode, cuGeom.depth);
+                if (mightSplit)
+                    addSplitFlagCost(*md.bestMode, cuGeom.depth);
 
-            // increment zOrder offset to point to next best depth in sharedDepth buffer
-            zOrder += g_depthInc[g_maxCUDepth - 1][reuseDepth[zOrder]];
+                // increment zOrder offset to point to next best depth in sharedDepth buffer
+                zOrder += g_depthInc[g_maxCUDepth - 1][reuseDepth[zOrder]];
 
-            mightSplit = false;
-            mightNotSplit = false;
+                mightSplit = false;
+                mightNotSplit = false;
+            }
+        }
+        else
+        {
+            char* reusePartSizes = &m_reuseIntraDataCTU->partSizes[parentCTU.m_cuAddr * parentCTU.m_numPartitions];
+            uint8_t* reuseChromaModes = &m_reuseIntraDataCTU->chromaModes[parentCTU.m_cuAddr * parentCTU.m_numPartitions];
+            PartSize size = (PartSize)reusePartSizes[zOrder];
+            Mode& mode = size == SIZE_2Nx2N ? md.pred[PRED_INTRA] : md.pred[PRED_INTRA_NxN];
+            int ipOffset = (int)(6.0 * X265_LOG2(m_param->rc.ipFactor) + 0.5);
+            mode.cu.initSubCU(parentCTU, cuGeom, X265_MAX(mode.cu.m_qp[0] - ipOffset, QP_MIN));
+            checkIntra(mode, cuGeom, size, &reuseModes[zOrder], &reuseChromaModes[zOrder]);
         }
     }
 
@@ -1213,7 +1243,7 @@
     bool splitIntra = true;
     uint32_t splitRefs[4] = { 0, 0, 0, 0 };
     /* Step 1. Evaluate Merge/Skip candidates for likely early-outs */
-    if (mightNotSplit)
+    if (mightNotSplit && !parentCTU.bForceIntra)
     {
         md.pred[PRED_SKIP].cu.initSubCU(parentCTU, cuGeom, qp);
         md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);
@@ -1282,7 +1312,7 @@
         if (m_slice->m_pps->bUseDQP && depth <= m_slice->m_pps->maxCuDQPDepth && m_slice->m_pps->maxCuDQPDepth != 0)
             setLambdaFromQP(parentCTU, qp);
 
-        if (!(foundSkip && m_param->bEnableEarlySkip))
+        if (!(foundSkip && m_param->bEnableEarlySkip) && !parentCTU.bForceIntra)
         {
             uint32_t refMasks[2];
             refMasks[0] = allSplitRefs;
@@ -1389,6 +1419,20 @@
                 }
             }
         }
+        if (!(foundSkip && m_param->bEnableEarlySkip) && parentCTU.bForceIntra)
+        {
+            ProfileCounter(parentCTU, totalIntraCU[cuGeom.depth]);
+            md.pred[PRED_INTRA].cu.initSubCU(parentCTU, cuGeom, qp);
+            checkIntra(md.pred[PRED_INTRA], cuGeom, SIZE_2Nx2N, NULL, NULL);
+            checkBestMode(md.pred[PRED_INTRA], depth);
+
+            if (cuGeom.log2CUSize == 3 && m_slice->m_sps->quadtreeTULog2MinSize < 3)
+            {
+                md.pred[PRED_INTRA_NxN].cu.initSubCU(parentCTU, cuGeom, qp);
+                checkIntra(md.pred[PRED_INTRA_NxN], cuGeom, SIZE_NxN, NULL, NULL);
+                checkBestMode(md.pred[PRED_INTRA_NxN], depth);
+            }
+        }
 
         if (m_bTryLossless)
             tryLossless(cuGeom);
@@ -1464,7 +1508,13 @@
             (candMvField[i][0].mv.y >= (m_param->searchRange + 1) * 4 ||
             candMvField[i][1].mv.y >= (m_param->searchRange + 1) * 4))
             continue;
-
+        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)
+        {
+            int maxX = (m_slice->m_refFrameList[0][0]->m_encData->m_pir->pirEndPelX * 16 - 3) * 4;
+            int maxMv = maxX - 4 * 16 * candMvField[i][0].mv.x;
+            if (maxMv > 0 && tempPred->cu.m_cuPelX < m_frame->m_encData->m_pir->pirEndPelX)
+                continue;
+        }
         tempPred->cu.m_mvpIdx[0][0] = (uint8_t)i; // merge candidate ID is stored in L0 MVP idx
         X265_CHECK(m_slice->m_sliceType == B_SLICE || !(candDir[i] & 0x10), " invalid merge for P slice\n");
         tempPred->cu.m_interDir[0] = candDir[i];
@@ -1592,7 +1642,13 @@
                 continue;
             triedBZero = true;
         }
-
+        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)
+        {
+            int maxX = (m_slice->m_refFrameList[0][0]->m_encData->m_pir->pirEndPelX * 16 - 3) * 4;
+            int maxMv = maxX - 4 * 16 * candMvField[i][0].mv.x;
+            if (maxMv > 0 && tempPred->cu.m_cuPelX < m_frame->m_encData->m_pir->pirEndPelX)
+                continue;
+        }
         tempPred->cu.m_mvpIdx[0][0] = (uint8_t)i;    /* merge candidate ID is stored in L0 MVP idx */
         tempPred->cu.m_interDir[0] = candDir[i];
         tempPred->cu.m_mv[0][0] = candMvField[i][0].mv;
diff -r 2ba81f60f111 -r d91760d89cbd source/encoder/api.cpp
--- a/source/encoder/api.cpp	Mon Sep 07 15:20:53 2015 +0530
+++ b/source/encoder/api.cpp	Mon Sep 07 16:53:55 2015 +0530
@@ -245,6 +245,12 @@
     }
 }
 
+void x265_encoder_intra_refresh(x265_encoder *enc)
+{
+    Encoder *encoder = static_cast<Encoder*>(enc);
+    encoder->m_param->bQueuedIntraRefresh = 1;
+}
+
 void x265_cleanup(void)
 {
     if (!g_ctuSizeConfigured)
diff -r 2ba81f60f111 -r d91760d89cbd source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Mon Sep 07 15:20:53 2015 +0530
+++ b/source/encoder/encoder.cpp	Mon Sep 07 16:53:55 2015 +0530
@@ -767,6 +767,43 @@
             if (m_param->rc.rateControlMode != X265_RC_CQP)
                 m_lookahead->getEstimatedPictureCost(frameEnc);
 
+            if (m_param->bIntraRefresh)
+            {
+                Slice* slice = frameEnc->m_encData->m_slice;
+                if (slice->m_sliceType == I_SLICE)
+                {
+                    frameEnc->m_encData->m_pir->framesSinceLastPir = 0;
+                    m_param->bQueuedIntraRefresh = 0;
+                    /* PIR is currently only supported with ref == 1, so any intra frame effectively refreshes
+                     * the whole frame and counts as an intra refresh. */
+                    frameEnc->m_encData->m_pir->position = frameEnc->m_lowres.maxBlocksInRow;
+                }
+                else if (slice->m_sliceType == P_SLICE)
+                {
+                    Frame* ref = frameEnc->m_encData->m_slice->m_refFrameList[0][0];
+                    int pocdiff = (frameEnc->m_poc - ref->m_poc);
+                    float increment = X265_MAX(((float)frameEnc->m_lowres.maxBlocksInRow - 1) / m_param->keyframeMax, 1);
+                    frameEnc->m_encData->m_pir->position = ref->m_encData->m_pir->position;
+                    frameEnc->m_encData->m_pir->framesSinceLastPir = ref->m_encData->m_pir->framesSinceLastPir + pocdiff;
+                    if (frameEnc->m_encData->m_pir->framesSinceLastPir >= m_param->keyframeMax ||
+                        (m_param->bQueuedIntraRefresh && frameEnc->m_encData->m_pir->position + 0.5 >= frameEnc->m_lowres.maxBlocksInRow))
+                    {
+                        frameEnc->m_encData->m_pir->position = 0;
+                        frameEnc->m_encData->m_pir->framesSinceLastPir = 0;
+                        m_param->bQueuedIntraRefresh = 0;
+                        frameEnc->m_lowres.bKeyframe = 1;
+                    }
+                    frameEnc->m_encData->m_pir->pirStartPelX = (uint32_t)(frameEnc->m_encData->m_pir->position + 0.5);
+                    frameEnc->m_encData->m_pir->position += increment * pocdiff;
+                    frameEnc->m_encData->m_pir->pirEndPelX = (uint32_t)(frameEnc->m_encData->m_pir->position + 0.5);
+                    /* If our intra refresh has reached the right side of the frame, we're done. */
+                    if (frameEnc->m_encData->m_pir->pirEndPelX >= frameEnc->m_lowres.maxBlocksInRow - 1)
+                    {
+                        frameEnc->m_encData->m_pir->position = frameEnc->m_lowres.maxBlocksInRow;
+                        frameEnc->m_encData->m_pir->pirEndPelX = frameEnc->m_lowres.maxBlocksInRow - 1;
+                    }
+                }
+            }
             /* Allow FrameEncoder::compressFrame() to start in the frame encoder thread */
             if (!curEncoder->startCompressFrame(frameEnc))
                 m_aborted = true;
diff -r 2ba81f60f111 -r d91760d89cbd source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp	Mon Sep 07 15:20:53 2015 +0530
+++ b/source/encoder/slicetype.cpp	Mon Sep 07 16:53:55 2015 +0530
@@ -774,6 +774,7 @@
             for (uint32_t cnt = 0; cnt < scale && lowresRow < heightInLowresCu; lowresRow++, cnt++)
             {
                 sum = 0; intraSum = 0;
+                int diff = 0;
                 lowresCuIdx = lowresRow * widthInLowresCu;
                 for (lowresCol = 0; lowresCol < widthInLowresCu; lowresCol++, lowresCuIdx++)
                 {
@@ -781,14 +782,18 @@
                     if (qp_offset)
                     {
                         lowresCuCost = (uint16_t)((lowresCuCost * x265_exp2fix8(qp_offset[lowresCuIdx]) + 128) >> 8);
-                        int32_t intraCuCost = curFrame->m_lowres.intraCost[lowresCuIdx]; 
+                        int32_t intraCuCost = curFrame->m_lowres.intraCost[lowresCuIdx];
                         curFrame->m_lowres.intraCost[lowresCuIdx] = (intraCuCost * x265_exp2fix8(qp_offset[lowresCuIdx]) + 128) >> 8;
                     }
+                    if (m_param->bIntraRefresh && slice->m_sliceType == X265_TYPE_P)
+                        for (uint32_t x = curFrame->m_encData->m_pir->pirStartPelX; x <= curFrame->m_encData->m_pir->pirEndPelX; x++)
+                            diff += curFrame->m_lowres.intraCost[lowresCuIdx] - lowresCuCost;
                     curFrame->m_lowres.lowresCostForRc[lowresCuIdx] = lowresCuCost;
                     sum += lowresCuCost;
                     intraSum += curFrame->m_lowres.intraCost[lowresCuIdx];
                 }
                 curFrame->m_encData->m_rowStat[row].satdForVbv += sum;
+                curFrame->m_encData->m_rowStat[row].satdForVbv += diff;
                 curFrame->m_encData->m_rowStat[row].intraSatdForVbv += intraSum;
             }
         }
@@ -900,8 +905,7 @@
             x265_log(m_param, X265_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid and %d reference frames\n",
                      frm.sliceType, m_param->maxNumReferences);
         }
-
-        if (/* (!param->intraRefresh || frm.frameNum == 0) && */ frm.frameNum - m_lastKeyframe >= m_param->keyframeMax)
+        if ((!m_param->bIntraRefresh || frm.frameNum == 0) && frm.frameNum - m_lastKeyframe >= m_param->keyframeMax)
         {
             if (frm.sliceType == X265_TYPE_AUTO || frm.sliceType == X265_TYPE_I)
                 frm.sliceType = m_param->bOpenGOP && m_lastKeyframe >= 0 ? X265_TYPE_I : X265_TYPE_IDR;
@@ -1184,7 +1188,7 @@
     frames[framecnt + 1] = NULL;
 
     keyintLimit = m_param->keyframeMax - frames[0]->frameNum + m_lastKeyframe - 1;
-    origNumFrames = numFrames = X265_MIN(framecnt, keyintLimit);
+    origNumFrames = numFrames = m_param->bIntraRefresh ? framecnt : X265_MIN(framecnt, keyintLimit);
 
     if (bIsVbvLookahead)
         numFrames = framecnt;
@@ -1384,7 +1388,7 @@
     if (m_param->rc.cuTree)
         cuTree(frames, X265_MIN(numFrames, m_param->keyframeMax), bKeyframe);
 
-    // if (!param->bIntraRefresh)
+     if (!m_param->bIntraRefresh)
     for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)
     {
         frames[j]->sliceType = X265_TYPE_I;
@@ -1504,7 +1508,7 @@
     {
         if (m_param->keyframeMin == m_param->keyframeMax)
             threshMin = threshMax;
-        if (gopSize <= m_param->keyframeMin / 4)
+        if (gopSize <= m_param->keyframeMin / 4 || m_param->bIntraRefresh)
             bias = threshMin / 4;
         else if (gopSize <= m_param->keyframeMin)
             bias = threshMin * gopSize / m_param->keyframeMin;
diff -r 2ba81f60f111 -r d91760d89cbd source/x265.h
--- a/source/x265.h	Mon Sep 07 15:20:53 2015 +0530
+++ b/source/x265.h	Mon Sep 07 16:53:55 2015 +0530
@@ -707,6 +707,8 @@
      * big keyframe, the keyframe is "spread" over many frames. */
     int       bIntraRefresh;
 
+    int       bQueuedIntraRefresh;
+
     /*== Coding Unit (CU) definitions ==*/
 
     /* Maximum CU width and height in pixels.  The size must be 64, 32, or 16.
@@ -1373,6 +1375,22 @@
  *      close an encoder handler */
 void x265_encoder_close(x265_encoder *);
 
+/* x265_encoder_intra_refresh:
+ *      If an intra refresh is not in progress, begin one with the next P-frame.
+ *      If an intra refresh is in progress, begin one as soon as the current one finishes.
+ *      Requires bIntraRefresh to be set.
+ *
+ *      Useful for interactive streaming where the client can tell the server that packet loss has
+ *      occurred.  In this case, keyint can be set to an extremely high value so that intra refreshes
+ *      occur only when calling x265_encoder_intra_refresh.
+ *
+ *      In multi-pass encoding, if x265_encoder_intra_refresh is called differently in each pass,
+ *      behavior is undefined.
+ *
+ *      Should not be called during an x265_encoder_encode. */
+
+void x265_encoder_intra_refresh(x265_encoder *);
+
 /* x265_cleanup:
  *       release library static allocations, reset configured CTU size */
 void x265_cleanup(void);


More information about the x265-devel mailing list