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

santhoshini at multicorewareinc.com santhoshini at multicorewareinc.com
Thu Sep 24 05:30:01 CEST 2015


# HG changeset patch
# User Santhoshini Sekar<santhoshini at multicorewareinc.com>
# Date 1442393819 -19800
#      Wed Sep 16 14:26:59 2015 +0530
# Node ID 97b62cb57bfa171e50d3fa736527634bb507cbe5
# Parent  98c0dcd5a10b8806aa1ceb775ff9342f7a7ae6c6
Implementation for Intra refresh

diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/analysis.cpp
--- a/source/encoder/analysis.cpp	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/analysis.cpp	Wed Sep 16 14:26:59 2015 +0530
@@ -171,10 +171,14 @@
     }
     else
     {
-        if (!m_param->rdLevel)
+        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+            ctu.m_cuPelX / g_maxCUSize >= frame.m_encData->m_pir.pirStartCol
+            && ctu.m_cuPelX / g_maxCUSize < frame.m_encData->m_pir.pirEndCol)
+            compressIntraCU(ctu, cuGeom, zOrder, qp);
+        else if (!m_param->rdLevel)
         {
             /* In RD Level 0/1, copy source pixels into the reconstructed block so
-            * they are available for intra predictions */
+             * they are available for intra predictions */
             m_modeDepth[0].fencYuv.copyToPicYuv(*m_frame->m_reconPic, ctu.m_cuAddr, 0);
 
             compressInterCU_rd0_4(ctu, cuGeom, qp);
@@ -1458,13 +1462,23 @@
     bestPred->sa8dCost = MAX_INT64;
     int bestSadCand = -1;
     int sizeIdx = cuGeom.log2CUSize - 2;
-
+    int safeX, maxSafeMv;
+    if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)
+    {
+        safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;
+        maxSafeMv = (safeX - tempPred->cu.m_cuPelX) * 4;
+    }
     for (uint32_t i = 0; i < numMergeCand; ++i)
     {
         if (m_bFrameParallel &&
             (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 &&
+            tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndCol &&
+            candMvField[i][0].mv.x > maxSafeMv)
+            // skip merge candidates which reference beyond safe reference area
+            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");
@@ -1570,7 +1584,12 @@
         first = *m_reuseBestMergeCand;
         last = first + 1;
     }
-
+    int safeX, maxSafeMv;
+    if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)
+    {
+        safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;
+        maxSafeMv = (safeX - tempPred->cu.m_cuPelX) * 4;
+    }
     for (uint32_t i = first; i < last; i++)
     {
         if (m_bFrameParallel &&
@@ -1593,7 +1612,11 @@
                 continue;
             triedBZero = true;
         }
-
+        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+            tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndCol &&
+            candMvField[i][0].mv.x > maxSafeMv)
+            // skip merge candidates which reference beyond safe reference area
+            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 98c0dcd5a10b -r 97b62cb57bfa source/encoder/api.cpp
--- a/source/encoder/api.cpp	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/api.cpp	Wed Sep 16 14:26:59 2015 +0530
@@ -245,6 +245,16 @@
     }
 }
 
+int x265_encoder_intra_refresh(x265_encoder *enc)
+{
+    if (!enc)
+        return -1;
+
+    Encoder *encoder = static_cast<Encoder*>(enc);
+    encoder->m_param->bQueuedIntraRefresh = 1;
+    return 0;
+}
+
 void x265_cleanup(void)
 {
     if (!g_ctuSizeConfigured)
@@ -317,6 +327,7 @@
     &x265_cleanup,
 
     sizeof(x265_frame_stats),
+    &x265_encoder_intra_refresh,
 };
 
 typedef const x265_api* (*api_get_func)(int bitDepth);
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/encoder.cpp	Wed Sep 16 14:26:59 2015 +0530
@@ -437,6 +437,46 @@
     }
 }
 
+void Encoder::calcRefreshInterval(Frame* frameEnc)
+{
+    Slice* slice = frameEnc->m_encData->m_slice;
+    uint32_t numBlocksInRow = slice->m_sps->numCuInWidth;
+    FrameData::PeriodicIR* pir = &frameEnc->m_encData->m_pir;
+    if (slice->m_sliceType == I_SLICE)
+    {
+        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. */
+        pir->position = numBlocksInRow;
+    }
+    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)numBlocksInRow - 1) / m_param->keyframeMax, 1);
+        pir->position = ref->m_encData->m_pir.position;
+        pir->framesSinceLastPir = ref->m_encData->m_pir.framesSinceLastPir + pocdiff;
+        if (pir->framesSinceLastPir >= m_param->keyframeMax ||
+            (m_param->bQueuedIntraRefresh && pir->position + 0.5 >= numBlocksInRow))
+        {
+            pir->position = 0;
+            pir->framesSinceLastPir = 0;
+            m_param->bQueuedIntraRefresh = 0;
+            frameEnc->m_lowres.bKeyframe = 1;
+        }
+        pir->pirStartCol = (uint32_t)(pir->position + 0.5);
+        pir->position += increment * pocdiff;
+        pir->pirEndCol = (uint32_t)(pir->position + 0.5);
+        /* If our intra refresh has reached the right side of the frame, we're done. */
+        if (pir->pirEndCol >= numBlocksInRow)
+        {
+            pir->position = numBlocksInRow;
+            pir->pirEndCol = numBlocksInRow;
+        }
+    }
+}
+
 /**
  * Feed one new input frame into the encoder, get one frame out. If pic_in is
  * NULL, a flush condition is implied and pic_in must be NULL for all subsequent
@@ -768,6 +808,8 @@
 
             if (m_param->rc.rateControlMode != X265_RC_CQP)
                 m_lookahead->getEstimatedPictureCost(frameEnc);
+             if (m_param->bIntraRefresh)
+                 calcRefreshInterval(frameEnc);
 
             /* Allow FrameEncoder::compressFrame() to start in the frame encoder thread */
             if (!curEncoder->startCompressFrame(frameEnc))
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/encoder.h
--- a/source/encoder/encoder.h	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/encoder.h	Wed Sep 16 14:26:59 2015 +0530
@@ -168,6 +168,8 @@
 
     void finishFrameStats(Frame* pic, FrameEncoder *curEncoder, uint64_t bits, x265_frame_stats* frameStats);
 
+    void calcRefreshInterval(Frame* frameEnc);
+
 protected:
 
     void initVPS(VPS *vps);
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/search.cpp
--- a/source/encoder/search.cpp	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/search.cpp	Wed Sep 16 14:26:59 2015 +0530
@@ -2460,6 +2460,17 @@
     cu.clipMv(mvmin);
     cu.clipMv(mvmax);
 
+    if (cu.m_encData->m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&
+          cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirStartCol &&
+          m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol < m_slice->m_sps->numCuInWidth)
+    {
+        int safeX, maxSafeMv;
+        safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;
+        maxSafeMv = (safeX - cu.m_cuPelX) * 4;
+        mvmax.x = X265_MIN(mvmax.x, maxSafeMv);
+        mvmin.x = X265_MIN(mvmin.x, maxSafeMv);
+    }
+
     /* Clip search range to signaled maximum MV length.
      * We do not support this VUI field being changed from the default */
     const int maxMvLen = (1 << 15) - 1;
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/encoder/slicetype.cpp	Wed Sep 16 14:26:59 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.pirStartCol; x <= curFrame->m_encData->m_pir.pirEndCol; 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,12 +1388,12 @@
     if (m_param->rc.cuTree)
         cuTree(frames, X265_MIN(numFrames, m_param->keyframeMax), bKeyframe);
 
-    // if (!param->bIntraRefresh)
-    for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)
-    {
-        frames[j]->sliceType = X265_TYPE_I;
-        resetStart = X265_MIN(resetStart, j + 1);
-    }
+    if (!m_param->bIntraRefresh)
+        for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)
+        {
+            frames[j]->sliceType = X265_TYPE_I;
+            resetStart = X265_MIN(resetStart, j + 1);
+        }
 
     if (bIsVbvLookahead)
         vbvLookahead(frames, numFrames, bKeyframe);
@@ -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 98c0dcd5a10b -r 97b62cb57bfa source/x265.def.in
--- a/source/x265.def.in	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/x265.def.in	Wed Sep 16 14:26:59 2015 +0530
@@ -22,3 +22,4 @@
 x265_cleanup
 x265_api_get_${X265_BUILD}
 x265_api_query
+x265_encoder_intra_refresh
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/x265.h
--- a/source/x265.h	Wed Sep 09 14:52:35 2015 +0530
+++ b/source/x265.h	Wed Sep 16 14:26:59 2015 +0530
@@ -708,6 +708,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.
@@ -1379,6 +1381,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. */
+
+int x265_encoder_intra_refresh(x265_encoder *);
+
 /* x265_cleanup:
  *       release library static allocations, reset configured CTU size */
 void x265_cleanup(void);
@@ -1426,6 +1444,7 @@
     void          (*cleanup)(void);
 
     int           sizeof_frame_stats;   /* sizeof(x265_frame_stats) */
+    int           (*encoder_intra_refresh)(x265_encoder*);
     /* add new pointers to the end, or increment X265_MAJOR_VERSION */
 } x265_api;
 


More information about the x265-devel mailing list