From cf25444e6b0f82de7b751c2436dec22ea94755b9 Mon Sep 17 00:00:00 2001
From: Niranjan Kumar <niranjan@multicorewareinc.com>
Date: Wed, 21 Oct 2020 19:18:14 +0530
Subject: [PATCH] Add: Forward and Backward masking

Enables scenecut-aware-qp in a specified direction
0 - Disabled
1 - Forward masking
2 - Backward masking
3 - Bi-directional masking
---
 doc/reST/cli.rst               | 85 +++++++++++++++++++++++--------
 source/common/param.cpp        | 90 ++++++++++++++++++++++++++-------
 source/encoder/encoder.cpp     |  6 +--
 source/encoder/ratecontrol.cpp | 91 +++++++++++++++++++++++++++-------
 source/encoder/ratecontrol.h   |  1 +
 source/x265.h                  | 32 +++++++++---
 source/x265cli.cpp             | 10 ++--
 source/x265cli.h               |  7 +--
 8 files changed, 244 insertions(+), 78 deletions(-)

diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst
index 1a1de9f50..4e20daa90 100755
--- a/doc/reST/cli.rst
+++ b/doc/reST/cli.rst
@@ -1963,37 +1963,78 @@ Quality, rate control and rate distortion options
 	
 	**CLI ONLY**
 
-.. option:: --scenecut-aware-qp, --no-scenecut-aware-qp
+.. option:: --scenecut-aware-qp <integer>
    
-   It reduces the bits spent on the inter-frames within the :option:`--scenecut-window`
+   It reduces the bits spent on the inter-frames within the scenecut window
    before and after a scenecut by increasing their QP in ratecontrol pass2 algorithm
    without any deterioration in visual quality. If a scenecut falls within the window,
    the QP of the inter-frames after this scenecut will not be modified. 
-   :option:`--scenecut-aware-qp` works only with --pass 2. Default disabled.
+   :option:`--scenecut-aware-qp` works only with --pass 2. Default 0.
    
-.. option:: --scenecut-window <integer>
-
-   The duration(in milliseconds) for which there is a reduction in the bits spent
-   on the inter-frames after a scenecut by increasing their QP, when
-   :option:`--scenecut-aware-qp` is enabled. Default 500ms.
+    +-------+---------------------------------------------------------------+
+	| Mode  | Description                                                   |
+	+=======+===============================================================+
+	| 0     | Disabled.                                                     |
+	+-------+---------------------------------------------------------------+
+	| 1     | Forward masking.                                              |
+	|       | Applies QP modification for frames after the scenecut.        |
+	+-------+---------------------------------------------------------------+
+	| 2     | Backward masking.                                             |
+    |       | Applies QP modification for frames before the scenecut.       |
+	+-------+---------------------------------------------------------------+
+	| 3     | Bi-directional masking.                                       |
+	|       | Applies QP modification for frames before and after           |
+	|       | the scenecut.                                                 |
+	+-------+---------------------------------------------------------------+
    
-   **Range of values:** 0 to 1000
+.. option:: --masking-strength <string>
    
-.. option:: --qp-delta-ref <double>
-
-   The offset by which QP is incremented for inter-frames
-   when :option:`--scenecut-aware-qp` is enabled. Default 5.
+   Comma separated list of values which specifies the duration and offset
+   for the QP increment for inter-frames when :option:`--scenecut-aware-qp` 
+   is enabled.
    
-   **Range of values:**  0 to 10
+   When :option:`--scenecut-aware-qp` is::
+   * 1 (Forward masking):
+     --masking-strength <fwdWindow,fwdRefQPDelta,fwdNonRefQPDelta>
    
-.. option:: --qp-delta-nonref <double>
-
-   The offset by which QP is incremented for non-referenced
-   inter-frames when :option:`--scenecut-aware-qp` is enabled.
-   The offset is computed from :option:`--qp-delta-ref` when it
-   is not explicitly specified.
-
-   **Range of values:**  0 to 10
+   * 2 (Backward masking):
+     --masking-strength <bwdWindow,bwdRefQPDelta,bwdNonRefQPDelta>
+   
+   * 3 (Bi-directional masking):
+     --masking-strength <fwdWindow,fwdRefQPDelta,fwdNonRefQPDelta,bwdWindow,bwdRefQPDelta,bwdNonRefQPDelta>
+   
+    +-----------------+---------------------------------------------------------------+
+	| Parameter       | Description                                                   |
+	+=================+===============================================================+
+	| fwdWindow       | The duration(in milliseconds) for which there is a reduction  |
+	|                 | in the bits spent on the inter-frames after a scenecut by     |
+	|			      | increasing their QP. Default 500ms.                           |
+    |                 | **Range of values:** 0 to 1000	                              |
+	+-----------------+---------------------------------------------------------------+
+	| fwdRefQPDelta   | The offset by which QP is incremented for inter-frames        |
+	|                 | after a scenecut. Default 5.                                  |
+	|                 | **Range of values:** 0 to 10	                              |
+	+-----------------+---------------------------------------------------------------+
+	| fwdNonRefQPDelta| The offset by which QP is incremented for non-referenced      |
+    |                 | inter-frames after a scenecut. The offset is computed from    |
+	|                 | fwdRefQPDelta when it is not explicitly specified.            |
+	|                 | **Range of values:** 0 to 10	                              |
+	+-----------------+---------------------------------------------------------------+
+	| bwdWindow       | The duration(in milliseconds) for which there is a reduction  |
+	|                 | in the bits spent on the inter-frames before a scenecut by    |
+	|			      | increasing their QP. Default 100ms.                           |
+	|                 | **Range of values:** 0 to 1000	                              |
+	+-----------------+---------------------------------------------------------------+
+	| bwdRefQPDelta   | The offset by which QP is incremented for inter-frames        |
+	|                 | before a scenecut. The offset is computed from                |
+	|                 | fwdRefQPDelta when it is not explicitly specified.            |         
+	|                 | **Range of values:** 0 to 10	                              |
+	+-----------------+---------------------------------------------------------------+
+	| bwdNonRefQPDelta| The offset by which QP is incremented for non-referenced      |
+    |                 | inter-frames before a scenecut. The offset is computed from   |
+	|                 | bwdRefQPDelta when it is not explicitly specified.            |
+	|                 | **Range of values:** 0 to 10	                              |
+	+-----------------+---------------------------------------------------------------+
 
 .. option:: --vbv-live-multi-pass, --no-vbv-live-multi-pass
 
diff --git a/source/common/param.cpp b/source/common/param.cpp
index 47a7a7c47..fe038fdf2 100755
--- a/source/common/param.cpp
+++ b/source/common/param.cpp
@@ -179,9 +179,12 @@ void x265_param_default(x265_param* param)
     param->bEnableHRDConcatFlag = 0;
     param->bEnableFades = 0;
     param->bEnableSceneCutAwareQp = 0;
-    param->scenecutWindow = 500;
-    param->refQpDelta = 5;
-    param->nonRefQpDelta = param->refQpDelta + (SLICE_TYPE_DELTA * param->refQpDelta);
+    param->fwdScenecutWindow = 500;
+    param->fwdRefQpDelta = 5;
+    param->fwdNonRefQpDelta = param->fwdRefQpDelta + (SLICE_TYPE_DELTA * param->fwdRefQpDelta);
+    param->bwdScenecutWindow = 100;
+    param->bwdRefQpDelta = -1;
+    param->bwdNonRefQpDelta = -1;
 
     /* Intra Coding Tools */
     param->bEnableConstrainedIntra = 0;
@@ -1344,10 +1347,51 @@ int x265_param_parse(x265_param* p, const char* name, const char* value)
             p->selectiveSAO = atoi(value);
         }
         OPT("fades") p->bEnableFades = atobool(value);
-        OPT("scenecut-aware-qp") p->bEnableSceneCutAwareQp = atobool(value);
-        OPT("scenecut-window") p->scenecutWindow = atoi(value);
-        OPT("qp-delta-ref") p->refQpDelta = atoi(value);
-        OPT("qp-delta-nonref") p->nonRefQpDelta = atoi(value);
+        OPT("scenecut-aware-qp") p->bEnableSceneCutAwareQp = atoi(value);
+        OPT("masking-strength")
+        {
+            int window1;
+            double refQpDelta1, nonRefQpDelta1;
+
+            if (p->bEnableSceneCutAwareQp == FORWARD)
+            {
+                sscanf(value, "%d,%lf,%lf", &window1, &refQpDelta1, &nonRefQpDelta1);
+                if (window1 > 0)
+                    p->fwdScenecutWindow = window1;
+                if (refQpDelta1 > 0)
+                    p->fwdRefQpDelta = refQpDelta1;
+                if (nonRefQpDelta1 > 0)
+                    p->fwdNonRefQpDelta = nonRefQpDelta1;
+            }
+            else if (p->bEnableSceneCutAwareQp == BACKWARD)
+            {
+                sscanf(value, "%d,%lf,%lf", &window1, &refQpDelta1, &nonRefQpDelta1);
+                if (window1 > 0)
+                    p->bwdScenecutWindow = window1;
+                if (refQpDelta1 > 0)
+                    p->bwdRefQpDelta = refQpDelta1;
+                if (nonRefQpDelta1 > 0)
+                    p->bwdNonRefQpDelta = nonRefQpDelta1;
+            }
+            else if (p->bEnableSceneCutAwareQp == BI_DIRECTIONAL)
+            {
+                int window2;
+                double refQpDelta2, nonRefQpDelta2;
+                sscanf(value, "%d,%lf,%lf,%d,%lf,%lf", &window1, &refQpDelta1, &nonRefQpDelta1, &window2, &refQpDelta2, &nonRefQpDelta2);
+                if (window1 > 0)
+                    p->fwdScenecutWindow = window1;
+                if (refQpDelta1 > 0)
+                    p->fwdRefQpDelta = refQpDelta1;
+                if (nonRefQpDelta1 > 0)
+                    p->fwdNonRefQpDelta = nonRefQpDelta1;
+                if (window2 > 0)
+                    p->bwdScenecutWindow = window2;
+                if (refQpDelta2 > 0)
+                    p->bwdRefQpDelta = refQpDelta2;
+                if (nonRefQpDelta2 > 0)
+                    p->bwdNonRefQpDelta = nonRefQpDelta2;
+            }
+        }
         OPT("field") p->bField = atobool( value );
         OPT("cll") p->bEmitCLL = atobool(value);
         OPT("frame-dup") p->bEnableFrameDuplication = atobool(value);
@@ -1787,12 +1831,19 @@ int x265_check_params(x265_param* param)
         }
         else
         {
-            CHECK(param->scenecutWindow < 0 || param->scenecutWindow > 1000,
-            "Invalid scenecut Window duration. Value must be between 0 and 1000(inclusive)");
-            CHECK(param->refQpDelta < 0 || param->refQpDelta > 10,
-            "Invalid refQpDelta value. Value must be between 0 and 10 (inclusive)");
-            CHECK(param->nonRefQpDelta < 0 || param->nonRefQpDelta > 10,
-            "Invalid nonRefQpDelta value. Value must be between 0 and 10 (inclusive)");
+            CHECK(param->fwdScenecutWindow < 0 || param->fwdScenecutWindow > 1000,
+            "Invalid forward scenecut Window duration. Value must be between 0 and 1000(inclusive)");
+            CHECK(param->fwdRefQpDelta < 0 || param->fwdRefQpDelta > 10,
+            "Invalid fwdRefQpDelta value. Value must be between 0 and 10 (inclusive)");
+            CHECK(param->fwdNonRefQpDelta < 0 || param->fwdNonRefQpDelta > 10,
+            "Invalid fwdNonRefQpDelta value. Value must be between 0 and 10 (inclusive)");
+
+            CHECK(param->bwdScenecutWindow < 0 || param->bwdScenecutWindow > 1000,
+                "Invalid backward scenecut Window duration. Value must be between 0 and 1000(inclusive)");
+            CHECK(param->bwdRefQpDelta < -1 || param->bwdRefQpDelta > 10,
+                "Invalid bwdRefQpDelta value. Value must be between 0 and 10 (inclusive)");
+            CHECK(param->bwdNonRefQpDelta < -1 || param->bwdNonRefQpDelta > 10,
+                "Invalid bwdNonRefQpDelta value. Value must be between 0 and 10 (inclusive)");
         }
     }
     if (param->bEnableHME)
@@ -2252,9 +2303,7 @@ char *x265_param2string(x265_param* p, int padx, int pady)
     BOOL(p->bEnableSvtHevc, "svt");
     BOOL(p->bField, "field");
     s += sprintf(s, " qp-adaptation-range=%.2f", p->rc.qpAdaptationRange);
-    BOOL(p->bEnableSceneCutAwareQp, "scenecut-aware-qp");
-    if (p->bEnableSceneCutAwareQp)
-        s += sprintf(s, " scenecut-window=%d qp-delta-ref=%f qp-delta-nonref=%f", p->scenecutWindow, p->refQpDelta, p->nonRefQpDelta);
+    s += sprintf(s, " scenecut-aware-qp=%d", p->bEnableSceneCutAwareQp);
     s += sprintf(s, "conformance-window-offsets right=%d bottom=%d", p->confWinRightOffset, p->confWinBottomOffset);
     s += sprintf(s, " decoder-max-rate=%d", p->decoderVbvMaxRate);
     BOOL(p->bliveVBV2pass, "vbv-live-multi-pass");
@@ -2608,9 +2657,12 @@ void x265_copy_params(x265_param* dst, x265_param* src)
     dst->bEnableSvtHevc = src->bEnableSvtHevc;
     dst->bEnableFades = src->bEnableFades;
     dst->bEnableSceneCutAwareQp = src->bEnableSceneCutAwareQp;
-    dst->scenecutWindow = src->scenecutWindow;
-    dst->refQpDelta = src->refQpDelta;
-    dst->nonRefQpDelta = src->nonRefQpDelta;
+    dst->fwdScenecutWindow = src->fwdScenecutWindow;
+    dst->fwdRefQpDelta = src->fwdRefQpDelta;
+    dst->fwdNonRefQpDelta = src->fwdNonRefQpDelta;
+    dst->bwdScenecutWindow = src->bwdScenecutWindow;
+    dst->bwdRefQpDelta = src->bwdRefQpDelta;
+    dst->bwdNonRefQpDelta = src->bwdNonRefQpDelta;
     dst->bField = src->bField;
 
     dst->confWinRightOffset = src->confWinRightOffset;
diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
index 1f710e1ce..d880bc62a 100644
--- a/source/encoder/encoder.cpp
+++ b/source/encoder/encoder.cpp
@@ -1810,13 +1810,13 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)
                 inFrame->m_lowres.m_bIsHardScenecut = isHardSC;
         }
 
-        if (m_param->bEnableSceneCutAwareQp && m_param->rc.bStatRead)
+        if ((m_param->bEnableSceneCutAwareQp == BI_DIRECTIONAL || m_param->bEnableSceneCutAwareQp == BACKWARD) && m_param->rc.bStatRead)
         {
             RateControlEntry * rcEntry = NULL;
             rcEntry = &(m_rateControl->m_rce2Pass[inFrame->m_poc]);
             if(rcEntry->scenecut)
             {
-                int backwardWindow = X265_MIN(int((p->fpsNum / p->fpsDenom) / 10), p->lookaheadDepth);
+                int backwardWindow = X265_MIN(int((m_param->bwdScenecutWindow / 1000.0) * (m_param->fpsNum / m_param->fpsDenom)), p->lookaheadDepth);
                 for (int i = 1; i <= backwardWindow; i++)
                 {
                     int frameNum = inFrame->m_poc - i;
@@ -2253,7 +2253,7 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)
                         m_rateControl->m_lastScenecut = frameEnc->m_poc;
                     else
                     {
-                        int maxWindowSize = int((m_param->scenecutWindow / 1000.0) * (m_param->fpsNum / m_param->fpsDenom) + 0.5);
+                        int maxWindowSize = int((m_param->fwdScenecutWindow / 1000.0) * (m_param->fpsNum / m_param->fpsDenom) + 0.5);
                         if (frameEnc->m_poc > (m_rateControl->m_lastScenecut + maxWindowSize))
                             m_rateControl->m_lastScenecut = frameEnc->m_poc;
                     }
diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp
index 4e7d52419..606c36bf5 100644
--- a/source/encoder/ratecontrol.cpp
+++ b/source/encoder/ratecontrol.cpp
@@ -1854,7 +1854,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
             rce->qpNoVbv = q;
         }
         /* Scenecut Aware QP offsets*/
-        if (m_param->bEnableSceneCutAwareQp)
+        if (m_param->bEnableSceneCutAwareQp == BI_DIRECTIONAL || m_param->bEnableSceneCutAwareQp == FORWARD)
         {
             double lqmin = m_lmin[m_sliceType];
             double lqmax = m_lmax[m_sliceType];
@@ -1863,6 +1863,15 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
             q = x265_qScale2qp(qScale);
             rce->qpNoVbv = q;
         }
+        else if(m_param->bEnableSceneCutAwareQp == BACKWARD)
+        {
+            double lqmin = m_lmin[m_sliceType];
+            double lqmax = m_lmax[m_sliceType];
+            qScale = backwardMasking(curFrame, qScale);
+            qScale = x265_clip3(lqmin, lqmax, qScale);
+            q = x265_qScale2qp(qScale);
+            rce->qpNoVbv = q;
+        }
 
         if (m_isVbv)
         {
@@ -1977,7 +1986,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
             }
 
             /* Scenecut Aware QP offsets*/
-            if (m_param->bEnableSceneCutAwareQp)
+            if (m_param->bEnableSceneCutAwareQp == BI_DIRECTIONAL || m_param->bEnableSceneCutAwareQp == FORWARD)
             {
                 double qmin = m_lmin[m_sliceType];
                 double qmax = m_lmax[m_sliceType];
@@ -1985,6 +1994,14 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
                 q = x265_clip3(qmin, qmax, q);
                 rce->qpNoVbv = x265_qScale2qp(q);
             }
+            else if (m_param->bEnableSceneCutAwareQp == BACKWARD)
+            {
+                double qmin = m_lmin[m_sliceType];
+                double qmax = m_lmax[m_sliceType];
+                q = backwardMasking(curFrame, q);
+                q = x265_clip3(qmin, qmax, q);
+                rce->qpNoVbv = x265_qScale2qp(q);
+            }
 
             if (m_isVbv)
             {
@@ -2141,7 +2158,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
                 q = x265_clip3(lqmin, lqmax, q);
             }
             /* Scenecut Aware QP offsets*/
-            if (m_param->bEnableSceneCutAwareQp)
+            if (m_param->bEnableSceneCutAwareQp == BI_DIRECTIONAL || m_param->bEnableSceneCutAwareQp == FORWARD)
             {
                 double qmin = m_lmin[m_sliceType];
                 double qmax = m_lmax[m_sliceType];
@@ -2149,6 +2166,14 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
                 q = x265_clip3(qmin, qmax, q);
                 rce->qpNoVbv = x265_qScale2qp(q);
             }
+            else if (m_param->bEnableSceneCutAwareQp == BACKWARD)
+            {
+                double qmin = m_lmin[m_sliceType];
+                double qmax = m_lmax[m_sliceType];
+                q = backwardMasking(curFrame, q);
+                q = x265_clip3(qmin, qmax, q);
+                rce->qpNoVbv = x265_qScale2qp(q);
+            }
             q = clipQscale(curFrame, rce, q);
 
             if (m_2pass)
@@ -3171,15 +3196,17 @@ void RateControl::splitbUsed(char bused[], RateControlEntry *rce)
 double RateControl::scenecutAwareMasking(Frame* curFrame, double q)
 {
     double qp = x265_qScale2qp(q);
-    uint32_t maxWindowSize = uint32_t((m_param->scenecutWindow / 1000.0) * (m_param->fpsNum / m_param->fpsDenom) + 0.5);
+    uint32_t maxWindowSize = uint32_t((m_param->fwdScenecutWindow / 1000.0) * (m_param->fpsNum / m_param->fpsDenom) + 0.5);
     uint32_t windowSize = maxWindowSize / 3;
     int lastScenecut = m_top->m_rateControl->m_lastScenecut;
     int lastIFrame = m_top->m_rateControl->m_lastScenecutAwareIFrame;
-    double refQpDelta = double(m_param->refQpDelta);
-    double nonRefQpDelta = double(m_param->nonRefQpDelta);
-    double sliceTypeDelta = SLICE_TYPE_DELTA * refQpDelta;
-    double window2Delta = WINDOW2_DELTA * refQpDelta;
-    double window3Delta = WINDOW3_DELTA * refQpDelta;
+    double fwdRefQpDelta = double(m_param->fwdRefQpDelta);
+    double fwdNonRefQpDelta = double(m_param->fwdNonRefQpDelta);
+    double bwdRefQpDelta = double(m_param->bwdRefQpDelta);
+    double bwdNonRefQpDelta = double(m_param->bwdNonRefQpDelta);
+    double sliceTypeDelta = SLICE_TYPE_DELTA * fwdRefQpDelta;
+    double window2Delta = WINDOW2_DELTA * fwdRefQpDelta;
+    double window3Delta = WINDOW3_DELTA * fwdRefQpDelta;
 
     if (curFrame->m_poc > lastScenecut && curFrame->m_poc <= (lastScenecut + int(maxWindowSize)))
         curFrame->m_isInsideWindow = FORWARD_WINDOW;
@@ -3194,7 +3221,7 @@ double RateControl::scenecutAwareMasking(Frame* curFrame, double q)
             if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int(maxWindowSize))
                 && curFrame->m_poc >= lastIFrame))
             {
-                qp += refQpDelta - sliceTypeDelta;
+                qp += fwdRefQpDelta - sliceTypeDelta;
                 if (((curFrame->m_poc) > (lastScenecut + int(windowSize))) && ((curFrame->m_poc) <= (lastScenecut + 2 * int(windowSize))))
                     qp -= window2Delta;
                 else if (curFrame->m_poc > lastScenecut + 2 * int(windowSize))
@@ -3206,7 +3233,7 @@ double RateControl::scenecutAwareMasking(Frame* curFrame, double q)
             if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int(maxWindowSize))
                 && curFrame->m_poc >= lastIFrame))
             {
-                qp += refQpDelta;
+                qp += fwdRefQpDelta;
                 if (((curFrame->m_poc) > (lastScenecut + int(windowSize))) && ((curFrame->m_poc) <= (lastScenecut + 2 * int(windowSize))))
                     qp -= window2Delta;
                 else if (curFrame->m_poc > lastScenecut + 2 * int(windowSize))
@@ -3218,7 +3245,7 @@ double RateControl::scenecutAwareMasking(Frame* curFrame, double q)
             if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int(maxWindowSize))
                 && curFrame->m_poc >= lastIFrame))
             {
-                qp += nonRefQpDelta;
+                qp += fwdNonRefQpDelta;
                 if (((curFrame->m_poc) > (lastScenecut + int(windowSize))) && ((curFrame->m_poc) <= (lastScenecut + 2 * int(windowSize))))
                     qp -= window2Delta;
                 else if (curFrame->m_poc > lastScenecut + 2 * int(windowSize))
@@ -3228,14 +3255,44 @@ double RateControl::scenecutAwareMasking(Frame* curFrame, double q)
     }
     else if (curFrame->m_isInsideWindow == BACKWARD_WINDOW)
     {
-        refQpDelta -= window3Delta;
-        nonRefQpDelta -= window3Delta;
+        if (bwdRefQpDelta < 0)
+            bwdRefQpDelta = fwdRefQpDelta - window3Delta;
+        sliceTypeDelta = SLICE_TYPE_DELTA * bwdRefQpDelta;
+        if (bwdNonRefQpDelta < 0)
+            bwdNonRefQpDelta = bwdRefQpDelta + sliceTypeDelta;
+
+        if (curFrame->m_lowres.sliceType == X265_TYPE_P)
+            qp += bwdRefQpDelta - sliceTypeDelta;
+        else if (curFrame->m_lowres.sliceType == X265_TYPE_BREF)
+            qp += bwdRefQpDelta;
+        else if (curFrame->m_lowres.sliceType == X265_TYPE_B)
+            qp += bwdNonRefQpDelta;
+    }
+
+    return x265_qp2qScale(qp);
+}
+double RateControl::backwardMasking(Frame* curFrame, double q)
+{
+    double qp = x265_qScale2qp(q);
+    double fwdRefQpDelta = double(m_param->fwdRefQpDelta);
+    double window3Delta = WINDOW3_DELTA * fwdRefQpDelta;
+    double bwdRefQpDelta = double(m_param->bwdRefQpDelta);
+    double bwdNonRefQpDelta = double(m_param->bwdNonRefQpDelta);
+
+    if (curFrame->m_isInsideWindow == BACKWARD_WINDOW)
+    {
+        if (bwdRefQpDelta < 0)
+            bwdRefQpDelta = fwdRefQpDelta - window3Delta;
+        double sliceTypeDelta = SLICE_TYPE_DELTA * bwdRefQpDelta;
+        if (bwdNonRefQpDelta < 0)
+            bwdNonRefQpDelta = bwdRefQpDelta + sliceTypeDelta;
+
         if (curFrame->m_lowres.sliceType == X265_TYPE_P)
-            qp += refQpDelta - sliceTypeDelta;
+            qp += bwdRefQpDelta - sliceTypeDelta;
         else if (curFrame->m_lowres.sliceType == X265_TYPE_BREF)
-            qp += refQpDelta;
+            qp += bwdRefQpDelta;
         else if (curFrame->m_lowres.sliceType == X265_TYPE_B)
-            qp += nonRefQpDelta;
+            qp += bwdNonRefQpDelta;
     }
 
     return x265_qp2qScale(qp);
diff --git a/source/encoder/ratecontrol.h b/source/encoder/ratecontrol.h
index 809e0c620..dc4d882a6 100644
--- a/source/encoder/ratecontrol.h
+++ b/source/encoder/ratecontrol.h
@@ -270,6 +270,7 @@ public:
     bool   initPass2();
 
     double scenecutAwareMasking(Frame* curFrame, double q);
+    double backwardMasking(Frame* curFrame, double q);
 
 protected:
 
diff --git a/source/x265.h b/source/x265.h
index f44040ba7..668e9a9c2 100644
--- a/source/x265.h
+++ b/source/x265.h
@@ -607,6 +607,9 @@ typedef enum
 #define X265_ANALYSIS_SAVE 1
 #define X265_ANALYSIS_LOAD 2
 
+#define FORWARD                 1
+#define BACKWARD                2
+#define BI_DIRECTIONAL          3
 #define SLICE_TYPE_DELTA        0.3 /* The offset decremented or incremented for P-frames or b-frames respectively*/
 #define BACKWARD_WINDOW         1 /* Scenecut window before a scenecut */
 #define FORWARD_WINDOW          2 /* Scenecut window after a scenecut */
@@ -1847,21 +1850,34 @@ typedef struct x265_param
       Default 1 (Enabled). API only. */
     int       bResetZoneConfig;
 
-    /* It reduces the bits spent on the inter-frames within the scenecutWindow before and after a scenecut
+    /* It reduces the bits spent on the inter-frames within the scenecutWindow before and / or after a scenecut
      * by increasing their QP in ratecontrol pass2 algorithm without any deterioration in visual quality.
-     * Default is disabled. */
+     * 0 - Disabled (default).
+     * 1 - Forward masking.
+     * 2 - Backward masking.
+     * 3 - Bi-directional masking. */
     int       bEnableSceneCutAwareQp;
 
     /* The duration(in milliseconds) for which there is a reduction in the bits spent on the inter-frames after a scenecut
-     * by increasing their QP, when bEnableSceneCutAwareQp is set. Default is 500ms.*/
-    int       scenecutWindow;
+     * by increasing their QP, when bEnableSceneCutAwareQp is 1 or 3. Default is 500ms.*/
+    int       fwdScenecutWindow;
 
-    /* The offset by which QP is incremented for inter-frames when bEnableSceneCutAwareQp is set.
+    /* The offset by which QP is incremented for inter-frames after a scenecut when bEnableSceneCutAwareQp is 1 or 3.
      * Default is +5. */
-    double       refQpDelta;
+    double    fwdRefQpDelta;
 
-    /* The offset by which QP is incremented for non-referenced inter-frames when bEnableSceneCutAwareQp is set. */
-    double       nonRefQpDelta;
+    /* The offset by which QP is incremented for non-referenced inter-frames after a scenecut when bEnableSceneCutAwareQp is 1 or 3. */
+    double    fwdNonRefQpDelta;
+
+    /* The duration(in milliseconds) for which there is a reduction in the bits spent on the inter-frames before a scenecut
+     * by increasing their QP, when bEnableSceneCutAwareQp is 2 or 3. Default is 100ms.*/
+    int       bwdScenecutWindow;
+
+    /* The offset by which QP is incremented for inter-frames before a scenecut when bEnableSceneCutAwareQp is 2 or 3. */
+    double    bwdRefQpDelta;
+
+    /* The offset by which QP is incremented for non-referenced inter-frames before a scenecut when bEnableSceneCutAwareQp is 2 or 3. */
+    double    bwdNonRefQpDelta;
 
     /* A genuine threshold used for histogram based scene cut detection.
      * This threshold determines whether a frame is a scenecut or not
diff --git a/source/x265cli.cpp b/source/x265cli.cpp
index c28dd7f8c..0f50589c1 100755
--- a/source/x265cli.cpp
+++ b/source/x265cli.cpp
@@ -177,10 +177,12 @@ namespace X265_NS {
         H0("   --no-hist-scenecut            Disables histogram based scene-cut detection using histogram based algorithm.\n");
         H1("   --hist-threshold <0.0..1.0>   Luma Edge histogram's Normalized SAD threshold for histogram based scenecut detection Default %.2f\n", param->edgeTransitionThreshold);
         H0("   --[no-]fades                  Enable detection and handling of fade-in regions. Default %s\n", OPT(param->bEnableFades));
-        H1("   --[no-]scenecut-aware-qp      Enable increasing QP for frames inside the scenecut window after scenecut. Default %s\n", OPT(param->bEnableSceneCutAwareQp));
-        H1("   --scenecut-window <0..1000>   QP incremental duration(in milliseconds) when scenecut-aware-qp is enabled. Default %d\n", param->scenecutWindow);
-        H1("   --qp-delta-ref <0..10>        QP offset to increment with base QP for inter-frames. Default %f\n", param->refQpDelta);
-        H1("   --qp-delta-nonref <0..10>     QP offset to increment with base QP for non-referenced inter-frames. Default %f\n", param->nonRefQpDelta);
+        H1("   --scenecut-aware-qp <0..3>    Enable increasing QP for frames inside the scenecut window around scenecut. Default %s\n", OPT(param->bEnableSceneCutAwareQp));
+        H1("                                 0 - Disabled\n");
+        H1("                                 1 - Forward masking\n");
+        H1("                                 2 - Backward masking\n");
+        H1("                                 3 - Bidirectional masking\n");
+        H1("   --masking-strength <string>   Comma separated values which specifies the duration and offset for the QP increment for inter-frames");
         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);
diff --git a/source/x265cli.h b/source/x265cli.h
index a24d25435..7a2e0a267 100644
--- a/source/x265cli.h
+++ b/source/x265cli.h
@@ -148,11 +148,8 @@ static const struct option long_options[] =
     { "hist-threshold", required_argument, NULL, 0},
     { "fades",                no_argument, NULL, 0 },
     { "no-fades",             no_argument, NULL, 0 },
-    { "scenecut-aware-qp",    no_argument, NULL, 0 },
-    { "no-scenecut-aware-qp", no_argument, NULL, 0 },
-    { "scenecut-window",required_argument, NULL, 0 },
-    { "qp-delta-ref",   required_argument, NULL, 0 },
-    { "qp-delta-nonref",required_argument, NULL, 0 },
+    { "scenecut-aware-qp", required_argument, NULL, 0 },
+    { "masking-strength",  required_argument, NULL, 0 },
     { "radl",           required_argument, NULL, 0 },
     { "ctu-info",       required_argument, NULL, 0 },
     { "intra-refresh",        no_argument, NULL, 0 },
-- 
2.18.0.windows.1

