changeset 11959:ecba08b10f3a draft

Add support for RADL pictures
author Aruna Matheswaran <aruna@multicorewareinc.com>
date Fri, 22 Dec 2017 14:50:08 +0530
parents 95fc0c4f03db
children ff02513b92c0
files doc/reST/cli.rst source/CMakeLists.txt source/common/lowres.cpp source/common/lowres.h source/common/param.cpp source/encoder/dpb.cpp source/encoder/encoder.cpp source/encoder/search.cpp source/encoder/slicetype.cpp source/encoder/weightPrediction.cpp source/test/regression-tests.txt source/x265.h source/x265cli.h
diffstat 13 files changed, 92 insertions(+-), 37 deletions(-) [+]
line wrap: on
line diff
--- a/doc/reST/cli.rst	Fri Dec 22 15:14:57 2017 +0530
+++ b/doc/reST/cli.rst	Fri Dec 22 14:50:08 2017 +0530
@@ -1345,7 +1345,14 @@ Slice decision options
 	This value represents the percentage difference between the inter cost and
 	intra cost of a frame used in scenecut detection. For example, a value of 5 indicates,
 	if the inter cost of a frame is greater than or equal to 95 percent of the intra cost of the frame,
-	then detect this frame as scenecut. Values between 5 and 15 are recommended. Default 5.	
+	then detect this frame as scenecut. Values between 5 and 15 are recommended. Default 5.
+	
+.. option:: --radl <integer>
+	
+	Number of RADL pictures allowed infront of IDR. Requires fixed keyframe interval.
+	Recommended value is 2-3. Default 0 (disabled).
+	
+	**Range of values: Between 0 and `--bframes`
 
 .. option:: --ctu-info <0, 1, 2, 4, 6>
 
--- a/source/CMakeLists.txt	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/CMakeLists.txt	Fri Dec 22 14:50:08 2017 +0530
@@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CP
 option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)
 mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
 # X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 149)
+set(X265_BUILD 150)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
--- a/source/common/lowres.cpp	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/common/lowres.cpp	Fri Dec 22 14:50:08 2017 +0530
@@ -89,7 +89,7 @@ bool Lowres::create(PicYuv *origPic, int
         }
     }
 
-    for (int i = 0; i < bframes + 1; i++)
+    for (int i = 0; i < bframes + 2; i++)
     {
         CHECKED_MALLOC(lowresMvs[0][i], MV, cuCount);
         CHECKED_MALLOC(lowresMvs[1][i], MV, cuCount);
@@ -118,7 +118,7 @@ void Lowres::destroy()
         }
     }
 
-    for (int i = 0; i < bframes + 1; i++)
+    for (int i = 0; i < bframes + 2; i++)
     {
         X265_FREE(lowresMvs[0][i]);
         X265_FREE(lowresMvs[1][i]);
@@ -152,7 +152,7 @@ void Lowres::init(PicYuv *origPic, int p
         for (int x = 0; x < bframes + 2; x++)
             rowSatds[y][x][0] = -1;
 
-    for (int i = 0; i < bframes + 1; i++)
+    for (int i = 0; i < bframes + 2; i++)
     {
         lowresMvs[0][i][0].x = 0x7FFF;
         lowresMvs[1][i][0].x = 0x7FFF;
--- a/source/common/lowres.h	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/common/lowres.h	Fri Dec 22 14:50:08 2017 +0530
@@ -130,8 +130,8 @@ struct Lowres : public ReferencePlanes
     int64_t   satdCost;
     uint16_t* lowresCostForRc;
     uint16_t* lowresCosts[X265_BFRAME_MAX + 2][X265_BFRAME_MAX + 2];
-    int32_t*  lowresMvCosts[2][X265_BFRAME_MAX + 1];
-    MV*       lowresMvs[2][X265_BFRAME_MAX + 1];
+    int32_t*  lowresMvCosts[2][X265_BFRAME_MAX + 2];
+    MV*       lowresMvs[2][X265_BFRAME_MAX + 2];
     uint32_t  maxBlocksInRow;
     uint32_t  maxBlocksInCol;
     uint32_t  maxBlocksInRowFullRes;
--- a/source/common/param.cpp	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/common/param.cpp	Fri Dec 22 14:50:08 2017 +0530
@@ -154,6 +154,7 @@ void x265_param_default(x265_param* para
     param->lookaheadSlices = 8;
     param->lookaheadThreads = 0;
     param->scenecutBias = 5.0;
+    param->radl = 0;
     /* Intra Coding Tools */
     param->bEnableConstrainedIntra = 0;
     param->bEnableStrongIntraSmoothing = 1;
@@ -1010,6 +1011,7 @@ int x265_param_parse(x265_param* p, cons
         OPT("gop-lookahead") p->gopLookahead = atoi(value);
         OPT("analysis-save") p->analysisSave = strdup(value);
         OPT("analysis-load") p->analysisLoad = strdup(value);
+        OPT("radl") p->radl = atoi(value);
         else
             return X265_PARAM_BAD_NAME;
     }
@@ -1316,6 +1318,8 @@ int x265_check_params(x265_param* param)
           "scenecutThreshold must be greater than 0");
     CHECK(param->scenecutBias < 0 || 100 < param->scenecutBias,
            "scenecut-bias must be between 0 and 100");
+    CHECK(param->radl < 0 || param->radl > param->bframes,
+          "radl must be between 0 and bframes");
     CHECK(param->rdPenalty < 0 || param->rdPenalty > 2,
           "Valid penalty for 32x32 intra TU in non-I slices. 0:disabled 1:RD-penalty 2:maximum");
     CHECK(param->keyframeMax < -1,
@@ -1575,6 +1579,7 @@ char *x265_param2string(x265_param* p, i
     s += sprintf(s, " rc-lookahead=%d", p->lookaheadDepth);
     s += sprintf(s, " lookahead-slices=%d", p->lookaheadSlices);
     s += sprintf(s, " scenecut=%d", p->scenecutThreshold);
+    s += sprintf(s, " radl=%d", p->radl);
     BOOL(p->bIntraRefresh, "intra-refresh");
     s += sprintf(s, " ctu=%d", p->maxCUSize);
     s += sprintf(s, " min-cu-size=%d", p->minCUSize);
--- a/source/encoder/dpb.cpp	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/encoder/dpb.cpp	Fri Dec 22 14:50:08 2017 +0530
@@ -181,7 +181,10 @@ void DPB::prepareEncode(Frame *newFrame)
     // Mark pictures in m_piclist as unreferenced if they are not included in RPS
     applyReferencePictureSet(&slice->m_rps, pocCurr);
 
-    slice->m_numRefIdx[0] = X265_MIN(newFrame->m_param->maxNumReferences, slice->m_rps.numberOfNegativePictures); // Ensuring L0 contains just the -ve POC
+    if (slice->m_sliceType != I_SLICE)
+        slice->m_numRefIdx[0] = x265_clip3(1, newFrame->m_param->maxNumReferences, slice->m_rps.numberOfNegativePictures);
+    else
+        slice->m_numRefIdx[0] = X265_MIN(newFrame->m_param->maxNumReferences, slice->m_rps.numberOfNegativePictures); // Ensuring L0 contains just the -ve POC
     slice->m_numRefIdx[1] = X265_MIN(newFrame->m_param->bBPyramid ? 2 : 1, slice->m_rps.numberOfPositivePictures);
     slice->setRefPicList(m_picList);
 
@@ -230,11 +233,14 @@ void DPB::computeRPS(int curPoc, bool is
     {
         if ((iterPic->m_poc != curPoc) && iterPic->m_encData->m_bHasReferences)
         {
-            rps->poc[poci] = iterPic->m_poc;
-            rps->deltaPOC[poci] = rps->poc[poci] - curPoc;
-            (rps->deltaPOC[poci] < 0) ? numNeg++ : numPos++;
-            rps->bUsed[poci] = !isRAP;
-            poci++;
+            if ((m_lastIDR >= curPoc) || (m_lastIDR <= iterPic->m_poc))
+            {
+                    rps->poc[poci] = iterPic->m_poc;
+                    rps->deltaPOC[poci] = rps->poc[poci] - curPoc;
+                    (rps->deltaPOC[poci] < 0) ? numNeg++ : numPos++;
+                    rps->bUsed[poci] = !isRAP;
+                    poci++;
+            }
         }
         iterPic = iterPic->m_next;
     }
--- a/source/encoder/encoder.cpp	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/encoder/encoder.cpp	Fri Dec 22 14:50:08 2017 +0530
@@ -3046,6 +3046,12 @@ void Encoder::configure(x265_param *p)
     p->maxCUDepth    = p->maxLog2CUSize - g_log2Size[p->minCUSize];
     p->unitSizeDepth = p->maxLog2CUSize - LOG2_UNIT_SIZE;
     p->num4x4Partitions = (1U << (p->unitSizeDepth << 1));
+
+    if (p->radl && (p->keyframeMax != p->keyframeMin))
+    {
+        p->radl = 0;
+        x265_log(p, X265_LOG_WARNING, "Radl requires fixed gop-length (keyint == min-keyint). Disabling radl.\n");
+    }
 }
 
 void Encoder::allocAnalysis(x265_analysis_data* analysis)
--- a/source/encoder/search.cpp	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/encoder/search.cpp	Fri Dec 22 14:50:08 2017 +0530
@@ -1947,7 +1947,7 @@ MV Search::getLowresMV(const CUData& cu,
         /* poc difference is out of range for lookahead */
         return 0;
 
-    MV* mvs = m_frame->m_lowres.lowresMvs[list][diffPoc - 1];
+    MV* mvs = m_frame->m_lowres.lowresMvs[list][diffPoc];
     if (mvs[0].x == 0x7FFF)
         /* this motion search was not estimated by lookahead */
         return 0;
--- a/source/encoder/slicetype.cpp	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/encoder/slicetype.cpp	Fri Dec 22 14:50:08 2017 +0530
@@ -879,7 +879,7 @@ void Lookahead::getEstimatedPictureCost(
     Slice *slice = curFrame->m_encData->m_slice;
     int p0 = 0, p1, b;
     int poc = slice->m_poc;
-    int l0poc = slice->m_refPOCList[0][0];
+    int l0poc = slice->m_rps.numberOfNegativePictures ? slice->m_refPOCList[0][0] : -1;
     int l1poc = slice->m_refPOCList[1][0];
 
     switch (slice->m_sliceType)
@@ -896,11 +896,22 @@ void Lookahead::getEstimatedPictureCost(
         break;
 
     case B_SLICE:
-        b = poc - l0poc;
-        p1 = b + l1poc - poc;
-        frames[p0] = &slice->m_refFrameList[0][0]->m_lowres;
-        frames[b] = &curFrame->m_lowres;
-        frames[p1] = &slice->m_refFrameList[1][0]->m_lowres;
+        if (l0poc >= 0)
+        {
+            b = poc - l0poc;
+            p1 = b + l1poc - poc;
+            frames[p0] = &slice->m_refFrameList[0][0]->m_lowres;
+            frames[b] = &curFrame->m_lowres;
+            frames[p1] = &slice->m_refFrameList[1][0]->m_lowres;
+        }
+        else 
+        {
+            p0 = b = 0;
+            p1 = b + l1poc - poc;
+            frames[p0] = frames[b] = &curFrame->m_lowres;
+            frames[p1] = &slice->m_refFrameList[1][0]->m_lowres;
+        }
+        
         break;
 
     default:
@@ -1120,12 +1131,20 @@ void Lookahead::slicetypeDecide()
             /* Closed GOP */
             m_lastKeyframe = frm.frameNum;
             frm.bKeyframe = true;
-            if (bframes > 0)
+            if (bframes > 0 && !m_param->radl)
             {
                 list[bframes - 1]->m_lowres.sliceType = X265_TYPE_P;
                 bframes--;
             }
         }
+        if (m_param->radl && !m_param->bOpenGOP && list[bframes + 1])
+        {
+            if ((frm.frameNum - m_lastKeyframe) >  (m_param->keyframeMax - m_param->radl - 1) && (frm.frameNum - m_lastKeyframe) <  m_param->keyframeMax)
+                frm.sliceType = X265_TYPE_B;
+            if ((frm.frameNum - m_lastKeyframe) == (m_param->keyframeMax - m_param->radl - 1))
+                frm.sliceType = X265_TYPE_P;
+        }
+
         if (bframes == m_param->bframes || !list[bframes + 1])
         {
             if (IS_X265_TYPE_B(frm.sliceType))
@@ -1175,8 +1194,13 @@ void Lookahead::slicetypeDecide()
         if (bframes)
         {
             p0 = 0; // last nonb
+            bool isp0available = frames[bframes + 1]->sliceType == X265_TYPE_IDR ? false : true;
+
             for (b = 1; b <= bframes; b++)
             {
+                if (!isp0available)
+                    p0 = b;
+
                 if (frames[b]->sliceType == X265_TYPE_B)
                     for (p1 = b; frames[p1]->sliceType == X265_TYPE_B; p1++)
                         ; // find new nonb or bref
@@ -1186,7 +1210,10 @@ void Lookahead::slicetypeDecide()
                 estGroup.singleCost(p0, p1, b);
 
                 if (frames[b]->sliceType == X265_TYPE_BREF)
+                {
                     p0 = b;
+                    isp0available = true;
+                }
             }
         }
     }
@@ -1413,12 +1440,12 @@ void Lookahead::slicetypeAnalyse(Lowres 
                     continue;
 
                 /* Skip search if already done */
-                if (frames[b]->lowresMvs[0][i - 1][0].x != 0x7FFF)
+                if (frames[b]->lowresMvs[0][i][0].x != 0x7FFF)
                     continue;
 
                 /* perform search to p1 at same distance, if possible */
                 int p1 = b + i;
-                if (p1 >= numFrames || frames[b]->lowresMvs[1][i - 1][0].x != 0x7FFF)
+                if (p1 >= numFrames || frames[b]->lowresMvs[1][i][0].x != 0x7FFF)
                     p1 = b;
 
                 estGroup.add(p0, p1, b);
@@ -1440,7 +1467,7 @@ void Lookahead::slicetypeAnalyse(Lowres 
 
                     /* only measure frame cost in this pass if motion searches
                      * are already done */
-                    if (frames[b]->lowresMvs[0][i - 1][0].x == 0x7FFF)
+                    if (frames[b]->lowresMvs[0][i][0].x == 0x7FFF)
                         continue;
 
                     int p0 = b - i;
@@ -1452,7 +1479,7 @@ void Lookahead::slicetypeAnalyse(Lowres 
                             break;
 
                         /* ensure P1 search is done */
-                        if (j && frames[b]->lowresMvs[1][j - 1][0].x == 0x7FFF)
+                        if (j && frames[b]->lowresMvs[1][j][0].x == 0x7FFF)
                             continue;
 
                         /* ensure frame cost is not done */
@@ -1867,7 +1894,7 @@ void Lookahead::aqMotion(Lowres **frames
 
 void Lookahead::calcMotionAdaptiveQuantFrame(Lowres **frames, int p0, int p1, int b)
 {
-    int listDist[2] = { b - p0 - 1, p1 - b - 1 };
+    int listDist[2] = { b - p0, p1 - b };
     int32_t strideInCU = m_8x8Width;
     double qp_adj = 0, avg_adj = 0, avg_adj_pow2 = 0, sd;
     for (uint16_t blocky = 0; blocky < m_8x8Height; blocky++)
@@ -2030,7 +2057,7 @@ void Lookahead::estimateCUPropagate(Lowr
     int32_t distScaleFactor = (((b - p0) << 8) + ((p1 - p0) >> 1)) / (p1 - p0);
     int32_t bipredWeight = m_param->bEnableWeightedBiPred ? 64 - (distScaleFactor >> 2) : 32;
     int32_t bipredWeights[2] = { bipredWeight, 64 - bipredWeight };
-    int listDist[2] = { b - p0 - 1, p1 - b - 1 };
+    int listDist[2] = { b - p0, p1 - b };
 
     memset(m_scratch, 0, m_8x8Width * sizeof(int));
 
@@ -2305,17 +2332,15 @@ int64_t CostEstimateGroup::estimateFrame
         score = fenc->costEst[b - p0][p1 - b];
     else
     {
-        X265_CHECK(p0 != b, "I frame estimates should always be pre-calculated\n");
-
         bool bDoSearch[2];
-        bDoSearch[0] = p0 < b && fenc->lowresMvs[0][b - p0 - 1][0].x == 0x7FFF;
-        bDoSearch[1] = p1 > b && fenc->lowresMvs[1][p1 - b - 1][0].x == 0x7FFF;
+        bDoSearch[0] = fenc->lowresMvs[0][b - p0][0].x == 0x7FFF;
+        bDoSearch[1] = p1 > b && fenc->lowresMvs[1][p1 - b][0].x == 0x7FFF;
 
 #if CHECKED_BUILD
-        X265_CHECK(!(p0 < b && fenc->lowresMvs[0][b - p0 - 1][0].x == 0x7FFE), "motion search batch duplication L0\n");
-        X265_CHECK(!(p1 > b && fenc->lowresMvs[1][p1 - b - 1][0].x == 0x7FFE), "motion search batch duplication L1\n");
-        if (bDoSearch[0]) fenc->lowresMvs[0][b - p0 - 1][0].x = 0x7FFE;
-        if (bDoSearch[1]) fenc->lowresMvs[1][p1 - b - 1][0].x = 0x7FFE;
+        X265_CHECK(!(p0 < b && fenc->lowresMvs[0][b - p0][0].x == 0x7FFE), "motion search batch duplication L0\n");
+        X265_CHECK(!(p1 > b && fenc->lowresMvs[1][p1 - b][0].x == 0x7FFE), "motion search batch duplication L1\n");
+        if (bDoSearch[0]) fenc->lowresMvs[0][b - p0][0].x = 0x7FFE;
+        if (bDoSearch[1]) fenc->lowresMvs[1][p1 - b][0].x = 0x7FFE;
 #endif
 
         fenc->weightedRef[b - p0].isWeighted = false;
@@ -2406,7 +2431,7 @@ void CostEstimateGroup::estimateCUCost(L
 
     /* A small, arbitrary bias to avoid VBV problems caused by zero-residual lookahead blocks. */
     int lowresPenalty = 4;
-    int listDist[2] = { b - p0 - 1, p1 - b - 1 };
+    int listDist[2] = { b - p0, p1 - b};
 
     MV mvmin, mvmax;
     int bcost = tld.me.COST_MAX;
--- a/source/encoder/weightPrediction.cpp	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/encoder/weightPrediction.cpp	Fri Dec 22 14:50:08 2017 +0530
@@ -323,7 +323,7 @@ void weightAnalyse(Slice& slice, Frame& 
 
             if (!plane && diffPoc <= param.bframes + 1)
             {
-                mvs = fenc.lowresMvs[list][diffPoc - 1];
+                mvs = fenc.lowresMvs[list][diffPoc];
 
                 /* test whether this motion search was performed by lookahead */
                 if (mvs[0].x != 0x7FFF)
--- a/source/test/regression-tests.txt	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/test/regression-tests.txt	Fri Dec 22 14:50:08 2017 +0530
@@ -151,6 +151,7 @@ Kimono1_1920x1080_24_400.yuv,--preset me
 Kimono1_1920x1080_24_400.yuv,--preset veryslow --crf 4 --cu-lossless --slices 2 --limit-refs 3 --limit-modes
 Kimono1_1920x1080_24_400.yuv,--preset placebo --ctu 32 --max-tu-size 8 --limit-tu 2
 big_buck_bunny_360p24.y4m, --keyint 60 --min-keyint 40 --gop-lookahead 14
+BasketballDrive_1920x1080_50.y4m, --preset medium --no-open-gop --keyint 50 --min-keyint 50 --radl 2
 
 # Main12 intraCost overflow bug test
 720p50_parkrun_ter.y4m,--preset medium
--- a/source/x265.h	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/x265.h	Fri Dec 22 14:50:08 2017 +0530
@@ -1268,6 +1268,7 @@ typedef struct x265_param
 
         /* internally enable if tune grain is set */
         int      bEnableConstVbv;
+
     } rc;
 
     /*== Video Usability Information ==*/
@@ -1542,6 +1543,8 @@ typedef struct x265_param
      * to reduce the amount of work the encoder must perform. Default disabled. */
     const char* analysisLoad;
 
+    /*Number of RADL pictures allowed in front of IDR*/
+    int radl;
 } x265_param;
 
 /* x265_param_alloc:
--- a/source/x265cli.h	Fri Dec 22 15:14:57 2017 +0530
+++ b/source/x265cli.h	Fri Dec 22 14:50:08 2017 +0530
@@ -124,6 +124,7 @@ static const struct option long_options[
     { "scenecut",       required_argument, NULL, 0 },
     { "no-scenecut",          no_argument, NULL, 0 },
     { "scenecut-bias",  required_argument, NULL, 0 },
+    { "radl",           required_argument, NULL, 0 },
     { "ctu-info",       required_argument, NULL, 0 },
     { "intra-refresh",        no_argument, NULL, 0 },
     { "rc-lookahead",   required_argument, NULL, 0 },
@@ -427,6 +428,7 @@ static void showHelp(x265_param *param)
     H0("   --no-scenecut                 Disable adaptive I-frame decision\n");
     H0("   --scenecut <integer>          How aggressively to insert extra I-frames. Default %d\n", param->scenecutThreshold);
     H1("   --scenecut-bias <0..100.0>    Bias for scenecut detection. Default %.2f\n", param->scenecutBias);
+    H0("   --radl <integer>              Number of RADL pictures allowed in front of IDR. Default %d\n", param->radl);
     H0("   --intra-refresh               Use Periodic Intra Refresh instead of IDR frames\n");
     H0("   --rc-lookahead <integer>      Number of frames for frame-type lookahead (determines encoder latency) Default %d\n", param->lookaheadDepth);
     H1("   --lookahead-slices <0..16>    Number of slices to use per lookahead cost estimate. Default %d\n", param->lookaheadSlices);