changeset 11940:00f0db8a7bfc draft

Extend gop boundary by doing gop lookahead
author Aruna Matheswaran <aruna@multicorewareinc.com>
date Tue, 21 Nov 2017 10:48:24 +0530
parents 5e5fbf5ce96f
children fc0570b8d8f9
files doc/reST/cli.rst source/CMakeLists.txt source/common/param.cpp source/encoder/slicetype.cpp source/encoder/slicetype.h source/test/regression-tests.txt source/x265.h source/x265cli.h
diffstat 8 files changed, 64 insertions(+-), 17 deletions(-) [+]
line wrap: on
line diff
--- a/doc/reST/cli.rst	Thu Nov 30 18:16:40 2017 +0100
+++ b/doc/reST/cli.rst	Tue Nov 21 10:48:24 2017 +0530
@@ -1373,6 +1373,16 @@ Slice decision options
 	Default 20
 
 	**Range of values:** Between the maximum consecutive bframe count (:option:`--bframes`) and 250
+.. option:: --gop-lookahead <integer>
+
+        Number of frames for GOP boundary decision lookahead. If a scenecut frame is found
+        within this from the gop boundary set by `--keyint`, the GOP will be extented until such a point,
+        otherwise the GOP will be terminated as set by `--keyint`. Default 0.
+
+        **Range of values:** Between 0 and (`--rc-lookahead` - mini-GOP length)
+
+        It is recommended to have `--gop-lookahaed` less than `--min-keyint` as scenecuts beyond
+        `--min-keyint` are already being coded as keyframes.
 
 .. option:: --lookahead-slices <0..16>
 
--- a/source/CMakeLists.txt	Thu Nov 30 18:16:40 2017 +0100
+++ b/source/CMakeLists.txt	Tue Nov 21 10:48:24 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 146)
+set(X265_BUILD 147)
 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/param.cpp	Thu Nov 30 18:16:40 2017 +0100
+++ b/source/common/param.cpp	Tue Nov 21 10:48:24 2017 +0530
@@ -144,6 +144,7 @@ void x265_param_default(x265_param* para
     /* Coding Structure */
     param->keyframeMin = 0;
     param->keyframeMax = 250;
+    param->gopLookahead = 0;
     param->bOpenGOP = 1;
     param->bframes = 4;
     param->lookaheadDepth = 20;
@@ -1004,6 +1005,7 @@ int x265_param_parse(x265_param* p, cons
                 bError = true;
             }
          }
+        OPT("gop-lookahead") p->gopLookahead = atoi(value);
         else
             return X265_PARAM_BAD_NAME;
     }
@@ -1314,6 +1316,8 @@ int x265_check_params(x265_param* param)
           "Valid penalty for 32x32 intra TU in non-I slices. 0:disabled 1:RD-penalty 2:maximum");
     CHECK(param->keyframeMax < -1,
           "Invalid max IDR period in frames. value should be greater than -1");
+    CHECK(param->gopLookahead < -1,
+          "GOP lookahead must be greater than -1");
     CHECK(param->decodedPictureHashSEI < 0 || param->decodedPictureHashSEI > 3,
           "Invalid hash option. Decoded Picture Hash SEI 0: disabled, 1: MD5, 2: CRC, 3: Checksum");
     CHECK(param->rc.vbvBufferSize < 0,
@@ -1561,6 +1565,7 @@ char *x265_param2string(x265_param* p, i
     BOOL(p->bOpenGOP, "open-gop");
     s += sprintf(s, " min-keyint=%d", p->keyframeMin);
     s += sprintf(s, " keyint=%d", p->keyframeMax);
+    s += sprintf(s, " gop-lookahead=%d", p->gopLookahead);
     s += sprintf(s, " bframes=%d", p->bframes);
     s += sprintf(s, " b-adapt=%d", p->bFrameAdaptive);
     BOOL(p->bBPyramid, "b-pyramid");
--- a/source/encoder/slicetype.cpp	Thu Nov 30 18:16:40 2017 +0100
+++ b/source/encoder/slicetype.cpp	Tue Nov 21 10:48:24 2017 +0530
@@ -589,7 +589,7 @@ Lookahead::Lookahead(x265_param *param, 
     m_outputSignalRequired = false;
     m_isActive = true;
     m_inputCount = 0;
-
+    m_extendGopBoundary = false;
     m_8x8Height = ((m_param->sourceHeight / 2) + X265_LOWRES_CU_SIZE - 1) >> X265_LOWRES_CU_BITS;
     m_8x8Width = ((m_param->sourceWidth / 2) + X265_LOWRES_CU_SIZE - 1) >> X265_LOWRES_CU_BITS;
     m_cuCount = m_8x8Width * m_8x8Height;
@@ -646,7 +646,11 @@ Lookahead::Lookahead(x265_param *param, 
         m_numRowsPerSlice = m_8x8Height;
         m_numCoopSlices = 1;
     }
-
+    if (param->gopLookahead && (param->gopLookahead > (param->lookaheadDepth - param->bframes - 2)))
+    {
+        param->gopLookahead = X265_MAX(0, param->lookaheadDepth - param->bframes - 2);
+        x265_log(param, X265_LOG_WARNING, "Gop-lookahead cannot be greater than (rc-lookahead - length of the mini-gop); Clipping gop-lookahead to %d\n", param->gopLookahead);
+    }
 #if DETAILED_CU_STATS
     m_slicetypeDecideElapsedTime = 0;
     m_preLookaheadElapsedTime = 0;
@@ -1086,7 +1090,8 @@ void Lookahead::slicetypeDecide()
             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 ((!m_param->bIntraRefresh || frm.frameNum == 0) && frm.frameNum - m_lastKeyframe >= m_param->keyframeMax)
+        if ((!m_param->bIntraRefresh || frm.frameNum == 0) && frm.frameNum - m_lastKeyframe >= m_param->keyframeMax &&
+            (!m_extendGopBoundary || frm.frameNum - m_lastKeyframe >= m_param->keyframeMax + m_param->gopLookahead))
         {
             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;
@@ -1377,12 +1382,14 @@ void Lookahead::slicetypeAnalyse(Lowres 
             cuTree(frames, 0, bKeyframe);
         return;
     }
-
     frames[framecnt + 1] = NULL;
+    int keyFrameLimit = m_param->keyframeMax + m_lastKeyframe - frames[0]->frameNum - 1;
+    if (m_param->gopLookahead && keyFrameLimit <= m_param->bframes + 1)
+        keyintLimit = keyFrameLimit + m_param->gopLookahead;
+    else
+        keyintLimit = keyFrameLimit;
 
-    keyintLimit = m_param->keyframeMax - frames[0]->frameNum + m_lastKeyframe - 1;
     origNumFrames = numFrames = m_param->bIntraRefresh ? framecnt : X265_MIN(framecnt, keyintLimit);
-
     if (bIsVbvLookahead)
         numFrames = framecnt;
     else if (m_param->bOpenGOP && numFrames < framecnt)
@@ -1472,7 +1479,26 @@ void Lookahead::slicetypeAnalyse(Lowres 
         frames[1]->sliceType = X265_TYPE_I;
         return;
     }
-
+    if (m_param->gopLookahead && (keyFrameLimit >= 0) && (keyFrameLimit <= m_param->bframes + 1))
+    {
+        bool sceneTransition = m_isSceneTransition;
+        m_extendGopBoundary = false;
+        for (int i = m_param->bframes + 1; i < origNumFrames; i += m_param->bframes + 1)
+        {
+            scenecut(frames, i, i + 1, true, origNumFrames);
+            for (int j = i + 1; j <= X265_MIN(i + m_param->bframes + 1, origNumFrames); j++)
+            {
+                if (frames[j]->bScenecut && scenecutInternal(frames, j - 1, j, true) )
+                {
+                    m_extendGopBoundary = true;
+                    break;
+                }
+            }
+            if (m_extendGopBoundary)
+                break;
+        }
+        m_isSceneTransition = sceneTransition;
+    }
     if (m_param->bframes)
     {
         if (m_param->bFrameAdaptive == X265_B_ADAPT_TRELLIS)
@@ -1578,6 +1604,8 @@ void Lookahead::slicetypeAnalyse(Lowres 
 
     if (m_param->rc.cuTree)
         cuTree(frames, X265_MIN(numFrames, m_param->keyframeMax), bKeyframe);
+    if (m_param->gopLookahead && (keyFrameLimit >= 0) && (keyFrameLimit <= m_param->bframes + 1) && !m_extendGopBoundary)
+        keyintLimit = keyFrameLimit;
 
     if (!m_param->bIntraRefresh)
         for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax)
@@ -1588,8 +1616,8 @@ void Lookahead::slicetypeAnalyse(Lowres 
 
     if (bIsVbvLookahead)
         vbvLookahead(frames, numFrames, bKeyframe);
+    int maxp1 = X265_MIN(m_param->bframes + 1, origNumFrames);
 
-     int maxp1 = X265_MIN(m_param->bframes + 1, origNumFrames);
     /* Restore frame types for all frames that haven't actually been decided yet. */
     for (int j = resetStart; j <= numFrames; j++)
     {
@@ -1613,8 +1641,8 @@ bool Lookahead::scenecut(Lowres **frames
         bool fluctuate = false;
         bool noScenecuts = false;
         int64_t avgSatdCost = 0;
-        if (frames[0]->costEst[1][0] > -1)
-            avgSatdCost = frames[0]->costEst[1][0];
+        if (frames[p0]->costEst[p1 - p0][0] > -1)
+            avgSatdCost = frames[p0]->costEst[p1 - p0][0];
         int cnt = 1;
         /* Where A and B are scenes: AAAAAABBBAAAAAA
          * If BBB is shorter than (maxp1-p0), it is detected as a flash
@@ -1700,12 +1728,10 @@ bool Lookahead::scenecutInternal(Lowres 
 
     CostEstimateGroup estGroup(*this, frames);
     estGroup.singleCost(p0, p1, p1);
-
     int64_t icost = frame->costEst[0][0];
     int64_t pcost = frame->costEst[p1 - p0][0];
-    int gopSize = frame->frameNum - m_lastKeyframe;
+    int gopSize = (frame->frameNum - m_lastKeyframe) % m_param->keyframeMax;
     float threshMax = (float)(m_param->scenecutThreshold / 100.0);
-
     /* magic numbers pulled out of thin air */
     float threshMin = (float)(threshMax * 0.25);
     double bias = m_param->scenecutBias;
--- a/source/encoder/slicetype.h	Thu Nov 30 18:16:40 2017 +0100
+++ b/source/encoder/slicetype.h	Tue Nov 21 10:48:24 2017 +0530
@@ -132,6 +132,7 @@ public:
     bool          m_filled;
     bool          m_isSceneTransition;
     int           m_numPools;
+    bool          m_extendGopBoundary;
     Lookahead(x265_param *param, ThreadPool *pool);
 #if DETAILED_CU_STATS
     int64_t       m_slicetypeDecideElapsedTime;
--- a/source/test/regression-tests.txt	Thu Nov 30 18:16:40 2017 +0100
+++ b/source/test/regression-tests.txt	Tue Nov 21 10:48:24 2017 +0530
@@ -150,7 +150,7 @@ Kimono1_1920x1080_24_400.yuv,--preset ul
 Kimono1_1920x1080_24_400.yuv,--preset medium --rdoq-level 0 --limit-refs 3 --slices 2
 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 20
 # Main12 intraCost overflow bug test
 720p50_parkrun_ter.y4m,--preset medium
 
--- a/source/x265.h	Thu Nov 30 18:16:40 2017 +0100
+++ b/source/x265.h	Tue Nov 21 10:48:24 2017 +0530
@@ -1532,11 +1532,14 @@ typedef struct x265_param
 
     /* Reuse MV information obtained through API */
     int       bMVType;
-
     /* Allow the encoder to have a copy of the planes of x265_picture in Frame */
     int       bCopyPicToFrame;
+
+    /*Number of frames for GOP boundary decision lookahead.If a scenecut frame is found
+    * within this from the gop boundary set by keyint, the GOP will be extented until such a point,
+    * otherwise the GOP will be terminated as set by keyint*/
+    int       gopLookahead;
 } x265_param;
-
 /* x265_param_alloc:
  *  Allocates an x265_param instance. The returned param structure is not
  *  special in any way, but using this method together with x265_param_free()
--- a/source/x265cli.h	Thu Nov 30 18:16:40 2017 +0100
+++ b/source/x265cli.h	Tue Nov 21 10:48:24 2017 +0530
@@ -119,6 +119,7 @@ static const struct option long_options[
     { "open-gop",             no_argument, NULL, 0 },
     { "keyint",         required_argument, NULL, 'I' },
     { "min-keyint",     required_argument, NULL, 'i' },
+    { "gop-lookahead",  required_argument, NULL, 0 },
     { "scenecut",       required_argument, NULL, 0 },
     { "no-scenecut",          no_argument, NULL, 0 },
     { "scenecut-bias",  required_argument, NULL, 0 },
@@ -418,6 +419,7 @@ static void showHelp(x265_param *param)
     H0("   --[no-]open-gop               Enable open-GOP, allows I slices to be non-IDR. Default %s\n", OPT(param->bOpenGOP));
     H0("-I/--keyint <integer>            Max IDR period in frames. -1 for infinite-gop. Default %d\n", param->keyframeMax);
     H0("-i/--min-keyint <integer>        Scenecuts closer together than this are coded as I, not IDR. Default: auto\n");
+    H0("   --gop-lookahead <integer>     Extends gop boundary if a scenecut is found within this from keyint boundary. Default 0\n");
     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);