<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Sep 12, 2015 at 12:02 AM, Steve Borho <span dir="ltr"><<a href="mailto:steve@borho.org" target="_blank">steve@borho.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On 09/09, <a href="mailto:santhoshini@multicorewareinc.com">santhoshini@multicorewareinc.com</a> wrote:<br>
> # HG changeset patch<br>
> # User Santhoshini Sekar<<a href="mailto:santhoshini@multicorewareinc.com">santhoshini@multicorewareinc.com</a>><br>
> # Date 1441783208 -19800<br>
> #      Wed Sep 09 12:50:08 2015 +0530<br>
> # Node ID b83e67c7cc436142f0e4c370681af1ff9aa38402<br>
> # Parent  f6892dcc7f4e74af58d7f39e085aed44afba919c<br>
> Implementation for Intra-refresh<br>
><br>
> diff -r f6892dcc7f4e -r b83e67c7cc43 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 09 12:50:08 2015 +0530<br>
> @@ -170,6 +170,9 @@<br>
>      }<br>
>      else<br>
>      {<br>
> +        bForceIntra = m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE &&<br>
> +                          ctu.m_cuPelX / g_maxCUSize >= frame.m_encData->m_pir.pirStartPelX && ctu.m_cuPelX / g_maxCUSize < frame.m_encData->m_pir.pirEndPelX;<br>
<br>
</span>it's occurred to me that when bForceIntra is true, we can simply call<br>
compressIntraCU() instead of compressInterCU_*(). Then bForceIntra can<br>
be a local variable instead of a member var, and all the other changes<br>
to this file can be discarded<br>
<div><div class="h5"><br>
>          if (!m_param->rdLevel)<br>
>          {<br>
>              /* In RD Level 0/1, copy source pixels into the reconstructed block so<br>
> @@ -828,7 +831,7 @@<br>
>      bool splitIntra = true;<br>
>      uint32_t splitRefs[4] = { 0, 0, 0, 0 };<br>
>      /* Step 1. Evaluate Merge/Skip candidates for likely early-outs */<br>
> -    if (mightNotSplit && depth >= minDepth)<br>
> +    if (mightNotSplit && depth >= minDepth && !bForceIntra)<br>
>      {<br>
>          /* Compute Merge Cost */<br>
>          md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> @@ -909,7 +912,7 @@<br>
>          if (m_slice->m_pps->bUseDQP && depth <= m_slice->m_pps->maxCuDQPDepth && m_slice->m_pps->maxCuDQPDepth != 0)<br>
>              setLambdaFromQP(parentCTU, qp);<br>
><br>
> -        if (!earlyskip)<br>
> +        if (!earlyskip && !bForceIntra)<br>
>          {<br>
>              uint32_t refMasks[2];<br>
>              refMasks[0] = allSplitRefs;<br>
> @@ -1119,7 +1122,21 @@<br>
>                      }<br>
>                  }<br>
>              }<br>
> -        } // !earlyskip<br>
> +        }<br>
> +        if (!earlyskip && bForceIntra)<br>
> +        {<br>
> +            ProfileCounter(parentCTU, totalIntraCU[cuGeom.depth]);<br>
> +            md.pred[PRED_INTRA].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> +            checkIntra(md.pred[PRED_INTRA], cuGeom, SIZE_2Nx2N, NULL, NULL);<br>
> +            checkBestMode(md.pred[PRED_INTRA], depth);<br>
> +<br>
> +            if (cuGeom.log2CUSize == 3 && m_slice->m_sps->quadtreeTULog2MinSize < 3)<br>
> +            {<br>
> +                md.pred[PRED_INTRA_NxN].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> +                checkIntra(md.pred[PRED_INTRA_NxN], cuGeom, SIZE_NxN, NULL, NULL);<br>
> +                checkBestMode(md.pred[PRED_INTRA_NxN], depth);<br>
> +            }<br>
> +        }// !earlyskip<br>
><br>
>          if (m_bTryLossless)<br>
>              tryLossless(cuGeom);<br>
> @@ -1189,23 +1206,36 @@<br>
>      {<br>
>          uint8_t* reuseDepth  = &m_reuseInterDataCTU->depth[parentCTU.m_cuAddr * parentCTU.m_numPartitions];<br>
>          uint8_t* reuseModes  = &m_reuseInterDataCTU->modes[parentCTU.m_cuAddr * parentCTU.m_numPartitions];<br>
> -        if (mightNotSplit && depth == reuseDepth[zOrder] && zOrder == cuGeom.absPartIdx && reuseModes[zOrder] == MODE_SKIP)<br>
> +        if (!bForceIntra)<br>
>          {<br>
> -            md.pred[PRED_SKIP].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> -            md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> -            checkMerge2Nx2N_rd5_6(md.pred[PRED_SKIP], md.pred[PRED_MERGE], cuGeom, true);<br>
> +            if (mightNotSplit && depth == reuseDepth[zOrder] && zOrder == cuGeom.absPartIdx && reuseModes[zOrder] == MODE_SKIP)<br>
> +            {<br>
> +                md.pred[PRED_SKIP].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> +                md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> +                checkMerge2Nx2N_rd5_6(md.pred[PRED_SKIP], md.pred[PRED_MERGE], cuGeom, true);<br>
><br>
> -            if (m_bTryLossless)<br>
> -                tryLossless(cuGeom);<br>
> +                if (m_bTryLossless)<br>
> +                    tryLossless(cuGeom);<br>
><br>
> -            if (mightSplit)<br>
> -                addSplitFlagCost(*md.bestMode, cuGeom.depth);<br>
> +                if (mightSplit)<br>
> +                    addSplitFlagCost(*md.bestMode, cuGeom.depth);<br>
><br>
> -            // increment zOrder offset to point to next best depth in sharedDepth buffer<br>
> -            zOrder += g_depthInc[g_maxCUDepth - 1][reuseDepth[zOrder]];<br>
> +                // increment zOrder offset to point to next best depth in sharedDepth buffer<br>
> +                zOrder += g_depthInc[g_maxCUDepth - 1][reuseDepth[zOrder]];<br>
><br>
> -            mightSplit = false;<br>
> -            mightNotSplit = false;<br>
> +                mightSplit = false;<br>
> +                mightNotSplit = false;<br>
> +            }<br>
> +        }<br>
> +        else<br>
> +        {<br>
> +            char* reusePartSizes = &m_reuseIntraDataCTU->partSizes[parentCTU.m_cuAddr * parentCTU.m_numPartitions];<br>
> +            uint8_t* reuseChromaModes = &m_reuseIntraDataCTU->chromaModes[parentCTU.m_cuAddr * parentCTU.m_numPartitions];<br>
> +            PartSize size = (PartSize)reusePartSizes[zOrder];<br>
> +            Mode& mode = size == SIZE_2Nx2N ? md.pred[PRED_INTRA] : md.pred[PRED_INTRA_NxN];<br>
> +            int ipOffset = (int)(6.0 * X265_LOG2(m_param->rc.ipFactor) + 0.5);<br>
> +            mode.cu.initSubCU(parentCTU, cuGeom, X265_MAX(mode.cu.m_qp[0] - ipOffset, QP_MIN));<br>
> +            checkIntra(mode, cuGeom, size, &reuseModes[zOrder], &reuseChromaModes[zOrder]);<br>
>          }<br>
>      }<br>
><br>
> @@ -1213,7 +1243,7 @@<br>
>      bool splitIntra = true;<br>
>      uint32_t splitRefs[4] = { 0, 0, 0, 0 };<br>
>      /* Step 1. Evaluate Merge/Skip candidates for likely early-outs */<br>
> -    if (mightNotSplit)<br>
> +    if (mightNotSplit && !bForceIntra)<br>
>      {<br>
>          md.pred[PRED_SKIP].cu.initSubCU(parentCTU, cuGeom, qp);<br>
>          md.pred[PRED_MERGE].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> @@ -1282,7 +1312,7 @@<br>
>          if (m_slice->m_pps->bUseDQP && depth <= m_slice->m_pps->maxCuDQPDepth && m_slice->m_pps->maxCuDQPDepth != 0)<br>
>              setLambdaFromQP(parentCTU, qp);<br>
><br>
> -        if (!(foundSkip && m_param->bEnableEarlySkip))<br>
> +        if (!(foundSkip && m_param->bEnableEarlySkip) && !bForceIntra)<br>
>          {<br>
>              uint32_t refMasks[2];<br>
>              refMasks[0] = allSplitRefs;<br>
> @@ -1389,6 +1419,20 @@<br>
>                  }<br>
>              }<br>
>          }<br>
> +        if (!(foundSkip && m_param->bEnableEarlySkip) && bForceIntra)<br>
> +        {<br>
> +            ProfileCounter(parentCTU, totalIntraCU[cuGeom.depth]);<br>
> +            md.pred[PRED_INTRA].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> +            checkIntra(md.pred[PRED_INTRA], cuGeom, SIZE_2Nx2N, NULL, NULL);<br>
> +            checkBestMode(md.pred[PRED_INTRA], depth);<br>
> +<br>
> +            if (cuGeom.log2CUSize == 3 && m_slice->m_sps->quadtreeTULog2MinSize < 3)<br>
> +            {<br>
> +                md.pred[PRED_INTRA_NxN].cu.initSubCU(parentCTU, cuGeom, qp);<br>
> +                checkIntra(md.pred[PRED_INTRA_NxN], cuGeom, SIZE_NxN, NULL, NULL);<br>
> +                checkBestMode(md.pred[PRED_INTRA_NxN], depth);<br>
> +            }<br>
> +        }<br>
><br>
>          if (m_bTryLossless)<br>
>              tryLossless(cuGeom);<br>
> @@ -1464,7 +1508,13 @@<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>
> -<br>
> +        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)<br>
> +        {<br>
> +            int maxX = (m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndPelX * 16 - 3) * 4;<br>
> +            int maxMv = maxX - 4 * 16 * candMvField[i][0].mv.x;<br>
> +            if (maxMv > 0 && tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndPelX)<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>
>          tempPred->cu.m_interDir[0] = candDir[i];<br>
> @@ -1592,7 +1642,13 @@<br>
>                  continue;<br>
>              triedBZero = true;<br>
>          }<br>
> -<br>
> +        if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)<br>
> +        {<br>
> +            int maxX = (m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndPelX * 16 - 3) * 4;<br>
> +            int maxMv = maxX - 4 * 16 * candMvField[i][0].mv.x;<br>
> +            if (maxMv > 0 && tempPred->cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirEndPelX)<br>
> +                continue;<br>
> +        }<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 f6892dcc7f4e -r b83e67c7cc43 source/encoder/analysis.h<br>
> --- a/source/encoder/analysis.h       Wed Sep 09 14:52:35 2015 +0530<br>
> +++ b/source/encoder/analysis.h       Wed Sep 09 12:50:08 2015 +0530<br>
> @@ -110,6 +110,8 @@<br>
><br>
>      uint32_t m_splitRefIdx[4];<br>
><br>
> +    int      bForceIntra; // For Periodic Intra Refresh.Supported only in P-frames<br>
<br>
</div></div>member vars always have m_ prefixes<br>
<div><div class="h5"><br>
> +<br>
>      /* full analysis for an I-slice CU */<br>
>      void compressIntraCU(const CUData& parentCTU, const CUGeom& cuGeom, uint32_t &zOrder, int32_t qp);<br>
><br>
> diff -r f6892dcc7f4e -r b83e67c7cc43 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 09 12:50:08 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>
> @@ -315,6 +325,7 @@<br>
>      &x265_encoder_log,<br>
>      &x265_encoder_close,<br>
>      &x265_cleanup,<br>
> +    &x265_encoder_intra_refresh,<br>
><br>
>      sizeof(x265_frame_stats),<br>
>  };<br>
> diff -r f6892dcc7f4e -r b83e67c7cc43 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 09 12:50:08 2015 +0530<br>
> @@ -768,6 +768,43 @@<br>
>              if (m_param->rc.rateControlMode != X265_RC_CQP)<br>
>                  m_lookahead->getEstimatedPictureCost(frameEnc);<br>
><br>
> +            if (m_param->bIntraRefresh)<br>
> +            {<br>
> +                Slice* slice = frameEnc->m_encData->m_slice;<br>
> +                if (slice->m_sliceType == I_SLICE)<br>
> +                {<br>
> +                    frameEnc->m_encData->m_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>
> +                    frameEnc->m_encData->m_pir.position = frameEnc->m_lowres.maxBlocksInRow;<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)frameEnc->m_lowres.maxBlocksInRow - 1) / m_param->keyframeMax, 1);<br>
> +                    frameEnc->m_encData->m_pir.position = ref->m_encData->m_pir.position;<br>
> +                    frameEnc->m_encData->m_pir.framesSinceLastPir = ref->m_encData->m_pir.framesSinceLastPir + pocdiff;<br>
> +                    if (frameEnc->m_encData->m_pir.framesSinceLastPir >= m_param->keyframeMax ||<br>
> +                        (m_param->bQueuedIntraRefresh && frameEnc->m_encData->m_pir.position + 0.5 >= frameEnc->m_lowres.maxBlocksInRow))<br>
> +                    {<br>
> +                        frameEnc->m_encData->m_pir.position = 0;<br>
> +                        frameEnc->m_encData->m_pir.framesSinceLastPir = 0;<br>
> +                        m_param->bQueuedIntraRefresh = 0;<br>
> +                        frameEnc->m_lowres.bKeyframe = 1;<br>
> +                    }<br>
> +                    frameEnc->m_encData->m_pir.pirStartPelX = (uint32_t)(frameEnc->m_encData->m_pir.position + 0.5);<br>
> +                    frameEnc->m_encData->m_pir.position += increment * pocdiff;<br>
> +                    frameEnc->m_encData->m_pir.pirEndPelX = (uint32_t)(frameEnc->m_encData->m_pir.position + 0.5);<br>
> +                    /* If our intra refresh has reached the right side of the frame, we're done. */<br>
> +                    if (frameEnc->m_encData->m_pir.pirEndPelX >= frameEnc->m_lowres.maxBlocksInRow - 1)<br>
> +                    {<br>
> +                        frameEnc->m_encData->m_pir.position = frameEnc->m_lowres.maxBlocksInRow;<br>
> +                        frameEnc->m_encData->m_pir.pirEndPelX = frameEnc->m_lowres.maxBlocksInRow - 1;<br>
> +                    }<br>
> +                }<br>
> +            }<br>
>              /* Allow FrameEncoder::compressFrame() to start in the frame encoder thread */<br>
>              if (!curEncoder->startCompressFrame(frameEnc))<br>
>                  m_aborted = true;<br>
> diff -r f6892dcc7f4e -r b83e67c7cc43 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 09 12:50:08 2015 +0530<br>
> @@ -1923,6 +1923,26 @@<br>
>      return costs[0] <= costs[1] ? 0 : 1;<br>
>  }<br>
><br>
> +void Search::clipMvPIR(const CUData& cu, MV& outMV) const<br>
> +{<br>
> +    const uint32_t mvshift = 2;<br>
> +    uint32_t offset = 8;<br>
> +<br>
> +    int16_t xmax = (int16_t)((m_slice->m_sps->picWidthInLumaSamples + offset - cu.m_cuPelX - 1) << mvshift);<br>
> +    int16_t xmin = -(int16_t)((g_maxCUSize + offset + cu.m_cuPelX - 1) << mvshift);<br>
> +<br>
> +    int16_t ymax = (int16_t)((m_slice->m_sps->picHeightInLumaSamples + offset - cu.m_cuPelY - 1) << mvshift);<br>
> +    int16_t ymin = -(int16_t)((g_maxCUSize + offset + cu.m_cuPelY - 1) << mvshift);<br>
> +<br>
> +    int maxX = (m_slice->m_refFrameList[0][0]->m_encData->m_pir.pirEndPelX * 16 - 3) << mvshift;<br>
> +    int maxMv = maxX - 4* 16 * outMV.x;<br>
<br>
</div></div>what are these 16's for? should they be CU size or g_maxCUSize?<br></blockquote><div><br></div><div>I tried with values 16 and 64. Couldn't spot much difference. I guess in x264 they have used 16 because their max mb size is 16. If that is the case then I can use <br>g_maxCUSize.  </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="HOEnZb"><div class="h5"><br>
> +    if (maxMv > 0 && cu.m_cuPelX / g_maxCUSize < m_frame->m_encData->m_pir.pirStartPelX)<br>
> +        xmax = X265_MIN(xmax, (int16_t)maxX);<br>
> +<br>
> +    outMV.x = X265_MIN(xmax, X265_MAX(xmin, outMV.x));<br>
> +    outMV.y = X265_MIN(ymax, X265_MAX(ymin, outMV.y));<br>
> +}<br>
> +<br>
>  void Search::PME::processTasks(int workerThreadId)<br>
>  {<br>
>  #if DETAILED_CU_STATS<br>
> @@ -2088,6 +2108,11 @@<br>
>                      mvc[numMvc++] = lmv;<br>
><br>
>                  setSearchRange(cu, mvp, m_param->searchRange, mvmin, mvmax);<br>
> +                if (m_param->bIntraRefresh && m_slice->m_sliceType == P_SLICE)<br>
> +                {<br>
> +                    clipMvPIR(cu, mvmin);<br>
> +                    clipMvPIR(cu, mvmax);<br>
> +                }<br>
>                  int satdCost = m_me.motionEstimate(&slice->m_mref[list][ref], mvmin, mvmax, mvp, numMvc, mvc, m_param->searchRange, outmv);<br>
><br>
>                  /* Get total cost of partition, but only include MV bit cost once */<br>
> diff -r f6892dcc7f4e -r b83e67c7cc43 source/encoder/search.h<br>
> --- a/source/encoder/search.h Wed Sep 09 14:52:35 2015 +0530<br>
> +++ b/source/encoder/search.h Wed Sep 09 12:50:08 2015 +0530<br>
> @@ -449,6 +449,7 @@<br>
><br>
>      /* inter/ME helper functions */<br>
>      int       selectMVP(const CUData& cu, const PredictionUnit& pu, const MV amvp[AMVP_NUM_CANDS], int list, int ref);<br>
> +    void     clipMvPIR(const CUData& cu, MV& outMV) const; // used only for Periodic Intra refresh<br>
>      const MV& checkBestMVP(const MV amvpCand[2], const MV& mv, int& mvpIdx, uint32_t& outBits, uint32_t& outCost) const;<br>
>      void     setSearchRange(const CUData& cu, const MV& mvp, int merange, MV& mvmin, MV& mvmax) const;<br>
>      uint32_t mergeEstimation(CUData& cu, const CUGeom& cuGeom, const PredictionUnit& pu, int puIdx, MergeData& m);<br>
> diff -r f6892dcc7f4e -r b83e67c7cc43 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 09 12:50:08 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.pirStartPelX; x <= curFrame->m_encData->m_pir.pirEndPelX; 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 f6892dcc7f4e -r b83e67c7cc43 source/x265.h<br>
> --- a/source/x265.h   Wed Sep 09 14:52:35 2015 +0530<br>
> +++ b/source/x265.h   Wed Sep 09 12:50:08 2015 +0530<br>
> @@ -707,6 +707,8 @@<br>
>       * big keyframe, the keyframe is "spread" over many frames. */<br>
>      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>
> @@ -1378,6 +1380,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>
> + *<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>
> @@ -1423,6 +1441,7 @@<br>
>      void          (*encoder_log)(x265_encoder*, int, char**);<br>
>      void          (*encoder_close)(x265_encoder*);<br>
>      void          (*cleanup)(void);<br>
> +    int           (*encoder_intra_refresh)(x265_encoder*);<br>
><br>
>      int           sizeof_frame_stats;   /* sizeof(x265_frame_stats) */<br>
>      /* add new pointers to the end, or increment X265_MAJOR_VERSION */<br>
</div></div><div class="HOEnZb"><div class="h5">> _______________________________________________<br>
> x265-devel mailing list<br>
> <a href="mailto:x265-devel@videolan.org">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>
<br>
--<br>
Steve Borho<br>
_______________________________________________<br>
x265-devel mailing list<br>
<a href="mailto:x265-devel@videolan.org">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>
</div></div></blockquote></div><br></div></div>