[x265] [PATCH] Implementation for Intra refresh

Deepthi Nandakumar deepthi at multicorewareinc.com
Mon Oct 19 16:14:12 CEST 2015


Pushed this with a few white-space fixes, but I didnt notice that the
author credentials were changed! :(

On Wed, Oct 7, 2015 at 4:40 PM, <santhoshini at multicorewareinc.com> wrote:

> # HG changeset patch
> # User Santhoshini Sekar<santhoshini at multicorewareinc.com>
> # Date 1442393819 -19800
> #      Wed Sep 16 14:26:59 2015 +0530
> # Node ID 0e6dc779c1b21d47a2c510c18536e52e4c28c878
> # Parent  7f24990073bb86ef6c632e5e2254c794daf3de3a
> Implementation for Intra refresh
>
> diff -r 7f24990073bb -r 0e6dc779c1b2 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 7f24990073bb -r 0e6dc779c1b2 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_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 7f24990073bb -r 0e6dc779c1b2 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_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_bQueuedIntraRefresh && pir->position + 0.5 >=
> numBlocksInRow))
> +        {
> +            pir->position = 0;
> +            pir->framesSinceLastPir = 0;
> +            m_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 7f24990073bb -r 0e6dc779c1b2 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
> @@ -131,6 +131,10 @@
>      bool               m_aborted;          // fatal error detected
>      bool               m_reconfigured;      // reconfigure of encoder
> detected
>
> +    /* Begin intra refresh when one not in progress or else begin one as
> soon as the current
> +     * one is done. Requires bIntraRefresh to be set.*/
> +    int                m_bQueuedIntraRefresh;
> +
>      Encoder();
>      ~Encoder() {}
>
> @@ -164,6 +168,8 @@
>
>      void finishFrameStats(Frame* pic, FrameEncoder *curEncoder,
> x265_frame_stats* frameStats);
>
> +    void calcRefreshInterval(Frame* frameEnc);
> +
>  protected:
>
>      void initVPS(VPS *vps);
> diff -r 7f24990073bb -r 0e6dc779c1b2 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
> @@ -2462,6 +2462,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 7f24990073bb -r 0e6dc779c1b2 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 7f24990073bb -r 0e6dc779c1b2 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 7f24990073bb -r 0e6dc779c1b2 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
> @@ -1379,6 +1379,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 +1442,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;
>
> _______________________________________________
> x265-devel mailing list
> x265-devel at videolan.org
> https://mailman.videolan.org/listinfo/x265-devel
>



-- 
Deepthi Nandakumar
Engineering Manager, x265
Multicoreware, Inc
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20151019/9c078797/attachment-0001.html>


More information about the x265-devel mailing list