<div dir="ltr"><div>So, why should maxNumReferences be 1 always? <br><br></div>I'm nicely confused by the requirement here. When intra-refresh is enabled (even if the user did not call  the API function x265_intra_refresh), all I frames are converted to P frames. The responsibility of the API call is only to start a mid-GOP refresh. Does this require a no-scenecut, if not, what happens when a scenecut is inserted mid-GOP? Trigger a refresh?<br><br><div><div><div class="gmail_extra"><div class="gmail_quote">On Thu, Sep 24, 2015 at 9:00 AM,  <span dir="ltr"><<a href="mailto:santhoshini@multicorewareinc.com" target="_blank">santhoshini@multicorewareinc.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"># HG changeset patch<br>
# User Santhoshini Sekar<<a href="mailto:santhoshini@multicorewareinc.com" target="_blank">santhoshini@multicorewareinc.com</a>><br>
# Date 1442393819 -19800<br>
#      Wed Sep 16 14:26:59 2015 +0530<br>
# Node ID 97b62cb57bfa171e50d3fa736527634bb507cbe5<br>
# Parent  98c0dcd5a10b8806aa1ceb775ff9342f7a7ae6c6<br>
Implementation for Intra refresh<br>
<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/analysis.cpp<br>
--- a/source/encoder/analysis.cpp       Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/encoder/analysis.cpp       Wed Sep 16 14:26:59 2015 +0530<br>
@@ -171,10 +171,14 @@<br>
     }<br>
     else<br>
     {<br>
-        if (!m_param->rdLevel)<br>
+        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&<br>
+            ctu.m_cuPelX / g_maxCUSize >= frame.m_encData->m_pir.pirStartCol<br>
+            && ctu.m_cuPelX / g_maxCUSize < frame.m_encData->m_pir.pirEndCol)<br>
+            compressIntraCU(ctu, cuGeom, zOrder, qp);<br>
+        else if (!m_param->rdLevel)<br>
         {<br>
             /* In RD Level 0/1, copy source pixels into the reconstructed block so<br>
-            * they are available for intra predictions */<br>
+             * they are available for intra predictions */<br>
             m_modeDepth[0].fencYuv.copyToPicYuv(*m_frame->m_reconPic, ctu.m_cuAddr, 0);<br>
<br>
             compressInterCU_rd0_4(ctu, cuGeom, qp);<br>
@@ -1458,13 +1462,23 @@<br>
     bestPred->sa8dCost = MAX_INT64;<br>
     int bestSadCand = -1;<br>
     int sizeIdx = cuGeom.log2CUSize - 2;<br>
-<br>
+    int safeX, maxSafeMv;<br>
+    if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)<br>
+    {<br>
+        safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;<br>
+        maxSafeMv = (safeX - tempPred->cu.m_cuPelX) * 4;<br>
+    }<br>
     for (uint32_t i = 0; i < numMergeCand; ++i)<br>
     {<br>
         if (m_bFrameParallel &&<br>
             (candMvField[i][0].mv.y >= (m_param->searchRange + 1) * 4 ||<br>
             candMvField[i][1].mv.y >= (m_param->searchRange + 1) * 4))<br>
             continue;<br>
+        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&<br>
+            tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndCol &&<br>
+            candMvField[i][0].mv.x > maxSafeMv)<br>
+            // skip merge candidates which reference beyond safe reference area<br>
+            continue;<br>
<br>
         tempPred->cu.m_mvpIdx[0][0] = (uint8_t)i; // merge candidate ID is stored in L0 MVP idx<br>
         X265_CHECK(m_slice->m_sliceType == B_SLICE || !(candDir[i] & 0x10), " invalid merge for P slice\n");<br>
@@ -1570,7 +1584,12 @@<br>
         first = *m_reuseBestMergeCand;<br>
         last = first + 1;<br>
     }<br>
-<br>
+    int safeX, maxSafeMv;<br>
+    if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)<br>
+    {<br>
+        safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;<br>
+        maxSafeMv = (safeX - tempPred->cu.m_cuPelX) * 4;<br>
+    }<br>
     for (uint32_t i = first; i < last; i++)<br>
     {<br>
         if (m_bFrameParallel &&<br>
@@ -1593,7 +1612,11 @@<br>
                 continue;<br>
             triedBZero = true;<br>
         }<br>
-<br>
+        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&<br>
+            tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndCol &&<br>
+            candMvField[i][0].mv.x > maxSafeMv)<br>
+            // skip merge candidates which reference beyond safe reference area<br>
+            continue;<br>
         tempPred->cu.m_mvpIdx[0][0] = (uint8_t)i;    /* merge candidate ID is stored in L0 MVP idx */<br>
         tempPred->cu.m_interDir[0] = candDir[i];<br>
         tempPred->cu.m_mv[0][0] = candMvField[i][0].mv;<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/api.cpp<br>
--- a/source/encoder/api.cpp    Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/encoder/api.cpp    Wed Sep 16 14:26:59 2015 +0530<br>
@@ -245,6 +245,16 @@<br>
     }<br>
 }<br>
<br>
+int x265_encoder_intra_refresh(x265_encoder *enc)<br>
+{<br>
+    if (!enc)<br>
+        return -1;<br>
+<br>
+    Encoder *encoder = static_cast<Encoder*>(enc);<br>
+    encoder->m_param->bQueuedIntraRefresh = 1;<br>
+    return 0;<br>
+}<br>
+<br>
 void x265_cleanup(void)<br>
 {<br>
     if (!g_ctuSizeConfigured)<br>
@@ -317,6 +327,7 @@<br>
     &x265_cleanup,<br>
<br>
     sizeof(x265_frame_stats),<br>
+    &x265_encoder_intra_refresh,<br>
 };<br>
<br>
 typedef const x265_api* (*api_get_func)(int bitDepth);<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/encoder.cpp<br>
--- a/source/encoder/encoder.cpp        Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/encoder/encoder.cpp        Wed Sep 16 14:26:59 2015 +0530<br>
@@ -437,6 +437,46 @@<br>
     }<br>
 }<br>
<br>
+void Encoder::calcRefreshInterval(Frame* frameEnc)<br>
+{<br>
+    Slice* slice = frameEnc->m_encData->m_slice;<br>
+    uint32_t numBlocksInRow = slice->m_sps->numCuInWidth;<br>
+    FrameData::PeriodicIR* pir = &frameEnc->m_encData->m_pir;<br>
+    if (slice->m_sliceType == I_SLICE)<br>
+    {<br>
+        pir->framesSinceLastPir = 0;<br>
+        m_param->bQueuedIntraRefresh = 0;<br>
+        /* PIR is currently only supported with ref == 1, so any intra frame effectively refreshes<br>
+         * the whole frame and counts as an intra refresh. */<br>
+        pir->position = numBlocksInRow;<br>
+    }<br>
+    else if (slice->m_sliceType == P_SLICE)<br>
+    {<br>
+        Frame* ref = frameEnc->m_encData->m_slice->m_refFrameList[0][0];<br>
+        int pocdiff = frameEnc->m_poc - ref->m_poc;<br>
+        float increment = X265_MAX(((float)numBlocksInRow - 1) / m_param->keyframeMax, 1);<br>
+        pir->position = ref->m_encData->m_pir.position;<br>
+        pir->framesSinceLastPir = ref->m_encData->m_pir.framesSinceLastPir + pocdiff;<br>
+        if (pir->framesSinceLastPir >= m_param->keyframeMax ||<br>
+            (m_param->bQueuedIntraRefresh && pir->position + 0.5 >= numBlocksInRow))<br>
+        {<br>
+            pir->position = 0;<br>
+            pir->framesSinceLastPir = 0;<br>
+            m_param->bQueuedIntraRefresh = 0;<br>
+            frameEnc->m_lowres.bKeyframe = 1;<br>
+        }<br>
+        pir->pirStartCol = (uint32_t)(pir->position + 0.5);<br>
+        pir->position += increment * pocdiff;<br>
+        pir->pirEndCol = (uint32_t)(pir->position + 0.5);<br>
+        /* If our intra refresh has reached the right side of the frame, we're done. */<br>
+        if (pir->pirEndCol >= numBlocksInRow)<br>
+        {<br>
+            pir->position = numBlocksInRow;<br>
+            pir->pirEndCol = numBlocksInRow;<br>
+        }<br>
+    }<br>
+}<br>
+<br>
 /**<br>
  * Feed one new input frame into the encoder, get one frame out. If pic_in is<br>
  * NULL, a flush condition is implied and pic_in must be NULL for all subsequent<br>
@@ -768,6 +808,8 @@<br>
<br>
             if (m_param->rc.rateControlMode != X265_RC_CQP)<br>
                 m_lookahead->getEstimatedPictureCost(frameEnc);<br>
+             if (m_param->bIntraRefresh)<br>
+                 calcRefreshInterval(frameEnc);<br>
<br>
             /* Allow FrameEncoder::compressFrame() to start in the frame encoder thread */<br>
             if (!curEncoder->startCompressFrame(frameEnc))<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/encoder.h<br>
--- a/source/encoder/encoder.h  Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/encoder/encoder.h  Wed Sep 16 14:26:59 2015 +0530<br>
@@ -168,6 +168,8 @@<br>
<br>
     void finishFrameStats(Frame* pic, FrameEncoder *curEncoder, uint64_t bits, x265_frame_stats* frameStats);<br>
<br>
+    void calcRefreshInterval(Frame* frameEnc);<br>
+<br>
 protected:<br>
<br>
     void initVPS(VPS *vps);<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/search.cpp<br>
--- a/source/encoder/search.cpp Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/encoder/search.cpp Wed Sep 16 14:26:59 2015 +0530<br>
@@ -2460,6 +2460,17 @@<br>
     cu.clipMv(mvmin);<br>
     cu.clipMv(mvmax);<br>
<br>
+    if (cu.m_encData->m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&<br>
+          cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirStartCol &&<br>
+          m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol < m_slice->m_sps->numCuInWidth)<br></blockquote><div><br></div><div>Why check for m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol < m_slice->m_sps->numCuInWidth here? This should be taken care of in calcRefreshInterval.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+    {<br>
+        int safeX, maxSafeMv;<br>
+        safeX = m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndCol * g_maxCUSize - 3;<br>
+        maxSafeMv = (safeX - cu.m_cuPelX) * 4;<br>
+        mvmax.x = X265_MIN(mvmax.x, maxSafeMv);<br>
+        mvmin.x = X265_MIN(mvmin.x, maxSafeMv);<br>
+    }<br>
+<br></blockquote><div></div><div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
     /* Clip search range to signaled maximum MV length.<br>
      * We do not support this VUI field being changed from the default */<br>
     const int maxMvLen = (1 << 15) - 1;<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/encoder/slicetype.cpp<br>
--- a/source/encoder/slicetype.cpp      Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/encoder/slicetype.cpp      Wed Sep 16 14:26:59 2015 +0530<br>
@@ -774,6 +774,7 @@<br>
             for (uint32_t cnt = 0; cnt < scale && lowresRow < heightInLowresCu; lowresRow++, cnt++)<br>
             {<br>
                 sum = 0; intraSum = 0;<br>
+                int diff = 0;<br>
                 lowresCuIdx = lowresRow * widthInLowresCu;<br>
                 for (lowresCol = 0; lowresCol < widthInLowresCu; lowresCol++, lowresCuIdx++)<br>
                 {<br>
@@ -781,14 +782,18 @@<br>
                     if (qp_offset)<br>
                     {<br>
                         lowresCuCost = (uint16_t)((lowresCuCost * x265_exp2fix8(qp_offset[lowresCuIdx]) + 128) >> 8);<br>
-                        int32_t intraCuCost = curFrame->m_lowres.intraCost[lowresCuIdx];<br>
+                        int32_t intraCuCost = curFrame->m_lowres.intraCost[lowresCuIdx];<br>
                         curFrame->m_lowres.intraCost[lowresCuIdx] = (intraCuCost * x265_exp2fix8(qp_offset[lowresCuIdx]) + 128) >> 8;<br>
                     }<br>
+                    if (m_param->bIntraRefresh && slice->m_sliceType == X265_TYPE_P)<br>
+                        for (uint32_t x = curFrame->m_encData->m_pir.pirStartCol; x <= curFrame->m_encData->m_pir.pirEndCol; x++)<br>
+                            diff += curFrame->m_lowres.intraCost[lowresCuIdx] - lowresCuCost;<br>
                     curFrame->m_lowres.lowresCostForRc[lowresCuIdx] = lowresCuCost;<br>
                     sum += lowresCuCost;<br>
                     intraSum += curFrame->m_lowres.intraCost[lowresCuIdx];<br>
                 }<br>
                 curFrame->m_encData->m_rowStat[row].satdForVbv += sum;<br>
+                curFrame->m_encData->m_rowStat[row].satdForVbv += diff;<br>
                 curFrame->m_encData->m_rowStat[row].intraSatdForVbv += intraSum;<br>
             }<br>
         }<br>
@@ -900,8 +905,7 @@<br>
             x265_log(m_param, X265_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid and %d reference frames\n",<br>
                      frm.sliceType, m_param->maxNumReferences);<br>
         }<br>
-<br>
-        if (/* (!param->intraRefresh || frm.frameNum == 0) && */ frm.frameNum - m_lastKeyframe >= m_param->keyframeMax)<br>
+        if ((!m_param->bIntraRefresh || frm.frameNum == 0) && frm.frameNum - m_lastKeyframe >= m_param->keyframeMax)<br>
         {<br>
             if (frm.sliceType == X265_TYPE_AUTO || frm.sliceType == X265_TYPE_I)<br>
                 frm.sliceType = m_param->bOpenGOP && m_lastKeyframe >= 0 ? X265_TYPE_I : X265_TYPE_IDR;<br>
@@ -1184,7 +1188,7 @@<br>
     frames[framecnt + 1] = NULL;<br>
<br>
     keyintLimit = m_param->keyframeMax - frames[0]->frameNum + m_lastKeyframe - 1;<br>
-    origNumFrames = numFrames = X265_MIN(framecnt, keyintLimit);<br>
+    origNumFrames = numFrames = m_param->bIntraRefresh ? framecnt : X265_MIN(framecnt, keyintLimit);<br>
<br>
     if (bIsVbvLookahead)<br>
         numFrames = framecnt;<br>
@@ -1384,12 +1388,12 @@<br>
     if (m_param->rc.cuTree)<br>
         cuTree(frames, X265_MIN(numFrames, m_param->keyframeMax), bKeyframe);<br>
<br>
-    // if (!param->bIntraRefresh)<br>
-    for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)<br>
-    {<br>
-        frames[j]->sliceType = X265_TYPE_I;<br>
-        resetStart = X265_MIN(resetStart, j + 1);<br>
-    }<br>
+    if (!m_param->bIntraRefresh)<br>
+        for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)<br>
+        {<br>
+            frames[j]->sliceType = X265_TYPE_I;<br>
+            resetStart = X265_MIN(resetStart, j + 1);<br>
+        }<br>
<br>
     if (bIsVbvLookahead)<br>
         vbvLookahead(frames, numFrames, bKeyframe);<br>
@@ -1504,7 +1508,7 @@<br>
     {<br>
         if (m_param->keyframeMin == m_param->keyframeMax)<br>
             threshMin = threshMax;<br>
-        if (gopSize <= m_param->keyframeMin / 4)<br>
+        if (gopSize <= m_param->keyframeMin / 4 || m_param->bIntraRefresh)<br>
             bias = threshMin / 4;<br>
         else if (gopSize <= m_param->keyframeMin)<br>
             bias = threshMin * gopSize / m_param->keyframeMin;<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/<a href="http://x265.def.in" rel="noreferrer" target="_blank">x265.def.in</a><br>
--- a/source/<a href="http://x265.def.in" rel="noreferrer" target="_blank">x265.def.in</a>        Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/<a href="http://x265.def.in" rel="noreferrer" target="_blank">x265.def.in</a>        Wed Sep 16 14:26:59 2015 +0530<br>
@@ -22,3 +22,4 @@<br>
 x265_cleanup<br>
 x265_api_get_${X265_BUILD}<br>
 x265_api_query<br>
+x265_encoder_intra_refresh<br>
diff -r 98c0dcd5a10b -r 97b62cb57bfa source/x265.h<br>
--- a/source/x265.h     Wed Sep 09 14:52:35 2015 +0530<br>
+++ b/source/x265.h     Wed Sep 16 14:26:59 2015 +0530<br>
@@ -708,6 +708,8 @@<br>
      * big keyframe, the keyframe is "spread" over many frames. */<br></blockquote><div><br></div><div>I dont see why we need two variables in x265_param. To track whether the external refresh API was called, we can set an internal Encoder variable. The basic bIntraRefresh will decide whether this feature is available for this encode. <br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
     int       bIntraRefresh;<br>
<br>
+    int       bQueuedIntraRefresh;<br>
+<br>
     /*== Coding Unit (CU) definitions ==*/<br>
<br>
     /* Maximum CU width and height in pixels.  The size must be 64, 32, or 16.<br>
@@ -1379,6 +1381,22 @@<br>
  *      close an encoder handler */<br>
 void x265_encoder_close(x265_encoder *);<br>
<br>
+/* x265_encoder_intra_refresh:<br>
+ *      If an intra refresh is not in progress, begin one with the next P-frame.<br>
+ *      If an intra refresh is in progress, begin one as soon as the current one finishes.<br>
+ *      Requires bIntraRefresh to be set.<br></blockquote><div><br></div><div>It should be noted that if bIntraRefresh is not set, then this API call will do nothing. <br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+ *<br>
+ *      Useful for interactive streaming where the client can tell the server that packet loss has<br>
+ *      occurred.  In this case, keyint can be set to an extremely high value so that intra refreshes<br>
+ *      occur only when calling x265_encoder_intra_refresh.<br>
+ *<br>
+ *      In multi-pass encoding, if x265_encoder_intra_refresh is called differently in each pass,<br>
+ *      behavior is undefined.<br>
+ *<br>
+ *      Should not be called during an x265_encoder_encode. */<br>
+<br>
+int x265_encoder_intra_refresh(x265_encoder *);<br>
+<br>
 /* x265_cleanup:<br>
  *       release library static allocations, reset configured CTU size */<br>
 void x265_cleanup(void);<br>
@@ -1426,6 +1444,7 @@<br>
     void          (*cleanup)(void);<br>
<br>
     int           sizeof_frame_stats;   /* sizeof(x265_frame_stats) */<br>
+    int           (*encoder_intra_refresh)(x265_encoder*);<br>
     /* add new pointers to the end, or increment X265_MAJOR_VERSION */<br>
 } x265_api;<br>
<br>
_______________________________________________<br>
x265-devel mailing list<br>
<a href="mailto:x265-devel@videolan.org" target="_blank">x265-devel@videolan.org</a><br>
<a href="https://mailman.videolan.org/listinfo/x265-devel" rel="noreferrer" target="_blank">https://mailman.videolan.org/listinfo/x265-devel</a><br>
</blockquote></div><br></div></div></div></div>