[x265] [x265 patch] Adaptive Frame Duplication

Akil akil at multicorewareinc.com
Thu Sep 26 10:33:34 CEST 2019


# HG changeset patch
# User Akil Ayyappan<akil at multicorewareinc.com>
# Date 1568370446 -19800
#      Fri Sep 13 15:57:26 2019 +0530
# Node ID e6e6ffeb158f5bfea074c65e9d2548f721de7a4e
# Parent  c4b098f973e6b0ee4aee3bf0d7b54da4e2734d42
Adaptive Frame duplication

This patch does the following.
1. Replaces 2-3 near-identical frames with one frame and sets pic_struct
based on frame doubling / tripling.
2. Add option "--frame-dup" and "--dup-threshold' to enable frame
duplication and to set threshold for frame similarity (optional).

diff -r c4b098f973e6 -r e6e6ffeb158f doc/reST/cli.rst
--- a/doc/reST/cli.rst Tue Aug 13 10:51:21 2019 +0530
+++ b/doc/reST/cli.rst Fri Sep 13 15:57:26 2019 +0530
@@ -501,6 +501,16 @@
  second. The decoder must re-combine the fields in their correct
  orientation for display.

+.. option:: --frame-dup, --no-frame-dup
+
+ Enable Frame duplication. Replaces 2-3 near-identical frames with one
+ frame and sets pic_struct based on frame doubling / tripling.
+ Default disabled.
+
+.. option:: --dup-threshold <integer>
+
+ Frame similarity threshold can vary between 1 and 99. Default 70.
+
 .. option:: --seek <integer>

  Number of frames to skip at start of input file. Default 0
diff -r c4b098f973e6 -r e6e6ffeb158f source/CMakeLists.txt
--- a/source/CMakeLists.txt Tue Aug 13 10:51:21 2019 +0530
+++ b/source/CMakeLists.txt Fri Sep 13 15:57:26 2019 +0530
@@ -29,7 +29,7 @@
 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 179)
+set(X265_BUILD 180)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r c4b098f973e6 -r e6e6ffeb158f source/common/frame.cpp
--- a/source/common/frame.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/common/frame.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -57,6 +57,7 @@
     m_addOnPrevChange = NULL;
     m_classifyFrame = false;
     m_fieldNum = 0;
+    m_picStruct = 0;
 }

 bool Frame::create(x265_param *param, float* quantOffsets)
diff -r c4b098f973e6 -r e6e6ffeb158f source/common/frame.h
--- a/source/common/frame.h Tue Aug 13 10:51:21 2019 +0530
+++ b/source/common/frame.h Fri Sep 13 15:57:26 2019 +0530
@@ -98,6 +98,7 @@

     float*                 m_quantOffsets;       // points to quantOffsets
in x265_picture
     x265_sei               m_userSEI;
+    uint32_t               m_picStruct;          // picture structure SEI
message
     x265_dolby_vision_rpu            m_rpu;

     /* Frame Parallelism - notification between FrameEncoders of available
motion reference rows */
diff -r c4b098f973e6 -r e6e6ffeb158f source/common/param.cpp
--- a/source/common/param.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/common/param.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -135,6 +135,7 @@

     /* Source specifications */
     param->internalBitDepth = X265_DEPTH;
+    param->sourceBitDepth = 8;
     param->internalCsp = X265_CSP_I420;
     param->levelIdc = 0; //Auto-detect level
     param->uhdBluray = 0;
@@ -338,6 +339,9 @@
     param->pictureStructure = -1;
     param->bEmitCLL = 1;

+    param->bEnableFrameDuplication = 0;
+    param->dupThreshold = 70;
+
     /* SVT Hevc Encoder specific params */
     param->bEnableSvtHevc = 0;
     param->svtHevcParam = NULL;
@@ -1294,6 +1298,8 @@
         OPT("fades") p->bEnableFades = atobool(value);
         OPT("field") p->bField = atobool( value );
         OPT("cll") p->bEmitCLL = atobool(value);
+        OPT("frame-dup") p->bEnableFrameDuplication = atobool(value);
+        OPT("dup-threshold") p->dupThreshold = atoi(value);
         OPT("hme") p->bEnableHME = atobool(value);
         OPT("hme-search")
         {
@@ -1680,6 +1686,8 @@
         "Supported factor for controlling max AU size is from 0.5 to 1");
     CHECK((param->dolbyProfile != 0) && (param->dolbyProfile != 50) &&
(param->dolbyProfile != 81) && (param->dolbyProfile != 82),
         "Unsupported Dolby Vision profile, only profile 5, profile 8.1 and
profile 8.2 enabled");
+    CHECK(param->dupThreshold < 1 || 99 < param->dupThreshold,
+        "Invalid frame-duplication threshold. Value must be between 1 and
99.");
     if (param->dolbyProfile)
     {
         CHECK((param->rc.vbvMaxBitrate <= 0 || param->rc.vbvBufferSize <=
0), "Dolby Vision requires VBV settings to enable HRD.\n");
@@ -1972,6 +1980,9 @@
     s += sprintf(s, " subme=%d", p->subpelRefine);
     s += sprintf(s, " merange=%d", p->searchRange);
     BOOL(p->bEnableTemporalMvp, "temporal-mvp");
+    BOOL(p->bEnableFrameDuplication, "frame-dup");
+    if(p->bEnableFrameDuplication)
+        s += sprintf(s, " dup-threshold=%d", p->dupThreshold);
     BOOL(p->bEnableHME, "hme");
     if (p->bEnableHME)
         s += sprintf(s, " Level 0,1,2=%d,%d,%d", p->hmeSearchMethod[0],
p->hmeSearchMethod[1], p->hmeSearchMethod[2]);
@@ -2209,6 +2220,7 @@
     if (src->csvfn) dst->csvfn = strdup(src->csvfn);
     else dst->csvfn = NULL;
     dst->internalBitDepth = src->internalBitDepth;
+    dst->sourceBitDepth = src->sourceBitDepth;
     dst->internalCsp = src->internalCsp;
     dst->fpsNum = src->fpsNum;
     dst->fpsDenom = src->fpsDenom;
@@ -2263,6 +2275,8 @@
     dst->subpelRefine = src->subpelRefine;
     dst->searchRange = src->searchRange;
     dst->bEnableTemporalMvp = src->bEnableTemporalMvp;
+    dst->bEnableFrameDuplication = src->bEnableFrameDuplication;
+    dst->dupThreshold = src->dupThreshold;
     dst->bEnableHME = src->bEnableHME;
     if (src->bEnableHME)
     {
diff -r c4b098f973e6 -r e6e6ffeb158f source/encoder/api.cpp
--- a/source/encoder/api.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/encoder/api.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -923,6 +923,7 @@
     pic->userSEI.numPayloads = 0;
     pic->rpu.payloadSize = 0;
     pic->rpu.payload = NULL;
+    pic->picStruct = 0;

     if ((param->analysisSave || param->analysisLoad) ||
(param->bAnalysisType == AVC_INFO))
     {
diff -r c4b098f973e6 -r e6e6ffeb158f source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/encoder/encoder.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -117,6 +117,8 @@
     m_cR = 1.0;
     for (int i = 0; i < X265_MAX_FRAME_THREADS; i++)
         m_frameEncoder[i] = NULL;
+    for (uint32_t i = 0; i < DUP_BUFFER; i++)
+        m_dupBuffer[i] = NULL;
     MotionEstimate::initScales();

 #if ENABLE_HDR10_PLUS
@@ -160,6 +162,32 @@
     int rows = (p->sourceHeight + p->maxCUSize - 1) >>
g_log2Size[p->maxCUSize];
     int cols = (p->sourceWidth  + p->maxCUSize - 1) >>
g_log2Size[p->maxCUSize];

+
+
+    if (m_param->bEnableFrameDuplication)
+    {
+        size_t framesize = 0;
+        int pixelbytes = p->sourceBitDepth > 8 ? 2 : 1;
+        for (int i = 0; i < x265_cli_csps[p->internalCsp].planes; i++)
+        {
+            int stride = (p->sourceWidth >>
x265_cli_csps[p->internalCsp].width[i]) * pixelbytes;
+            framesize += (stride * (p->sourceHeight >>
x265_cli_csps[p->internalCsp].height[i]));
+        }
+
+        for (uint32_t i = 0; i < DUP_BUFFER; i++)
+        {
+            m_dupBuffer[i] =
(AdaptiveFrameDuplication*)x265_malloc(sizeof(AdaptiveFrameDuplication));
+            m_dupBuffer[i]->dupPic = NULL;
+            m_dupBuffer[i]->dupPic = x265_picture_alloc();
+            x265_picture_init(p, m_dupBuffer[i]->dupPic);
+            m_dupBuffer[i]->dupPlane = NULL;
+            m_dupBuffer[i]->dupPlane = X265_MALLOC(char, framesize);
+            m_dupBuffer[i]->dupPic->planes[0] = m_dupBuffer[i]->dupPlane;
+            m_dupBuffer[i]->bOccupied = 0;
+            m_dupBuffer[i]->bDup = 0;
+        }
+    }
+
     // Do not allow WPP if only one row or fewer than 3 columns, it is
pointless and unstable
     if (rows == 1 || cols < 3)
     {
@@ -771,6 +799,16 @@
         m_exportedPic = NULL;
     }

+    if (m_param->bEnableFrameDuplication)
+    {
+        for (uint32_t i = 0; i < DUP_BUFFER; i++)
+        {
+            X265_FREE(m_dupBuffer[i]->dupPlane);
+            x265_picture_free(m_dupBuffer[i]->dupPic);
+            X265_FREE(m_dupBuffer[i]);
+        }
+    }
+
     for (int i = 0; i < m_param->frameNumThreads; i++)
     {
         if (m_frameEncoder[i])
@@ -981,6 +1019,159 @@
     }
 }

+//Find Sum of Squared Difference (SSD) between two pictures
+uint64_t Encoder::computeSSD(pixel *fenc, pixel *rec, intptr_t stride,
uint32_t width, uint32_t height, x265_param *param)
+{
+    uint64_t ssd = 0;
+
+    if (!param->bEnableFrameDuplication)
+    {
+        if ((width | height) & 3)
+        {
+            /* Slow Path */
+            for (uint32_t y = 0; y < height; y++)
+            {
+                for (uint32_t x = 0; x < width; x++)
+                {
+                    int diff = (int)(fenc[x] - rec[x]);
+                    ssd += diff * diff;
+                }
+
+                fenc += stride;
+                rec += stride;
+            }
+
+            return ssd;
+        }
+    }
+
+    uint32_t y = 0;
+
+    /* Consume rows in ever narrower chunks of height */
+    for (int size = BLOCK_64x64; size >= BLOCK_4x4 && y < height; size--)
+    {
+        uint32_t rowHeight = 1 << (size + 2);
+
+        for (; y + rowHeight <= height; y += rowHeight)
+        {
+            uint32_t y1, x = 0;
+
+            /* Consume each row using the largest square blocks possible */
+            if (size == BLOCK_64x64 && !(stride & 31))
+                for (; x + 64 <= width; x += 64)
+                    ssd += primitives.cu[BLOCK_64x64].sse_pp(fenc + x,
stride, rec + x, stride);
+
+            if (size >= BLOCK_32x32 && !(stride & 15))
+                for (; x + 32 <= width; x += 32)
+                    for (y1 = 0; y1 + 32 <= rowHeight; y1 += 32)
+                        ssd += primitives.cu[BLOCK_32x32].sse_pp(fenc + y1
* stride + x, stride, rec + y1 * stride + x, stride);
+
+            if (size >= BLOCK_16x16)
+                for (; x + 16 <= width; x += 16)
+                    for (y1 = 0; y1 + 16 <= rowHeight; y1 += 16)
+                        ssd += primitives.cu[BLOCK_16x16].sse_pp(fenc + y1
* stride + x, stride, rec + y1 * stride + x, stride);
+
+            if (size >= BLOCK_8x8)
+                for (; x + 8 <= width; x += 8)
+                    for (y1 = 0; y1 + 8 <= rowHeight; y1 += 8)
+                        ssd += primitives.cu[BLOCK_8x8].sse_pp(fenc + y1 *
stride + x, stride, rec + y1 * stride + x, stride);
+
+            for (; x + 4 <= width; x += 4)
+                for (y1 = 0; y1 + 4 <= rowHeight; y1 += 4)
+                    ssd += primitives.cu[BLOCK_4x4].sse_pp(fenc + y1 *
stride + x, stride, rec + y1 * stride + x, stride);
+
+            fenc += stride * rowHeight;
+            rec += stride * rowHeight;
+        }
+    }
+
+    /* Handle last few rows of frames for videos
+    with height not divisble by 4 */
+    uint32_t h = height % y;
+    if (param->bEnableFrameDuplication && h)
+    {
+        for (uint32_t i = 0; i < h; i++)
+        {
+            for (uint32_t j = 0; j < width; j++)
+            {
+                int diff = (int)(fenc[j] - rec[j]);
+                ssd += diff * diff;
+            }
+
+            fenc += stride;
+            rec += stride;
+        }
+    }
+
+    return ssd;
+}
+
+//Compute the PSNR weightage between two pictures
+double Encoder::ComputePSNR(x265_picture *firstPic, x265_picture *secPic,
x265_param *param)
+{
+    uint64_t ssdY = 0, ssdU = 0, ssdV = 0;
+    intptr_t strideL, strideC;
+    uint32_t widthL, heightL, widthC, heightC;
+    double psnrY = 0, psnrU = 0, psnrV = 0, psnrWeight = 0;
+    int width = firstPic->width;
+    int height = firstPic->height;
+    int hshift = CHROMA_H_SHIFT(firstPic->colorSpace);
+    int vshift = CHROMA_V_SHIFT(firstPic->colorSpace);
+
+    strideL = widthL = width;
+    heightL = height;
+
+    strideC = widthC = widthL >> hshift;
+    heightC = heightL >> vshift;
+
+    int size = width * height;
+    int maxvalY = 255 << (X265_DEPTH - 8);
+    int maxvalC = 255 << (X265_DEPTH - 8);
+    double refValueY = (double)maxvalY * maxvalY * size;
+    double refValueC = (double)maxvalC * maxvalC * size / 4.0;
+
+    pixel *yFirstPic = (pixel*)firstPic->planes[0];
+    pixel *ySecPic = (pixel*)secPic->planes[0];
+    ssdY = computeSSD(yFirstPic, ySecPic, strideL, widthL, heightL, param);
+    psnrY = (ssdY ? 10.0 * log10(refValueY / (double)ssdY) : 99.99);
+
+    if (param->internalCsp != X265_CSP_I400)
+    {
+        pixel *uFirstPic = (pixel*)firstPic->planes[1];
+        pixel *vFirstPic = (pixel*)firstPic->planes[2];
+        pixel *uSecPic = (pixel*)secPic->planes[1];
+        pixel *vSecPic = (pixel*)secPic->planes[2];
+        ssdU = computeSSD(uFirstPic, uSecPic, strideC, widthC, heightC,
param);
+        ssdV = computeSSD(vFirstPic, vSecPic, strideC, widthC, heightC,
param);
+        psnrU = (ssdU ? 10.0 * log10(refValueC / (double)ssdU) : 99.99);
+        psnrV = (ssdV ? 10.0 * log10(refValueC / (double)ssdV) : 99.99);
+    }
+
+    //Compute PSNR(picN,pic(N+1))
+    return psnrWeight = (psnrY * 6 + psnrU + psnrV) / 8;
+}
+
+void Encoder::copyPicture(x265_picture *dest, const x265_picture *src)
+{
+    dest->poc = src->poc;
+    dest->pts = src->pts;
+    dest->userSEI = src->userSEI;
+    dest->bitDepth = src->bitDepth;
+    dest->framesize = src->framesize;
+    dest->height = src->height;
+    dest->width = src->width;
+    dest->colorSpace = src->colorSpace;
+    dest->userSEI = src->userSEI;
+    dest->rpu.payload = src->rpu.payload;
+    dest->picStruct = src->picStruct;
+    dest->stride[0] = src->stride[0];
+    dest->stride[1] = src->stride[1];
+    dest->stride[2] = src->stride[2];
+    memcpy(dest->planes[0], src->planes[0], src->framesize * sizeof(char));
+    dest->planes[1] = (char*)dest->planes[0] + src->stride[0] *
src->height;
+    dest->planes[2] = (char*)dest->planes[1] + src->stride[1] *
(src->height >> x265_cli_csps[src->colorSpace].height[1]);
+}
+
 /**
  * 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
@@ -1004,6 +1195,10 @@
     if (m_aborted)
         return -1;

+    const x265_picture* inputPic = NULL;
+    static int written = 0, read = 0;
+    bool dontRead = false;
+
     if (m_exportedPic)
     {
         if (!m_param->bUseAnalysisFile && m_param->analysisSave)
@@ -1012,25 +1207,86 @@
         m_exportedPic = NULL;
         m_dpb->recycleUnreferenced();
     }
-    if (pic_in && (!m_param->chunkEnd || (m_encodedFrameNum <
m_param->chunkEnd)))
-    {
-        if (m_latestParam->forceFlush == 1)
+    if ((pic_in && (!m_param->chunkEnd || (m_encodedFrameNum <
m_param->chunkEnd))) || (m_param->bEnableFrameDuplication && !pic_in &&
(read < written)))
+    {
+        if ((m_param->bEnableFrameDuplication && !pic_in && (read <
written)))
+            dontRead = true;
+        else
         {
-            m_lookahead->setLookaheadQueue();
-            m_latestParam->forceFlush = 0;
+            if (m_latestParam->forceFlush == 1)
+            {
+                m_lookahead->setLookaheadQueue();
+                m_latestParam->forceFlush = 0;
+            }
+            if (m_latestParam->forceFlush == 2)
+            {
+                m_lookahead->m_filled = false;
+                m_latestParam->forceFlush = 0;
+            }
+
+            if (pic_in->bitDepth < 8 || pic_in->bitDepth > 16)
+            {
+                x265_log(m_param, X265_LOG_ERROR, "Input bit depth (%d)
must be between 8 and 16\n",
+                    pic_in->bitDepth);
+                return -1;
+            }
         }
-        if (m_latestParam->forceFlush == 2)
+
+        if (m_param->bEnableFrameDuplication)
         {
-            m_lookahead->m_filled = false;
-            m_latestParam->forceFlush = 0;
+#define doubling 7
+#define tripling 8
+            double psnrWeight = 0;
+
+            if (!dontRead)
+            {
+                if (!m_dupBuffer[0]->bOccupied)
+                {
+                    copyPicture(m_dupBuffer[0]->dupPic, pic_in);
+                    m_dupBuffer[0]->bOccupied = 1;
+                    written++;
+                    return 0;
+                }
+                else if (!m_dupBuffer[1]->bOccupied)
+                {
+                    copyPicture(m_dupBuffer[1]->dupPic, pic_in);
+                    m_dupBuffer[1]->bOccupied = 1;
+                    written++;
+                }
+
+                psnrWeight = ComputePSNR(m_dupBuffer[0]->dupPic,
m_dupBuffer[1]->dupPic, m_param);
+
+                if (psnrWeight >= m_param->dupThreshold)
+                {
+                    if (m_dupBuffer[0]->bDup)
+                    {
+                        m_dupBuffer[0]->dupPic->picStruct = tripling;
+                        m_dupBuffer[0]->bDup = 0;
+                        read++;
+                    }
+                    else
+                    {
+                        m_dupBuffer[0]->dupPic->picStruct = doubling;
+                        m_dupBuffer[0]->bDup = 1;
+                        m_dupBuffer[1]->bOccupied = 0;
+                        read++;
+                        return 0;
+                    }
+                }
+                else if (m_dupBuffer[0]->bDup)
+                    m_dupBuffer[0]->bDup = 0;
+                else
+                    m_dupBuffer[0]->dupPic->picStruct = 0;
+            }
+
+            if (read < written)
+            {
+                inputPic = m_dupBuffer[0]->dupPic;
+                read++;
+            }
         }
-
-        if (pic_in->bitDepth < 8 || pic_in->bitDepth > 16)
-        {
-            x265_log(m_param, X265_LOG_ERROR, "Input bit depth (%d) must
be between 8 and 16\n",
-                     pic_in->bitDepth);
-            return -1;
-        }
+        else
+            inputPic = pic_in;

         Frame *inFrame;
         x265_param* p = (m_reconfigure || m_reconfigureRc) ? m_latestParam
: m_param;
@@ -1038,7 +1294,7 @@
         {
             inFrame = new Frame;
             inFrame->m_encodeStartTime = x265_mdate();
-            if (inFrame->create(p, pic_in->quantOffsets))
+            if (inFrame->create(p, inputPic->quantOffsets))
             {
                 /* the first PicYuv created is asked to generate the CU
and block unit offset
                  * arrays which are then shared with all subsequent PicYuv
(orig and recon)
@@ -1098,34 +1354,35 @@
         }

         /* Copy input picture into a Frame and PicYuv, send to lookahead */
-        inFrame->m_fencPic->copyFromPicture(*pic_in, *m_param,
m_sps.conformanceWindow.rightOffset, m_sps.conformanceWindow.bottomOffset);
+        inFrame->m_fencPic->copyFromPicture(*inputPic, *m_param,
m_sps.conformanceWindow.rightOffset, m_sps.conformanceWindow.bottomOffset);

         inFrame->m_poc       = ++m_pocLast;
-        inFrame->m_userData  = pic_in->userData;
-        inFrame->m_pts       = pic_in->pts;
-        inFrame->m_forceqp   = pic_in->forceqp;
+        inFrame->m_userData  = inputPic->userData;
+        inFrame->m_pts       = inputPic->pts;
+        inFrame->m_forceqp   = inputPic->forceqp;
         inFrame->m_param     = (m_reconfigure || m_reconfigureRc) ?
m_latestParam : m_param;
+        inFrame->m_picStruct = inputPic->picStruct;
         if (m_param->bField && m_param->interlaceMode)
-            inFrame->m_fieldNum = pic_in->fieldNum;
-
-        copyUserSEIMessages(inFrame, pic_in);
-
-        /*Copy Dolby Vision RPU from pic_in to frame*/
-        if (pic_in->rpu.payloadSize)
+            inFrame->m_fieldNum = inputPic->fieldNum;
+
+        copyUserSEIMessages(inFrame, inputPic);
+
+        /*Copy Dolby Vision RPU from inputPic to frame*/
+        if (inputPic->rpu.payloadSize)
         {
-            inFrame->m_rpu.payloadSize = pic_in->rpu.payloadSize;
-            inFrame->m_rpu.payload = new uint8_t[pic_in->rpu.payloadSize];
-            memcpy(inFrame->m_rpu.payload, pic_in->rpu.payload,
pic_in->rpu.payloadSize);
+            inFrame->m_rpu.payloadSize = inputPic->rpu.payloadSize;
+            inFrame->m_rpu.payload = new
uint8_t[inputPic->rpu.payloadSize];
+            memcpy(inFrame->m_rpu.payload, inputPic->rpu.payload,
inputPic->rpu.payloadSize);
         }

-        if (pic_in->quantOffsets != NULL)
+        if (inputPic->quantOffsets != NULL)
         {
             int cuCount;
             if (m_param->rc.qgSize == 8)
                 cuCount = inFrame->m_lowres.maxBlocksInRowFullRes *
inFrame->m_lowres.maxBlocksInColFullRes;
             else
                 cuCount = inFrame->m_lowres.maxBlocksInRow *
inFrame->m_lowres.maxBlocksInCol;
-            memcpy(inFrame->m_quantOffsets, pic_in->quantOffsets, cuCount
* sizeof(float));
+            memcpy(inFrame->m_quantOffsets, inputPic->quantOffsets,
cuCount * sizeof(float));
         }

         if (m_pocLast == 0)
@@ -1147,9 +1404,9 @@
         }

         /* Use the frame types from the first pass, if available */
-        int sliceType = (m_param->rc.bStatRead) ?
m_rateControl->rateControlSliceType(inFrame->m_poc) : pic_in->sliceType;
-
-        /* In analysisSave mode, x265_analysis_data is allocated in pic_in
and inFrame points to this */
+        int sliceType = (m_param->rc.bStatRead) ?
m_rateControl->rateControlSliceType(inFrame->m_poc) : inputPic->sliceType;
+
+        /* In analysisSave mode, x265_analysis_data is allocated in
inputPic and inFrame points to this */
         /* Load analysis data before lookahead->addPicture, since
sliceType has been decided */
         if (m_param->analysisLoad)
         {
@@ -1157,7 +1414,7 @@
             static int paramBytes = 0;
             if (!inFrame->m_poc && m_param->bAnalysisType != HEVC_INFO)
             {
-                x265_analysis_data analysisData = pic_in->analysisData;
+                x265_analysis_data analysisData = inputPic->analysisData;
                 paramBytes = validateAnalysisData(&analysisData, 0);
                 if (paramBytes == -1)
                 {
@@ -1178,10 +1435,10 @@
                 uint32_t outOfBoundaryLowresH = extendedHeight -
m_param->sourceHeight / 2;
                 if (outOfBoundaryLowresH * 2 >= m_param->maxCUSize)
                     cuLocInFrame.skipHeight = true;
-                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc,
pic_in, paramBytes, cuLocInFrame);
+                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc,
inputPic, paramBytes, cuLocInFrame);
             }
             else
-                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc,
pic_in, paramBytes);
+                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc,
inputPic, paramBytes);
             inFrame->m_poc = inFrame->m_analysisData.poc;
             sliceType = inFrame->m_analysisData.sliceType;
             inFrame->m_lowres.bScenecut =
!!inFrame->m_analysisData.bScenecut;
@@ -1202,9 +1459,9 @@
                 }
             }
         }
-        if (m_param->bUseRcStats && pic_in->rcData)
+        if (m_param->bUseRcStats && inputPic->rcData)
         {
-            RcStats* rc = (RcStats*)pic_in->rcData;
+            RcStats* rc = (RcStats*)inputPic->rcData;
             m_rateControl->m_accumPQp = rc->cumulativePQp;
             m_rateControl->m_accumPNorm = rc->cumulativePNorm;
             m_rateControl->m_isNextGop = true;
@@ -1228,6 +1485,18 @@
             }
             m_param->bUseRcStats = 0;
         }
+
+        if (m_param->bEnableFrameDuplication && ((read < written) ||
(m_dupBuffer[0]->dupPic->picStruct == tripling && (read <= written))))
+        {
+            if (m_dupBuffer[0]->dupPic->picStruct == tripling)
+                m_dupBuffer[0]->bOccupied = m_dupBuffer[1]->bOccupied = 0;
+            else
+            {
+                copyPicture(m_dupBuffer[0]->dupPic,
m_dupBuffer[1]->dupPic);
+                m_dupBuffer[1]->bOccupied = 0;
+            }
+        }
+
         if (m_reconfigureRc)
             inFrame->m_reconfigureRc = true;

@@ -1262,7 +1531,7 @@
             Slice *slice = outFrame->m_encData->m_slice;
             x265_frame_stats* frameData = NULL;

-            /* Free up pic_in->analysisData since it has already been used
*/
+            /* Free up inputPic->analysisData since it has already been
used */
             if ((m_param->analysisLoad && !m_param->analysisSave) ||
((m_param->bAnalysisType == AVC_INFO) && slice->m_sliceType != I_SLICE))
                 x265_free_analysis_data(m_param,
&outFrame->m_analysisData);

@@ -2615,7 +2884,7 @@
     vui.defaultDisplayWindow.bottomOffset =
m_param->vui.defDispWinBottomOffset;
     vui.defaultDisplayWindow.leftOffset =
m_param->vui.defDispWinLeftOffset;

-    vui.frameFieldInfoPresentFlag = !!m_param->interlaceMode ||
(m_param->pictureStructure >= 0);
+    vui.frameFieldInfoPresentFlag = !!m_param->interlaceMode ||
(m_param->pictureStructure >= 0) || m_param->bEnableFrameDuplication;
     vui.fieldSeqFlag = !!m_param->interlaceMode;

     vui.hrdParametersPresentFlag = m_param->bEmitHRDSEI;
@@ -3174,6 +3443,30 @@
         p->dynamicRd = 0;
         x265_log(p, X265_LOG_WARNING, "Dynamic-rd disabled, requires RD <=
4, VBV and aq-mode enabled\n");
     }
+
+    if (!p->bEnableFrameDuplication && p->dupThreshold && p->dupThreshold
!= 70)
+    {
+        x265_log(p, X265_LOG_WARNING, "Frame-duplication threshold works
only with frame-duplication enabled. Enabling frame-duplication.\n");
+        p->bEnableFrameDuplication = 1;
+    }
+
+    if (p->bEnableFrameDuplication && p->interlaceMode)
+    {
+        x265_log(p, X265_LOG_WARNING, "Frame-duplication does not support
interlace mode. Disabling Frame Duplication.\n");
+        p->bEnableFrameDuplication = 0;
+    }
+
+    if (p->bEnableFrameDuplication && p->pictureStructure != -1)
+    {
+        x265_log(p, X265_LOG_WARNING, "Frame-duplication does not work
with pic_struct. Disabling pic_struct.\n");
+        p->pictureStructure = -1;
+    }
+
+    if (m_param->bEnableFrameDuplication && (!bIsVbv ||
!m_param->bEmitHRDSEI))
+    {
+        x265_log(m_param, X265_LOG_WARNING, "Frame-duplication require NAL
HRD and VBV parameters. Disabling frame duplication\n");
+        m_param->bEnableFrameDuplication = 0;
+    }
 #ifdef ENABLE_HDR10_PLUS
     if (m_param->bDhdr10opt && m_param->toneMapFile == NULL)
     {
diff -r c4b098f973e6 -r e6e6ffeb158f source/encoder/encoder.h
--- a/source/encoder/encoder.h Tue Aug 13 10:51:21 2019 +0530
+++ b/source/encoder/encoder.h Fri Sep 13 15:57:26 2019 +0530
@@ -88,6 +88,7 @@
 };

 #define MAX_NUM_REF_IDX 64
+#define DUP_BUFFER 2

 struct RefIdxLastGOP
 {
@@ -141,6 +142,18 @@
     }
 };

+struct AdaptiveFrameDuplication
+{
+    x265_picture* dupPic;
+    char* dupPlane;
+
+    //Flag to denote the availability of the picture buffer.
+    int bOccupied;
+
+    //Flag to check whether the picture has duplicated.
+    int bDup;
+};
+

 class FrameEncoder;
 class DPB;
@@ -189,6 +202,7 @@
     x265_param*        m_latestParam;     // Holds latest param during a
reconfigure
     RateControl*       m_rateControl;
     Lookahead*         m_lookahead;
+    AdaptiveFrameDuplication* m_dupBuffer[DUP_BUFFER];      // picture
buffer of size 2

     bool               m_externalFlush;
     /* Collect statistics globally */
@@ -324,6 +338,12 @@

     void calcRefreshInterval(Frame* frameEnc);

+    uint64_t computeSSD(pixel *fenc, pixel *rec, intptr_t stride, uint32_t
width, uint32_t height, x265_param *param);
+
+    double ComputePSNR(x265_picture *firstPic, x265_picture *secPic,
x265_param *param);
+
+    void copyPicture(x265_picture *dest, const x265_picture *src);
+
     void initRefIdx();
     void analyseRefIdx(int *numRefIdx);
     void updateRefIdx();
diff -r c4b098f973e6 -r e6e6ffeb158f source/encoder/frameencoder.cpp
--- a/source/encoder/frameencoder.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/encoder/frameencoder.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -713,6 +713,8 @@
                         sei->m_picStruct = (poc & 1) ? 2 /* bottom */ : 1
/* top */;
                 }
             }
+            else if (m_param->bEnableFrameDuplication)
+                sei->m_picStruct = m_frame->m_picStruct;
             else
                 sei->m_picStruct = m_param->pictureStructure;

diff -r c4b098f973e6 -r e6e6ffeb158f source/encoder/framefilter.cpp
--- a/source/encoder/framefilter.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/encoder/framefilter.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -32,7 +32,6 @@

 using namespace X265_NS;

-static uint64_t computeSSD(pixel *fenc, pixel *rec, intptr_t stride,
uint32_t width, uint32_t height);
 static float calculateSSIM(pixel *pix1, intptr_t stride1, pixel *pix2,
intptr_t stride2, uint32_t width, uint32_t height, void *buf, uint32_t&
cnt);

 namespace X265_NS
@@ -673,7 +672,7 @@
         uint32_t width  = reconPic->m_picWidth - m_pad[0];
         uint32_t height = m_parallelFilter[row].getCUHeight();

-        uint64_t ssdY = computeSSD(fencPic->getLumaAddr(cuAddr),
reconPic->getLumaAddr(cuAddr), stride, width, height);
+        uint64_t ssdY =
m_frameEncoder->m_top->computeSSD(fencPic->getLumaAddr(cuAddr),
reconPic->getLumaAddr(cuAddr), stride, width, height, m_param);
         m_frameEncoder->m_SSDY += ssdY;

         if (m_param->internalCsp != X265_CSP_I400)
@@ -682,8 +681,8 @@
             width >>= m_hChromaShift;
             stride = reconPic->m_strideC;

-            uint64_t ssdU = computeSSD(fencPic->getCbAddr(cuAddr),
reconPic->getCbAddr(cuAddr), stride, width, height);
-            uint64_t ssdV = computeSSD(fencPic->getCrAddr(cuAddr),
reconPic->getCrAddr(cuAddr), stride, width, height);
+            uint64_t ssdU =
m_frameEncoder->m_top->computeSSD(fencPic->getCbAddr(cuAddr),
reconPic->getCbAddr(cuAddr), stride, width, height, m_param);
+            uint64_t ssdV =
m_frameEncoder->m_top->computeSSD(fencPic->getCrAddr(cuAddr),
reconPic->getCrAddr(cuAddr), stride, width, height, m_param);

             m_frameEncoder->m_SSDU += ssdU;
             m_frameEncoder->m_SSDV += ssdV;
@@ -825,71 +824,6 @@
     }
 }

-static uint64_t computeSSD(pixel *fenc, pixel *rec, intptr_t stride,
uint32_t width, uint32_t height)
-{
-    uint64_t ssd = 0;
-
-    if ((width | height) & 3)
-    {
-        /* Slow Path */
-        for (uint32_t y = 0; y < height; y++)
-        {
-            for (uint32_t x = 0; x < width; x++)
-            {
-                int diff = (int)(fenc[x] - rec[x]);
-                ssd += diff * diff;
-            }
-
-            fenc += stride;
-            rec += stride;
-        }
-
-        return ssd;
-    }
-
-    uint32_t y = 0;
-
-    /* Consume rows in ever narrower chunks of height */
-    for (int size = BLOCK_64x64; size >= BLOCK_4x4 && y < height; size--)
-    {
-        uint32_t rowHeight = 1 << (size + 2);
-
-        for (; y + rowHeight <= height; y += rowHeight)
-        {
-            uint32_t y1, x = 0;
-
-            /* Consume each row using the largest square blocks possible */
-            if (size == BLOCK_64x64 && !(stride & 31))
-                for (; x + 64 <= width; x += 64)
-                    ssd += primitives.cu[BLOCK_64x64].sse_pp(fenc + x,
stride, rec + x, stride);
-
-            if (size >= BLOCK_32x32 && !(stride & 15))
-                for (; x + 32 <= width; x += 32)
-                    for (y1 = 0; y1 + 32 <= rowHeight; y1 += 32)
-                        ssd += primitives.cu[BLOCK_32x32].sse_pp(fenc + y1
* stride + x, stride, rec + y1 * stride + x, stride);
-
-            if (size >= BLOCK_16x16)
-                for (; x + 16 <= width; x += 16)
-                    for (y1 = 0; y1 + 16 <= rowHeight; y1 += 16)
-                        ssd += primitives.cu[BLOCK_16x16].sse_pp(fenc + y1
* stride + x, stride, rec + y1 * stride + x, stride);
-
-            if (size >= BLOCK_8x8)
-                for (; x + 8 <= width; x += 8)
-                    for (y1 = 0; y1 + 8 <= rowHeight; y1 += 8)
-                        ssd += primitives.cu[BLOCK_8x8].sse_pp(fenc + y1 *
stride + x, stride, rec + y1 * stride + x, stride);
-
-            for (; x + 4 <= width; x += 4)
-                for (y1 = 0; y1 + 4 <= rowHeight; y1 += 4)
-                    ssd += primitives.cu[BLOCK_4x4].sse_pp(fenc + y1 *
stride + x, stride, rec + y1 * stride + x, stride);
-
-            fenc += stride * rowHeight;
-            rec += stride * rowHeight;
-        }
-    }
-
-    return ssd;
-}
-
 /* Function to calculate SSIM for each row */
 static float calculateSSIM(pixel *pix1, intptr_t stride1, pixel *pix2,
intptr_t stride2, uint32_t width, uint32_t height, void *buf, uint32_t& cnt)
 {
diff -r c4b098f973e6 -r e6e6ffeb158f source/input/y4m.cpp
--- a/source/input/y4m.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/input/y4m.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -388,6 +388,7 @@
         pic.bitDepth = depth;
         pic.framesize = framesize;
         pic.height = height;
+        pic.width = width;
         pic.colorSpace = colorSpace;
         pic.stride[0] = width * pixelbytes;
         pic.stride[1] = pic.stride[0] >>
x265_cli_csps[colorSpace].width[1];
diff -r c4b098f973e6 -r e6e6ffeb158f source/input/yuv.cpp
--- a/source/input/yuv.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/input/yuv.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -204,6 +204,7 @@
         pic.bitDepth = depth;
         pic.framesize = framesize;
         pic.height = height;
+        pic.width = width;
         pic.stride[0] = width * pixelbytes;
         pic.stride[1] = pic.stride[0] >>
x265_cli_csps[colorSpace].width[1];
         pic.stride[2] = pic.stride[0] >>
x265_cli_csps[colorSpace].width[2];
diff -r c4b098f973e6 -r e6e6ffeb158f source/test/regression-tests.txt
--- a/source/test/regression-tests.txt Tue Aug 13 10:51:21 2019 +0530
+++ b/source/test/regression-tests.txt Fri Sep 13 15:57:26 2019 +0530
@@ -156,6 +156,7 @@
 720p50_parkrun_ter.y4m,--preset medium --bitrate 400 --hme
 ducks_take_off_420_1_720p50.y4m,--preset medium --aq-mode 4 --crf 22
--no-cutree
 ducks_take_off_420_1_720p50.y4m,--preset medium --selective-sao 4 --sao
--crf 20
+Traffic_4096x2048_30p.y4m, --preset medium --frame-dup --dup-threshold 60
--hrd --bitrate 10000 --vbv-bufsize 15000 --vbv-maxrate 12000

 # Main12 intraCost overflow bug test
 720p50_parkrun_ter.y4m,--preset medium
diff -r c4b098f973e6 -r e6e6ffeb158f source/x265.cpp
--- a/source/x265.cpp Tue Aug 13 10:51:21 2019 +0530
+++ b/source/x265.cpp Fri Sep 13 15:57:26 2019 +0530
@@ -541,10 +541,11 @@
         return true;
     }

-    /* Unconditionally accept height/width/csp from file info */
+    /* Unconditionally accept height/width/csp/bitDepth from file info */
     param->sourceWidth = info.width;
     param->sourceHeight = info.height;
     param->internalCsp = info.csp;
+    param->sourceBitDepth = info.depth;

     /* Accept fps and sar from file info if not specified by user */
     if (param->fpsDenom == 0 || param->fpsNum == 0)
diff -r c4b098f973e6 -r e6e6ffeb158f source/x265.h
--- a/source/x265.h Tue Aug 13 10:51:21 2019 +0530
+++ b/source/x265.h Fri Sep 13 15:57:26 2019 +0530
@@ -455,16 +455,21 @@
      * multi pass ratecontrol mode. */
     void*  rcData;

-    uint64_t framesize;
+    size_t framesize;

     int    height;

+    int    width;
+
     // pts is reordered in the order of encoding.
     int64_t reorderedPts;

     //Dolby Vision RPU metadata
     x265_dolby_vision_rpu rpu;
-
+
+    //SEI picture structure message
+    uint32_t picStruct;
+
     int fieldNum;
 } x265_picture;

@@ -844,6 +849,9 @@
      * Future builds may support 12bit pixels. */
     int       internalBitDepth;

+    /*Input sequence bit depth. It can be either 8bit, 10bit or 12bit.*/
+    int       sourceBitDepth;
+
     /* Color space of internal pictures, must match color space of input
      * pictures */
     int       internalCsp;
@@ -1327,6 +1335,19 @@
  * */
  int       pictureStructure;

+    /*
+    * Signals picture structure SEI timing message for every frame
+    * picture structure 7 is signalled for frame doubling
+    * picture structure 8 is signalled for frame tripling
+    * */
+    int       bEnableFrameDuplication;
+
+    /*
+    * For adaptive frame duplication, a threshold is set above which the
frames are similar.
+    * User can set a variable threshold. Default 70.
+    * */
+    int       dupThreshold;
+
     struct
     {
         /* Explicit mode of rate-control, necessary for API users. It must
diff -r c4b098f973e6 -r e6e6ffeb158f source/x265cli.h
--- a/source/x265cli.h Tue Aug 13 10:51:21 2019 +0530
+++ b/source/x265cli.h Fri Sep 13 15:57:26 2019 +0530
@@ -321,6 +321,9 @@
     { "hevc-aq", no_argument, NULL, 0 },
     { "no-hevc-aq", no_argument, NULL, 0 },
     { "qp-adaptation-range", required_argument, NULL, 0 },
+    { "frame-dup",            no_argument, NULL, 0 },
+    { "no-frame-dup", no_argument, NULL, 0 },
+    { "dup-threshold", required_argument, NULL, 0 },
 #ifdef SVT_HEVC
     { "svt",     no_argument, NULL, 0 },
     { "no-svt",  no_argument, NULL, 0 },
@@ -638,6 +641,8 @@
     H1("   --recon-depth <integer>       Bit-depth of reconstructed raw
image file. Defaults to input bit depth, or 8 if Y4M\n");
     H1("   --recon-y4m-exec <string>     pipe reconstructed frames to Y4M
viewer, ex:\"ffplay -i pipe:0 -autoexit\"\n");
     H0("   --lowpass-dct                 Use low-pass subband dct
approximation. Default %s\n", OPT(param->bLowPassDct));
+    H0("   --[no-]frame-dup              Enable Frame duplication. Default
%s\n", OPT(param->bEnableFrameDuplication));
+    H0("   --dup-threshold <integer>     PSNR threshold for Frame
duplication. Default %d\n", param->dupThreshold);
 #ifdef SVT_HEVC
     H0("   --[no]svt                     Enable SVT HEVC encoder %s\n",
OPT(param->bEnableSvtHevc));
     H0("   --[no-]svt-hme                Enable Hierarchial motion
estimation(HME) in SVT HEVC encoder \n");

Thanks & Regards
*Akil R*
Video Codec Engineer
Media & AI Analytics
<https://multicorewareinc.com/>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20190926/444597f4/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: AFD_final.patch
Type: application/octet-stream
Size: 38116 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20190926/444597f4/attachment-0001.obj>


More information about the x265-devel mailing list