changeset 12512:3f4fb9a2ac68 draft

api: Add option "--fades" to detect and handle fade-in regions It does the following: Force I-slice and Initialize RC history for the brightest frame after fade-in.
author Pooja Venkatesan <pooja@multicorewareinc.com>
date Mon, 22 Apr 2019 11:11:35 +0530
parents 35bcf7fb2b30
children 220cdb4328a1
files doc/reST/cli.rst source/CMakeLists.txt source/common/lowres.cpp source/common/lowres.h source/common/param.cpp source/encoder/ratecontrol.cpp source/encoder/ratecontrol.h source/encoder/slicetype.cpp source/encoder/slicetype.h source/test/regression-tests.txt source/x265.h source/x265cli.h
diffstat 12 files changed, 90 insertions(+-), 7 deletions(-) [+]
line wrap: on
line diff
--- a/doc/reST/cli.rst	Fri May 17 15:00:30 2019 +0530
+++ b/doc/reST/cli.rst	Mon Apr 22 11:11:35 2019 +0530
@@ -1520,6 +1520,10 @@ Slice decision options
 	    slicetype decision may change with this option.
 	2 - flush the slicetype decided frames only.   
 
+.. option:: --fades, --no-fades
+
+	Detect and handle fade-in regions. Default disabled.
+
 Quality, rate control and rate distortion options
 =================================================
 
--- a/source/CMakeLists.txt	Fri May 17 15:00:30 2019 +0530
+++ b/source/CMakeLists.txt	Mon Apr 22 11:11:35 2019 +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 173)
+set(X265_BUILD 174)
 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 May 17 15:00:30 2019 +0530
+++ b/source/common/lowres.cpp	Mon Apr 22 11:11:35 2019 +0530
@@ -82,7 +82,7 @@ bool Lowres::create(x265_param* param, P
 
     if (origPic->m_param->bAQMotion)
         CHECKED_MALLOC_ZERO(qpAqMotionOffset, double, cuCountFullRes);
-    if (origPic->m_param->bDynamicRefine)
+    if (origPic->m_param->bDynamicRefine || origPic->m_param->bEnableFades)
         CHECKED_MALLOC_ZERO(blockVariance, uint32_t, cuCountFullRes);
 
     if (!!param->rc.hevcAq)
@@ -217,6 +217,7 @@ void Lowres::init(PicYuv *origPic, int p
 {
     bLastMiniGopBFrame = false;
     bKeyframe = false; // Not a keyframe unless identified by lookahead
+    bIsFadeEnd = false;
     frameNum = poc;
     leadingBframes = 0;
     indB = 0;
--- a/source/common/lowres.h	Fri May 17 15:00:30 2019 +0530
+++ b/source/common/lowres.h	Mon Apr 22 11:11:35 2019 +0530
@@ -160,6 +160,7 @@ struct Lowres : public ReferencePlanes
     bool   bScenecut;        // Set to false if the frame cannot possibly be part of a real scenecut.
     bool   bKeyframe;
     bool   bLastMiniGopBFrame;
+    bool   bIsFadeEnd;
 
     double ipCostRatio;
 
@@ -195,6 +196,7 @@ struct Lowres : public ReferencePlanes
     uint32_t* blockVariance;
     uint64_t  wp_ssd[3];       // This is different than SSDY, this is sum(pixel^2) - sum(pixel)^2 for entire frame
     uint64_t  wp_sum[3];
+    double    frameVariance;
 
     /* cutree intermediate data */
     PicQPAdaptationLayer* pAQLayer;
--- a/source/common/param.cpp	Fri May 17 15:00:30 2019 +0530
+++ b/source/common/param.cpp	Mon Apr 22 11:11:35 2019 +0530
@@ -172,6 +172,7 @@ void x265_param_default(x265_param* para
     param->chunkStart = 0;
     param->chunkEnd = 0;
     param->bEnableHRDConcatFlag = 0;
+    param->bEnableFades = 0;
 
     /* Intra Coding Tools */
     param->bEnableConstrainedIntra = 0;
@@ -1265,6 +1266,7 @@ int x265_param_parse(x265_param* p, cons
         OPT("svt-pred-struct") x265_log(p, X265_LOG_WARNING, "Option %s is SVT-HEVC Encoder specific; Disabling it here \n", name);
         OPT("svt-fps-in-vps") x265_log(p, X265_LOG_WARNING, "Option %s is SVT-HEVC Encoder specific; Disabling it here \n", name);
 #endif
+        OPT("fades") p->bEnableFades = atobool(value);
         else
             return X265_PARAM_BAD_NAME;
     }
@@ -2367,6 +2369,7 @@ void x265_copy_params(x265_param* dst, x
     dst->bEnableHRDConcatFlag = src->bEnableHRDConcatFlag;
     dst->dolbyProfile = src->dolbyProfile;
     dst->bEnableSvtHevc = src->bEnableSvtHevc;
+    dst->bEnableFades = src->bEnableFades;
 
 #ifdef SVT_HEVC
     memcpy(dst->svtHevcParam, src->svtHevcParam, sizeof(EB_H265_ENC_CONFIGURATION));
--- a/source/encoder/ratecontrol.cpp	Fri May 17 15:00:30 2019 +0530
+++ b/source/encoder/ratecontrol.cpp	Mon Apr 22 11:11:35 2019 +0530
@@ -1262,6 +1262,7 @@ int RateControl::rateControlStart(Frame*
     }
     rce->isActive = true;
     rce->scenecut = false;
+    rce->isFadeEnd = curFrame->m_lowres.bIsFadeEnd;
     bool isRefFrameScenecut = m_sliceType!= I_SLICE && m_curSlice->m_refFrameList[0][0]->m_lowres.bScenecut;
     m_isFirstMiniGop = m_sliceType == I_SLICE ? true : m_isFirstMiniGop;
     if (curFrame->m_lowres.bScenecut)
@@ -1373,6 +1374,8 @@ int RateControl::rateControlStart(Frame*
                         m_numBframesInPattern++;
                 }
             }
+            if (rce->isFadeEnd)
+                m_isPatternPresent = true;
         }
         /* For a scenecut that occurs within the mini-gop, enable scene transition
          * switch until the next mini-gop to ensure a min qp for all the frames within 
@@ -2097,7 +2100,7 @@ void RateControl::checkAndResetABR(RateC
     double abrBuffer = 2 * m_rateTolerance * m_bitrate;
 
     // Check if current Slice is a scene cut that follows low detailed/blank frames
-    if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut)
+    if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut || rce->isFadeEnd)
     {
         if (!m_isAbrReset && rce->movingAvgSum > 0
             && (m_isPatternPresent || !m_param->bframes))
@@ -2110,7 +2113,7 @@ void RateControl::checkAndResetABR(RateC
                 shrtTermTotalBitsSum += m_encodedBitsWindow[i];
             double underflow = (shrtTermTotalBitsSum - shrtTermWantedBits) / abrBuffer;
             const double epsilon = 0.0001f;
-            if (underflow < epsilon && !isFrameDone)
+            if ((underflow < epsilon || rce->isFadeEnd) && !isFrameDone)
             {
                 init(*m_curSlice->m_sps);
                 m_shortTermCplxSum = rce->lastSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);
--- a/source/encoder/ratecontrol.h	Fri May 17 15:00:30 2019 +0530
+++ b/source/encoder/ratecontrol.h	Mon Apr 22 11:11:35 2019 +0530
@@ -115,6 +115,7 @@ struct RateControlEntry
     HRDTiming        *hrdTiming;
     int      rpsIdx;
     RPS      rpsData;
+    bool     isFadeEnd;
 };
 
 class RateControl
--- a/source/encoder/slicetype.cpp	Fri May 17 15:00:30 2019 +0530
+++ b/source/encoder/slicetype.cpp	Mon Apr 22 11:11:35 2019 +0530
@@ -459,17 +459,21 @@ void LookaheadTLD::calcAdaptiveQuantFram
         }
     }
 
-    if (param->bDynamicRefine)
+    if (param->bDynamicRefine || param->bEnableFades)
     {
-        int blockXY = 0;
+        uint64_t blockXY = 0, rowVariance = 0;
+        curFrame->m_lowres.frameVariance = 0;
         for (int blockY = 0; blockY < maxRow; blockY += loopIncr)
         {
             for (int blockX = 0; blockX < maxCol; blockX += loopIncr)
             {
                 curFrame->m_lowres.blockVariance[blockXY] = acEnergyCu(curFrame, blockX, blockY, param->internalCsp, param->rc.qgSize);
+                rowVariance += curFrame->m_lowres.blockVariance[blockXY];
                 blockXY++;
             }
+            curFrame->m_lowres.frameVariance += (rowVariance / maxCol);
         }
+        curFrame->m_lowres.frameVariance /= maxRow;
     }
 }
 
@@ -757,6 +761,9 @@ Lookahead::Lookahead(x265_param *param, 
     m_8x8Width = ((m_param->sourceWidth / 2) + X265_LOWRES_CU_SIZE - 1) >> X265_LOWRES_CU_BITS;
     m_cuCount = m_8x8Width * m_8x8Height;
     m_8x8Blocks = m_8x8Width > 2 && m_8x8Height > 2 ? (m_cuCount + 4 - 2 * (m_8x8Width + m_8x8Height)) : m_cuCount;
+    m_isFadeIn = false;
+    m_fadeCount = 0;
+    m_fadeStart = -1;
 
     /* Allow the strength to be adjusted via qcompress, since the two concepts
      * are very similar. */
@@ -1181,7 +1188,6 @@ void PreLookaheadGroup::processTasks(int
 void Lookahead::slicetypeDecide()
 {
     PreLookaheadGroup pre(*this);
-
     Lowres* frames[X265_LOOKAHEAD_MAX + X265_BFRAME_MAX + 4];
     Frame*  list[X265_BFRAME_MAX + 4];
     memset(frames, 0, sizeof(frames));
@@ -1226,6 +1232,54 @@ void Lookahead::slicetypeDecide()
         pre.waitForExit();
     }
 
+    if(m_param->bEnableFades)
+    {
+        int j, endIndex = 0, length = X265_BFRAME_MAX + 4;
+        for (j = 0; j < length; j++)
+            m_frameVariance[j] = -1;
+        for (j = 0; list[j] != NULL; j++)
+            m_frameVariance[list[j]->m_poc % length] = list[j]->m_lowres.frameVariance;
+        for (int k = list[0]->m_poc % length; k <= list[j - 1]->m_poc % length; k++)
+        {
+            if (m_frameVariance[k]  == -1)
+                break;
+            if((k > 0 && m_frameVariance[k] >= m_frameVariance[k - 1]) || 
+                (k == 0 && m_frameVariance[k] >= m_frameVariance[length - 1]))
+            {
+                m_isFadeIn = true;
+                if (m_fadeCount == 0 && m_fadeStart == -1)
+                {
+                    for(int temp = list[0]->m_poc; temp <= list[j - 1]->m_poc; temp++)
+                        if (k == temp % length) {
+                            m_fadeStart = temp ? temp - 1 : 0;
+                            break;
+                        }
+                }
+                m_fadeCount = list[endIndex]->m_poc > m_fadeStart ? list[endIndex]->m_poc - m_fadeStart : 0;
+                endIndex++;
+            }
+            else
+            {
+                if (m_isFadeIn && m_fadeCount >= m_param->fpsNum / m_param->fpsDenom)
+                {
+                    for (int temp = 0; list[temp] != NULL; temp++)
+                    {
+                        if (list[temp]->m_poc == m_fadeStart + (int)m_fadeCount)
+                        {
+                            list[temp]->m_lowres.bIsFadeEnd = true;
+                            break;
+                        }
+                    }
+                }
+                m_isFadeIn = false;
+                m_fadeCount = 0;
+                m_fadeStart = -1;
+            }
+            if (k == length - 1)
+                k = -1;
+        }
+    }
+
     if (m_lastNonB && !m_param->rc.bStatRead &&
         ((m_param->bFrameAdaptive && m_param->bframes) ||
          m_param->rc.cuTree || m_param->scenecutThreshold ||
@@ -1285,6 +1339,9 @@ void Lookahead::slicetypeDecide()
                     frm.sliceType = m_param->bOpenGOP && m_lastKeyframe >= 0 ? X265_TYPE_I : X265_TYPE_IDR;
                 }
             }
+            if (frm.bIsFadeEnd){
+                frm.sliceType = m_param->bOpenGOP && m_lastKeyframe >= 0 ? X265_TYPE_I : X265_TYPE_IDR;
+            }
             for (int i = 0; i < m_param->rc.zonefileCount; i++)
             {
                 int curZoneStart = m_param->rc.zones[i].startFrame;
--- a/source/encoder/slicetype.h	Fri May 17 15:00:30 2019 +0530
+++ b/source/encoder/slicetype.h	Mon Apr 22 11:11:35 2019 +0530
@@ -134,6 +134,10 @@ public:
     bool          m_isSceneTransition;
     int           m_numPools;
     bool          m_extendGopBoundary;
+    double        m_frameVariance[X265_BFRAME_MAX + 4];
+    bool          m_isFadeIn;
+    uint64_t      m_fadeCount;
+    int           m_fadeStart;
     Lookahead(x265_param *param, ThreadPool *pool);
 #if DETAILED_CU_STATS
     int64_t       m_slicetypeDecideElapsedTime;
--- a/source/test/regression-tests.txt	Fri May 17 15:00:30 2019 +0530
+++ b/source/test/regression-tests.txt	Mon Apr 22 11:11:35 2019 +0530
@@ -151,6 +151,7 @@ Kimono1_1920x1080_24_400.yuv,--preset ve
 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 --vbv-maxrate 5000 --vbv-bufsize 5000
+big_buck_bunny_360p24.y4m, --bitrate 500 --fades
 
 # Main12 intraCost overflow bug test
 720p50_parkrun_ter.y4m,--preset medium
--- a/source/x265.h	Fri May 17 15:00:30 2019 +0530
+++ b/source/x265.h	Mon Apr 22 11:11:35 2019 +0530
@@ -1771,6 +1771,10 @@ typedef struct x265_param
 
     /* SVT-HEVC param structure. For internal use when SVT HEVC encoder is enabled */
     void* svtHevcParam;
+
+    /* Detect fade-in regions. Enforces I-slice for the brightest point.
+       Re-init RC history at that point in ABR mode. Default is disabled. */
+    int       bEnableFades;
 } x265_param;
 /* x265_param_alloc:
  *  Allocates an x265_param instance. The returned param structure is not
--- a/source/x265cli.h	Fri May 17 15:00:30 2019 +0530
+++ b/source/x265cli.h	Mon Apr 22 11:11:35 2019 +0530
@@ -124,6 +124,8 @@ static const struct option long_options[
     { "scenecut",       required_argument, NULL, 0 },
     { "no-scenecut",          no_argument, NULL, 0 },
     { "scenecut-bias",  required_argument, NULL, 0 },
+    { "fades",                no_argument, NULL, 0 },
+    { "no-fades",             no_argument, NULL, 0 },
     { "radl",           required_argument, NULL, 0 },
     { "ctu-info",       required_argument, NULL, 0 },
     { "intra-refresh",        no_argument, NULL, 0 },
@@ -471,6 +473,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("   --[no-]fades                  Enable detection and handling of fade-in regions. Default %s\n", OPT(param->bEnableFades));
     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);