<div dir="ltr"><div># HG changeset patch<br># User Akil Ayyappan<<a href="mailto:akil@multicorewareinc.com">akil@multicorewareinc.com</a>><br># Date 1568370446 -19800<br>#      Fri Sep 13 15:57:26 2019 +0530<br># Node ID 531f6b03eed0a40a38d3589dec03f14743293146<br># Parent  c4b098f973e6b0ee4aee3bf0d7b54da4e2734d42<br>Adaptive Frame duplication<br><br>This patch does the following.<br>1. Replaces 2-3 near-identical frames with one frame and sets pic_struct based on frame doubling / tripling.<br>2. Add option "--frame-dup" and "--dup-threshold' to enable frame duplication and to set threshold for frame similarity (optional).<br><br>diff -r c4b098f973e6 -r 531f6b03eed0 doc/reST/cli.rst<br>--- a/doc/reST/cli.rst    Tue Aug 13 10:51:21 2019 +0530<br>+++ b/doc/reST/cli.rst  Fri Sep 13 15:57:26 2019 +0530<br>@@ -501,6 +501,16 @@<br>         second. The decoder must re-combine the fields in their correct<br>      orientation for display.<br> <br>+.. option:: --frame-dup, --no-frame-dup<br>+<br>+    Enable Frame duplication. Replaces 2-3 near-identical frames with one <br>+       frame and sets pic_struct based on frame doubling / tripling. <br>+       Default disabled.<br>+<br>+.. option:: --dup-threshold <integer><br>+<br>+        Frame similarity threshold can vary between 1 and 99. Default 70.<br>+<br> .. option:: --seek <integer><br> <br>       Number of frames to skip at start of input file. Default 0<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/CMakeLists.txt<br>--- a/source/CMakeLists.txt     Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/CMakeLists.txt     Fri Sep 13 15:57:26 2019 +0530<br>@@ -29,7 +29,7 @@<br> option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)<br> mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)<br> # X265_BUILD must be incremented each time the public API is changed<br>-set(X265_BUILD 179)<br>+set(X265_BUILD 180)<br> configure_file("${PROJECT_SOURCE_DIR}/<a href="http://x265.def.in">x265.def.in</a>"<br>                "${PROJECT_BINARY_DIR}/x265.def")<br> configure_file("${PROJECT_SOURCE_DIR}/<a href="http://x265_config.h.in">x265_config.h.in</a>"<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/common/frame.cpp<br>--- a/source/common/frame.cpp        Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/common/frame.cpp   Fri Sep 13 15:57:26 2019 +0530<br>@@ -57,6 +57,7 @@<br>     m_addOnPrevChange = NULL;<br>     m_classifyFrame = false;<br>     m_fieldNum = 0;<br>+    m_picStruct = 0;<br> }<br> <br> bool Frame::create(x265_param *param, float* quantOffsets)<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/common/frame.h<br>--- a/source/common/frame.h        Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/common/frame.h     Fri Sep 13 15:57:26 2019 +0530<br>@@ -98,6 +98,7 @@<br> <br>     float*                 m_quantOffsets;       // points to quantOffsets in x265_picture<br>     x265_sei               m_userSEI;<br>+    uint32_t               m_picStruct;          // picture structure SEI message<br>     x265_dolby_vision_rpu            m_rpu;<br> <br>     /* Frame Parallelism - notification between FrameEncoders of available motion reference rows */<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/common/param.cpp<br>--- a/source/common/param.cpp   Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/common/param.cpp   Fri Sep 13 15:57:26 2019 +0530<br>@@ -135,6 +135,7 @@<br> <br>     /* Source specifications */<br>     param->internalBitDepth = X265_DEPTH;<br>+    param->sourceBitDepth = 8;<br>     param->internalCsp = X265_CSP_I420;<br>     param->levelIdc = 0; //Auto-detect level<br>     param->uhdBluray = 0;<br>@@ -338,6 +339,9 @@<br>     param->pictureStructure = -1;<br>     param->bEmitCLL = 1;<br> <br>+    param->bEnableFrameDuplication = 0;<br>+    param->dupThreshold = 0;<br>+<br>     /* SVT Hevc Encoder specific params */<br>     param->bEnableSvtHevc = 0;<br>     param->svtHevcParam = NULL;<br>@@ -1294,6 +1298,8 @@<br>         OPT("fades") p->bEnableFades = atobool(value);<br>         OPT("field") p->bField = atobool( value );<br>         OPT("cll") p->bEmitCLL = atobool(value);<br>+        OPT("frame-dup") p->bEnableFrameDuplication = atobool(value);<br>+        OPT("dup-threshold") p->dupThreshold = atoi(value);<br>         OPT("hme") p->bEnableHME = atobool(value);<br>         OPT("hme-search")<br>         {<br>@@ -1680,6 +1686,8 @@<br>         "Supported factor for controlling max AU size is from 0.5 to 1");<br>     CHECK((param->dolbyProfile != 0) && (param->dolbyProfile != 50) && (param->dolbyProfile != 81) && (param->dolbyProfile != 82),<br>         "Unsupported Dolby Vision profile, only profile 5, profile 8.1 and profile 8.2 enabled");<br>+    CHECK(param->dupThreshold < 0 || 99 < param->dupThreshold,<br>+        "Invalid frame-duplication threshold. Value must be between 1 and 99.");<br>     if (param->dolbyProfile)<br>     {<br>         CHECK((param->rc.vbvMaxBitrate <= 0 || param->rc.vbvBufferSize <= 0), "Dolby Vision requires VBV settings to enable HRD.\n");<br>@@ -1972,6 +1980,9 @@<br>     s += sprintf(s, " subme=%d", p->subpelRefine);<br>     s += sprintf(s, " merange=%d", p->searchRange);<br>     BOOL(p->bEnableTemporalMvp, "temporal-mvp");<br>+    BOOL(p->bEnableFrameDuplication, "frame-dup");<br>+    if(p->bEnableFrameDuplication)<br>+        s += sprintf(s, " dup-threshold=%d", p->dupThreshold);<br>     BOOL(p->bEnableHME, "hme");<br>     if (p->bEnableHME)<br>         s += sprintf(s, " Level 0,1,2=%d,%d,%d", p->hmeSearchMethod[0], p->hmeSearchMethod[1], p->hmeSearchMethod[2]);<br>@@ -2209,6 +2220,7 @@<br>     if (src->csvfn) dst->csvfn = strdup(src->csvfn);<br>     else dst->csvfn = NULL;<br>     dst->internalBitDepth = src->internalBitDepth;<br>+    dst->sourceBitDepth = src->sourceBitDepth;<br>     dst->internalCsp = src->internalCsp;<br>     dst->fpsNum = src->fpsNum;<br>     dst->fpsDenom = src->fpsDenom;<br>@@ -2263,6 +2275,8 @@<br>     dst->subpelRefine = src->subpelRefine;<br>     dst->searchRange = src->searchRange;<br>     dst->bEnableTemporalMvp = src->bEnableTemporalMvp;<br>+    dst->bEnableFrameDuplication = src->bEnableFrameDuplication;<br>+    dst->dupThreshold = src->dupThreshold;<br>     dst->bEnableHME = src->bEnableHME;<br>     if (src->bEnableHME)<br>     {<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/encoder/api.cpp<br>--- a/source/encoder/api.cpp   Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/encoder/api.cpp    Fri Sep 13 15:57:26 2019 +0530<br>@@ -923,6 +923,7 @@<br>     pic->userSEI.numPayloads = 0;<br>     pic->rpu.payloadSize = 0;<br>     pic->rpu.payload = NULL;<br>+    pic->picStruct = 0;<br> <br>     if ((param->analysisSave || param->analysisLoad) || (param->bAnalysisType == AVC_INFO))<br>     {<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/encoder/encoder.cpp<br>--- a/source/encoder/encoder.cpp     Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/encoder/encoder.cpp        Fri Sep 13 15:57:26 2019 +0530<br>@@ -117,6 +117,11 @@<br>     m_cR = 1.0;<br>     for (int i = 0; i < X265_MAX_FRAME_THREADS; i++)<br>         m_frameEncoder[i] = NULL;<br>+    for (int i = 0; i < X265_DUP_BUFFER; i++)<br>+    {<br>+        m_picList[i] = NULL;<br>+        m_plane[i] = NULL;<br>+    }<br>     MotionEstimate::initScales();<br> <br> #if ENABLE_HDR10_PLUS<br>@@ -160,6 +165,33 @@<br>     int rows = (p->sourceHeight + p->maxCUSize - 1) >> g_log2Size[p->maxCUSize];<br>     int cols = (p->sourceWidth  + p->maxCUSize - 1) >> g_log2Size[p->maxCUSize];<br> <br>+<br>+<br>+    if (m_param->bEnableFrameDuplication)<br>+    {<br>+#define DUP_THRESHOLD 70<br>+        size_t framesize = 0;<br>+        int pixelbytes = p->sourceBitDepth > 8 ? 2 : 1;<br>+        for (int i = 0; i < x265_cli_csps[p->internalCsp].planes; i++)<br>+        {<br>+            int stride = (p->sourceWidth >> x265_cli_csps[p->internalCsp].width[i]) * pixelbytes;<br>+            framesize += (stride * (p->sourceHeight >> x265_cli_csps[p->internalCsp].height[i]));<br>+        }<br>+<br>+        if (!m_param->dupThreshold)<br>+            m_param->dupThreshold = DUP_THRESHOLD;<br>+<br>+        for (int i = 0; i < X265_DUP_BUFFER; i++)<br>+        {<br>+            m_picList[i] = x265_picture_alloc();<br>+            x265_picture_init(p, m_picList[i]);<br>+            m_plane[i] = X265_MALLOC(char, framesize);<br>+            m_picList[i]->planes[0] = m_plane[i];<br>+            m_picList[i]->lock = 0;<br>+            m_picList[i]->dup = 0;<br>+        }<br>+    }<br>+<br>     // Do not allow WPP if only one row or fewer than 3 columns, it is pointless and unstable<br>     if (rows == 1 || cols < 3)<br>     {<br>@@ -771,6 +803,15 @@<br>         m_exportedPic = NULL;<br>     }<br> <br>+    if (m_param->bEnableFrameDuplication)<br>+    {<br>+        for (int i = 0; i < X265_DUP_BUFFER; i++)<br>+        {<br>+            x265_picture_free(m_picList[i]);<br>+            X265_FREE(m_plane[i]);<br>+        }<br>+    }<br>+<br>     for (int i = 0; i < m_param->frameNumThreads; i++)<br>     {<br>         if (m_frameEncoder[i])<br>@@ -981,6 +1022,136 @@<br>     }<br> }<br> <br>+//Find Sum of Squared Diference (SSD) between two pictures<br>+uint64_t Encoder::computeSSD(pixel *fenc, pixel *rec, intptr_t stride, uint32_t width, uint32_t height)<br>+{<br>+    uint64_t ssd = 0;<br>+<br>+    if ((width | height) & 3)<br>+    {<br>+        /* Slow Path */<br>+        for (uint32_t y = 0; y < height; y++)<br>+        {<br>+            for (uint32_t x = 0; x < width; x++)<br>+            {<br>+                int diff = (int)(fenc[x] - rec[x]);<br>+                ssd += diff * diff;<br>+            }<br>+<br>+            fenc += stride;<br>+            rec += stride;<br>+        }<br>+<br>+        return ssd;<br>+    }<br>+<br>+    uint32_t y = 0;<br>+<br>+    /* Consume rows in ever narrower chunks of height */<br>+    for (int size = BLOCK_64x64; size >= BLOCK_4x4 && y < height; size--)<br>+    {<br>+        uint32_t rowHeight = 1 << (size + 2);<br>+<br>+        for (; y + rowHeight <= height; y += rowHeight)<br>+        {<br>+            uint32_t y1, x = 0;<br>+<br>+            /* Consume each row using the largest square blocks possible */<br>+            if (size == BLOCK_64x64 && !(stride & 31))<br>+                for (; x + 64 <= width; x += 64)<br>+                    ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_64x64].sse_pp(fenc + x, stride, rec + x, stride);<br>+<br>+            if (size >= BLOCK_32x32 && !(stride & 15))<br>+                for (; x + 32 <= width; x += 32)<br>+                    for (y1 = 0; y1 + 32 <= rowHeight; y1 += 32)<br>+                        ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_32x32].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>+<br>+            if (size >= BLOCK_16x16)<br>+                for (; x + 16 <= width; x += 16)<br>+                    for (y1 = 0; y1 + 16 <= rowHeight; y1 += 16)<br>+                        ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_16x16].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>+<br>+            if (size >= BLOCK_8x8)<br>+                for (; x + 8 <= width; x += 8)<br>+                    for (y1 = 0; y1 + 8 <= rowHeight; y1 += 8)<br>+                        ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_8x8].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>+<br>+            for (; x + 4 <= width; x += 4)<br>+                for (y1 = 0; y1 + 4 <= rowHeight; y1 += 4)<br>+                    ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_4x4].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>+<br>+            fenc += stride * rowHeight;<br>+            rec += stride * rowHeight;<br>+        }<br>+    }<br>+<br>+    return ssd;<br>+}<br>+<br>+//Compute the PSNR weightage between two pictures<br>+double Encoder::ComputePSNR(x265_picture *firstPic, x265_picture *secPic, x265_param *param)<br>+{<br>+    uint64_t ssdY = 0, ssdU = 0, ssdV = 0;<br>+    intptr_t strideL, strideC;<br>+    uint32_t widthL, heightL, widthC, heightC;<br>+    double psnrY = 0, psnrU = 0, psnrV = 0, psnrWeight = 0;<br>+    int hshift = CHROMA_H_SHIFT(firstPic->colorSpace);<br>+    int vshift = CHROMA_V_SHIFT(firstPic->colorSpace);<br>+<br>+    strideL = widthL = firstPic->width;<br>+    heightL = firstPic->height;<br>+<br>+    strideC = widthC = widthL >> hshift;<br>+    heightC = heightL >> vshift;<br>+<br>+    int size = widthL * heightL;<br>+    int maxvalY = 255 << (X265_DEPTH - 8);<br>+    int maxvalC = 255 << (X265_DEPTH - 8);<br>+    double refValueY = (double)maxvalY * maxvalY * size;<br>+    double refValueC = (double)maxvalC * maxvalC * size / 4.0;<br>+<br>+    pixel *yFirstPic = (pixel*)firstPic->planes[0];<br>+    pixel *ySecPic = (pixel*)secPic->planes[0];<br>+    ssdY = computeSSD(yFirstPic, ySecPic, strideL, widthL, heightL);<br>+    psnrY = (ssdY ? 10.0 * log10(refValueY / (double)ssdY) : 99.99);<br>+<br>+    if (param->internalCsp != X265_CSP_I400)<br>+    {<br>+        pixel *uFirstPic = (pixel*)firstPic->planes[1];<br>+        pixel *vFirstPic = (pixel*)firstPic->planes[2];<br>+        pixel *uSecPic = (pixel*)secPic->planes[1];<br>+        pixel *vSecPic = (pixel*)secPic->planes[2];<br>+        ssdU = computeSSD(uFirstPic, uSecPic, strideC, widthC, heightC);<br>+        ssdV = computeSSD(vFirstPic, vSecPic, strideC, widthC, heightC);<br>+        psnrU = (ssdU ? 10.0 * log10(refValueC / (double)ssdU) : 99.99);<br>+        psnrV = (ssdV ? 10.0 * log10(refValueC / (double)ssdV) : 99.99);<br>+    }<br>+<br>+    //Compute PSNR(picN,pic(N+1))<br>+    return psnrWeight = (psnrY * 6 + psnrU + psnrV) / 8;<br>+}<br>+<br>+void Encoder::copyPicture(x265_picture *dest, const x265_picture *src)<br>+{<br>+    dest->poc = src->poc;<br>+    dest->pts = src->pts;<br>+    dest->userSEI = src->userSEI;<br>+    dest->bitDepth = src->bitDepth;<br>+    dest->framesize = src->framesize;<br>+    dest->height = src->height;<br>+    dest->width = src->width;<br>+    dest->colorSpace = src->colorSpace;<br>+    dest->userSEI = src->userSEI;<br>+    dest->rpu.payload = src->rpu.payload;<br>+    dest->picStruct = src->picStruct;<br>+    dest->stride[0] = src->stride[0];<br>+    dest->stride[1] = src->stride[1];<br>+    dest->stride[2] = src->stride[2];<br>+    memcpy(dest->planes[0], src->planes[0], src->framesize * sizeof(char));<br>+    dest->planes[1] = (char*)dest->planes[0] + src->stride[0] * src->height;<br>+    dest->planes[2] = (char*)dest->planes[1] + src->stride[1] * (src->height >> x265_cli_csps[src->colorSpace].height[1]);<br>+}<br>+<br> /**<br>  * Feed one new input frame into the encoder, get one frame out. If pic_in is<br>  * NULL, a flush condition is implied and pic_in must be NULL for all subsequent<br>@@ -1004,6 +1175,10 @@<br>     if (m_aborted)<br>         return -1;<br> <br>+    const x265_picture* inputPic = NULL;<br>+    static int written = 0, read = 0;<br>+    bool dontRead = false;<br>+<br>     if (m_exportedPic)<br>     {<br>         if (!m_param->bUseAnalysisFile && m_param->analysisSave)<br>@@ -1012,25 +1187,86 @@<br>         m_exportedPic = NULL;<br>         m_dpb->recycleUnreferenced();<br>     }<br>-    if (pic_in && (!m_param->chunkEnd || (m_encodedFrameNum < m_param->chunkEnd)))<br>-    {<br>-        if (m_latestParam->forceFlush == 1)<br>+    if ((pic_in && (!m_param->chunkEnd || (m_encodedFrameNum < m_param->chunkEnd))) || (m_param->bEnableFrameDuplication && !pic_in && (read < written)))<br>+    {<br>+        if ((m_param->bEnableFrameDuplication && !pic_in && (read < written)))<br>+            dontRead = true;<br>+        else<br>         {<br>-            m_lookahead->setLookaheadQueue();<br>-            m_latestParam->forceFlush = 0;<br>+            if (m_latestParam->forceFlush == 1)<br>+            {<br>+                m_lookahead->setLookaheadQueue();<br>+                m_latestParam->forceFlush = 0;<br>+            }<br>+            if (m_latestParam->forceFlush == 2)<br>+            {<br>+                m_lookahead->m_filled = false;<br>+                m_latestParam->forceFlush = 0;<br>+            }<br>+<br>+            if (pic_in->bitDepth < 8 || pic_in->bitDepth > 16)<br>+            {<br>+                x265_log(m_param, X265_LOG_ERROR, "Input bit depth (%d) must be between 8 and 16\n",<br>+                    pic_in->bitDepth);<br>+                return -1;<br>+            }<br>         }<br>-        if (m_latestParam->forceFlush == 2)<br>+<br>+        if (m_param->bEnableFrameDuplication)<br>         {<br>-            m_lookahead->m_filled = false;<br>-            m_latestParam->forceFlush = 0;<br>+#define doubling 7<br>+#define tripling 8<br>+            double psnrWeight = 0;<br>+<br>+            if (!dontRead)<br>+            {<br>+                if (!m_picList[0]->lock)<br>+                {<br>+                    copyPicture(m_picList[0], pic_in);<br>+                    m_picList[0]->lock = 1;<br>+                    written++;<br>+                    return 0;<br>+                }<br>+                else if (!m_picList[1]->lock)<br>+                {<br>+                    copyPicture(m_picList[1], pic_in);<br>+                    m_picList[1]->lock = 1;<br>+                    written++;<br>+                }<br>+<br>+                psnrWeight = ComputePSNR(m_picList[0], m_picList[1], m_param);<br>+<br>+                if (psnrWeight >= m_param->dupThreshold)<br>+                {<br>+                    if (m_picList[0]->dup)<br>+                    {<br>+                        m_picList[0]->picStruct = tripling;<br>+                        m_picList[0]->dup = 0;<br>+                        read++;<br>+                    }<br>+                    else<br>+                    {<br>+                        m_picList[0]->picStruct = doubling;<br>+                        m_picList[0]->dup = 1;<br>+                        m_picList[1]->lock = 0;<br>+                        read++;<br>+                        return 0;<br>+                    }<br>+                }<br>+                else if (m_picList[0]->dup)<br>+                    m_picList[0]->dup = 0;<br>+                else<br>+                    m_picList[0]->picStruct = 0;<br>+            }<br>+<br>+            if (read < written)<br>+            {<br>+                inputPic = m_picList[0];<br>+                read++;<br>+            }<br>         }<br>-<br>-        if (pic_in->bitDepth < 8 || pic_in->bitDepth > 16)<br>-        {<br>-            x265_log(m_param, X265_LOG_ERROR, "Input bit depth (%d) must be between 8 and 16\n",<br>-                     pic_in->bitDepth);<br>-            return -1;<br>-        }<br>+        else<br>+            inputPic = pic_in;<br> <br>         Frame *inFrame;<br>         x265_param* p = (m_reconfigure || m_reconfigureRc) ? m_latestParam : m_param;<br>@@ -1038,7 +1274,7 @@<br>         {<br>             inFrame = new Frame;<br>             inFrame->m_encodeStartTime = x265_mdate();<br>-            if (inFrame->create(p, pic_in->quantOffsets))<br>+            if (inFrame->create(p, inputPic->quantOffsets))<br>             {<br>                 /* the first PicYuv created is asked to generate the CU and block unit offset<br>                  * arrays which are then shared with all subsequent PicYuv (orig and recon) <br>@@ -1098,34 +1334,38 @@<br>         }<br> <br>         /* Copy input picture into a Frame and PicYuv, send to lookahead */<br>-        inFrame->m_fencPic->copyFromPicture(*pic_in, *m_param, m_sps.conformanceWindow.rightOffset, m_sps.conformanceWindow.bottomOffset);<br>+        inFrame->m_fencPic->copyFromPicture(*inputPic, *m_param, m_sps.conformanceWindow.rightOffset, m_sps.conformanceWindow.bottomOffset);<br> <br>         inFrame->m_poc       = ++m_pocLast;<br>-        inFrame->m_userData  = pic_in->userData;<br>-        inFrame->m_pts       = pic_in->pts;<br>-        inFrame->m_forceqp   = pic_in->forceqp;<br>+        inFrame->m_userData  = inputPic->userData;<br>+        if (m_param->bEnableFrameDuplication)<br>+            inFrame->m_pts   = inFrame->m_poc;<br>+        else<br>+            inFrame->m_pts   = inputPic->pts;<br>+        inFrame->m_forceqp   = inputPic->forceqp;<br>         inFrame->m_param     = (m_reconfigure || m_reconfigureRc) ? m_latestParam : m_param;<br>+        inFrame->m_picStruct = inputPic->picStruct;<br>         if (m_param->bField && m_param->interlaceMode)<br>-            inFrame->m_fieldNum = pic_in->fieldNum;<br>-<br>-        copyUserSEIMessages(inFrame, pic_in);<br>-<br>-        /*Copy Dolby Vision RPU from pic_in to frame*/<br>-        if (pic_in->rpu.payloadSize)<br>+            inFrame->m_fieldNum = inputPic->fieldNum;<br>+<br>+        copyUserSEIMessages(inFrame, inputPic);<br>+<br>+        /*Copy Dolby Vision RPU from inputPic to frame*/<br>+        if (inputPic->rpu.payloadSize)<br>         {<br>-            inFrame->m_rpu.payloadSize = pic_in->rpu.payloadSize;<br>-            inFrame->m_rpu.payload = new uint8_t[pic_in->rpu.payloadSize];<br>-            memcpy(inFrame->m_rpu.payload, pic_in->rpu.payload, pic_in->rpu.payloadSize);<br>+            inFrame->m_rpu.payloadSize = inputPic->rpu.payloadSize;<br>+            inFrame->m_rpu.payload = new uint8_t[inputPic->rpu.payloadSize];<br>+            memcpy(inFrame->m_rpu.payload, inputPic->rpu.payload, inputPic->rpu.payloadSize);<br>         }<br> <br>-        if (pic_in->quantOffsets != NULL)<br>+        if (inputPic->quantOffsets != NULL)<br>         {<br>             int cuCount;<br>             if (m_param->rc.qgSize == 8)<br>                 cuCount = inFrame->m_lowres.maxBlocksInRowFullRes * inFrame->m_lowres.maxBlocksInColFullRes;<br>             else<br>                 cuCount = inFrame->m_lowres.maxBlocksInRow * inFrame->m_lowres.maxBlocksInCol;<br>-            memcpy(inFrame->m_quantOffsets, pic_in->quantOffsets, cuCount * sizeof(float));<br>+            memcpy(inFrame->m_quantOffsets, inputPic->quantOffsets, cuCount * sizeof(float));<br>         }<br> <br>         if (m_pocLast == 0)<br>@@ -1147,9 +1387,9 @@<br>         }<br> <br>         /* Use the frame types from the first pass, if available */<br>-        int sliceType = (m_param->rc.bStatRead) ? m_rateControl->rateControlSliceType(inFrame->m_poc) : pic_in->sliceType;<br>-<br>-        /* In analysisSave mode, x265_analysis_data is allocated in pic_in and inFrame points to this */<br>+        int sliceType = (m_param->rc.bStatRead) ? m_rateControl->rateControlSliceType(inFrame->m_poc) : inputPic->sliceType;<br>+<br>+        /* In analysisSave mode, x265_analysis_data is allocated in inputPic and inFrame points to this */<br>         /* Load analysis data before lookahead->addPicture, since sliceType has been decided */<br>         if (m_param->analysisLoad)<br>         {<br>@@ -1157,7 +1397,7 @@<br>             static int paramBytes = 0;<br>             if (!inFrame->m_poc && m_param->bAnalysisType != HEVC_INFO)<br>             {<br>-                x265_analysis_data analysisData = pic_in->analysisData;<br>+                x265_analysis_data analysisData = inputPic->analysisData;<br>                 paramBytes = validateAnalysisData(&analysisData, 0);<br>                 if (paramBytes == -1)<br>                 {<br>@@ -1178,10 +1418,10 @@<br>                 uint32_t outOfBoundaryLowresH = extendedHeight - m_param->sourceHeight / 2;<br>                 if (outOfBoundaryLowresH * 2 >= m_param->maxCUSize)<br>                     cuLocInFrame.skipHeight = true;<br>-                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc, pic_in, paramBytes, cuLocInFrame);<br>+                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc, inputPic, paramBytes, cuLocInFrame);<br>             }<br>             else<br>-                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc, pic_in, paramBytes);<br>+                readAnalysisFile(&inFrame->m_analysisData, inFrame->m_poc, inputPic, paramBytes);<br>             inFrame->m_poc = inFrame->m_analysisData.poc;<br>             sliceType = inFrame->m_analysisData.sliceType;<br>             inFrame->m_lowres.bScenecut = !!inFrame->m_analysisData.bScenecut;<br>@@ -1202,9 +1442,9 @@<br>                 }<br>             }<br>         }<br>-        if (m_param->bUseRcStats && pic_in->rcData)<br>+        if (m_param->bUseRcStats && inputPic->rcData)<br>         {<br>-            RcStats* rc = (RcStats*)pic_in->rcData;<br>+            RcStats* rc = (RcStats*)inputPic->rcData;<br>             m_rateControl->m_accumPQp = rc->cumulativePQp;<br>             m_rateControl->m_accumPNorm = rc->cumulativePNorm;<br>             m_rateControl->m_isNextGop = true;<br>@@ -1228,6 +1468,21 @@<br>             }<br>             m_param->bUseRcStats = 0;<br>         }<br>+<br>+        if (m_param->bEnableFrameDuplication && ((read < written) || (m_picList[0]->picStruct == tripling && (read <= written))))<br>+        {<br>+            if (m_picList[0]->picStruct == tripling)<br>+            {<br>+                m_picList[0]->lock = 0;<br>+                m_picList[1]->lock = 0;<br>+            }<br>+            else<br>+            {<br>+                copyPicture(m_picList[0], m_picList[1]);<br>+                m_picList[1]->lock = 0;<br>+            }<br>+        }<br>+<br>         if (m_reconfigureRc)<br>             inFrame->m_reconfigureRc = true;<br> <br>@@ -1262,7 +1517,7 @@<br>             Slice *slice = outFrame->m_encData->m_slice;<br>             x265_frame_stats* frameData = NULL;<br> <br>-            /* Free up pic_in->analysisData since it has already been used */<br>+            /* Free up inputPic->analysisData since it has already been used */<br>             if ((m_param->analysisLoad && !m_param->analysisSave) || ((m_param->bAnalysisType == AVC_INFO) && slice->m_sliceType != I_SLICE))<br>                 x265_free_analysis_data(m_param, &outFrame->m_analysisData);<br> <br>@@ -2615,7 +2870,7 @@<br>     vui.defaultDisplayWindow.bottomOffset = m_param->vui.defDispWinBottomOffset;<br>     vui.defaultDisplayWindow.leftOffset = m_param->vui.defDispWinLeftOffset;<br> <br>-    vui.frameFieldInfoPresentFlag = !!m_param->interlaceMode || (m_param->pictureStructure >= 0);<br>+    vui.frameFieldInfoPresentFlag = !!m_param->interlaceMode || (m_param->pictureStructure >= 0) || m_param->bEnableFrameDuplication;<br>     vui.fieldSeqFlag = !!m_param->interlaceMode;<br> <br>     vui.hrdParametersPresentFlag = m_param->bEmitHRDSEI;<br>@@ -3174,6 +3429,30 @@<br>         p->dynamicRd = 0;<br>         x265_log(p, X265_LOG_WARNING, "Dynamic-rd disabled, requires RD <= 4, VBV and aq-mode enabled\n");<br>     }<br>+<br>+    if (!p->bEnableFrameDuplication && p->dupThreshold)<br>+    {<br>+        x265_log(p, X265_LOG_WARNING, "Frame-duplication threshold works only with frame-duplication enabled. Enabling frame-duplication.\n");<br>+        p->bEnableFrameDuplication = 1;<br>+    }<br>+<br>+    if (p->bEnableFrameDuplication && p->interlaceMode)<br>+    {<br>+        x265_log(p, X265_LOG_WARNING, "Frame-duplication does not support interlace mode. Disabling interlace mode.\n");<br>+        p->interlaceMode = 0;<br>+    }<br>+<br>+    if (p->bEnableFrameDuplication && p->pictureStructure != -1)<br>+    {<br>+        x265_log(p, X265_LOG_WARNING, "Frame-duplication does not work with pic_struct. Disabling pic_struct.\n");<br>+        p->pictureStructure = -1;<br>+    }<br>+<br>+    if (m_param->bEnableFrameDuplication && (!bIsVbv || !m_param->bEmitHRDSEI))<br>+    {<br>+        x265_log(m_param, X265_LOG_WARNING, "Frame-duplication require NAL HRD and VBV parameters. Disabling frame duplication\n");<br>+        m_param->bEnableFrameDuplication = 0;<br>+    }<br> #ifdef ENABLE_HDR10_PLUS<br>     if (m_param->bDhdr10opt && m_param->toneMapFile == NULL)<br>     {<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/encoder/encoder.h<br>--- a/source/encoder/encoder.h     Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/encoder/encoder.h  Fri Sep 13 15:57:26 2019 +0530<br>@@ -168,6 +168,7 @@<br>     int                m_bframeDelay;<br>     int                m_numPools;<br>     int                m_curEncoder;<br>+    int                m_framesToBeEncoded;<br> <br>     // weighted prediction<br>     int                m_numLumaWPFrames;    // number of P frames with weighted luma reference<br>@@ -180,6 +181,9 @@<br> <br>     ThreadPool*        m_threadPool;<br>     FrameEncoder*      m_frameEncoder[X265_MAX_FRAME_THREADS];<br>+    x265_picture*      m_picList[X265_DUP_BUFFER];<br>+    char*              m_plane[X265_DUP_BUFFER];<br>+<br>     DPB*               m_dpb;<br>     Frame*             m_exportedPic;<br>     FILE*              m_analysisFileIn;<br>@@ -324,6 +328,12 @@<br> <br>     void calcRefreshInterval(Frame* frameEnc);<br> <br>+    uint64_t computeSSD(pixel *fenc, pixel *rec, intptr_t stride, uint32_t width, uint32_t height);<br>+<br>+    double ComputePSNR(x265_picture *firstPic, x265_picture *secPic, x265_param *param);<br>+<br>+    void copyPicture(x265_picture *dest, const x265_picture *src);<br>+<br>     void initRefIdx();<br>     void analyseRefIdx(int *numRefIdx);<br>     void updateRefIdx();<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/encoder/frameencoder.cpp<br>--- a/source/encoder/frameencoder.cpp   Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/encoder/frameencoder.cpp   Fri Sep 13 15:57:26 2019 +0530<br>@@ -713,6 +713,8 @@<br>                         sei->m_picStruct = (poc & 1) ? 2 /* bottom */ : 1 /* top */;<br>                 }<br>             }<br>+            else if (m_param->bEnableFrameDuplication)<br>+                sei->m_picStruct = m_frame->m_picStruct;<br>             else<br>                 sei->m_picStruct = m_param->pictureStructure;<br> <br>diff -r c4b098f973e6 -r 531f6b03eed0 source/encoder/framefilter.cpp<br>--- a/source/encoder/framefilter.cpp   Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/encoder/framefilter.cpp    Fri Sep 13 15:57:26 2019 +0530<br>@@ -32,7 +32,6 @@<br> <br> using namespace X265_NS;<br> <br>-static uint64_t computeSSD(pixel *fenc, pixel *rec, intptr_t stride, uint32_t width, uint32_t height);<br> static float calculateSSIM(pixel *pix1, intptr_t stride1, pixel *pix2, intptr_t stride2, uint32_t width, uint32_t height, void *buf, uint32_t& cnt);<br> <br> namespace X265_NS<br>@@ -673,7 +672,7 @@<br>         uint32_t width  = reconPic->m_picWidth - m_pad[0];<br>         uint32_t height = m_parallelFilter[row].getCUHeight();<br> <br>-        uint64_t ssdY = computeSSD(fencPic->getLumaAddr(cuAddr), reconPic->getLumaAddr(cuAddr), stride, width, height);<br>+        uint64_t ssdY = m_frameEncoder->m_top->computeSSD(fencPic->getLumaAddr(cuAddr), reconPic->getLumaAddr(cuAddr), stride, width, height);<br>         m_frameEncoder->m_SSDY += ssdY;<br> <br>         if (m_param->internalCsp != X265_CSP_I400)<br>@@ -682,8 +681,8 @@<br>             width >>= m_hChromaShift;<br>             stride = reconPic->m_strideC;<br> <br>-            uint64_t ssdU = computeSSD(fencPic->getCbAddr(cuAddr), reconPic->getCbAddr(cuAddr), stride, width, height);<br>-            uint64_t ssdV = computeSSD(fencPic->getCrAddr(cuAddr), reconPic->getCrAddr(cuAddr), stride, width, height);<br>+            uint64_t ssdU = m_frameEncoder->m_top->computeSSD(fencPic->getCbAddr(cuAddr), reconPic->getCbAddr(cuAddr), stride, width, height);<br>+            uint64_t ssdV = m_frameEncoder->m_top->computeSSD(fencPic->getCrAddr(cuAddr), reconPic->getCrAddr(cuAddr), stride, width, height);<br> <br>             m_frameEncoder->m_SSDU += ssdU;<br>             m_frameEncoder->m_SSDV += ssdV;<br>@@ -825,71 +824,6 @@<br>     }<br> }<br> <br>-static uint64_t computeSSD(pixel *fenc, pixel *rec, intptr_t stride, uint32_t width, uint32_t height)<br>-{<br>-    uint64_t ssd = 0;<br>-<br>-    if ((width | height) & 3)<br>-    {<br>-        /* Slow Path */<br>-        for (uint32_t y = 0; y < height; y++)<br>-        {<br>-            for (uint32_t x = 0; x < width; x++)<br>-            {<br>-                int diff = (int)(fenc[x] - rec[x]);<br>-                ssd += diff * diff;<br>-            }<br>-<br>-            fenc += stride;<br>-            rec += stride;<br>-        }<br>-<br>-        return ssd;<br>-    }<br>-<br>-    uint32_t y = 0;<br>-<br>-    /* Consume rows in ever narrower chunks of height */<br>-    for (int size = BLOCK_64x64; size >= BLOCK_4x4 && y < height; size--)<br>-    {<br>-        uint32_t rowHeight = 1 << (size + 2);<br>-<br>-        for (; y + rowHeight <= height; y += rowHeight)<br>-        {<br>-            uint32_t y1, x = 0;<br>-<br>-            /* Consume each row using the largest square blocks possible */<br>-            if (size == BLOCK_64x64 && !(stride & 31))<br>-                for (; x + 64 <= width; x += 64)<br>-                    ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_64x64].sse_pp(fenc + x, stride, rec + x, stride);<br>-<br>-            if (size >= BLOCK_32x32 && !(stride & 15))<br>-                for (; x + 32 <= width; x += 32)<br>-                    for (y1 = 0; y1 + 32 <= rowHeight; y1 += 32)<br>-                        ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_32x32].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>-<br>-            if (size >= BLOCK_16x16)<br>-                for (; x + 16 <= width; x += 16)<br>-                    for (y1 = 0; y1 + 16 <= rowHeight; y1 += 16)<br>-                        ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_16x16].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>-<br>-            if (size >= BLOCK_8x8)<br>-                for (; x + 8 <= width; x += 8)<br>-                    for (y1 = 0; y1 + 8 <= rowHeight; y1 += 8)<br>-                        ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_8x8].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>-<br>-            for (; x + 4 <= width; x += 4)<br>-                for (y1 = 0; y1 + 4 <= rowHeight; y1 += 4)<br>-                    ssd += <a href="http://primitives.cu">primitives.cu</a>[BLOCK_4x4].sse_pp(fenc + y1 * stride + x, stride, rec + y1 * stride + x, stride);<br>-<br>-            fenc += stride * rowHeight;<br>-            rec += stride * rowHeight;<br>-        }<br>-    }<br>-<br>-    return ssd;<br>-}<br>-<br> /* Function to calculate SSIM for each row */<br> static float calculateSSIM(pixel *pix1, intptr_t stride1, pixel *pix2, intptr_t stride2, uint32_t width, uint32_t height, void *buf, uint32_t& cnt)<br> {<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/input/y4m.cpp<br>--- a/source/input/y4m.cpp        Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/input/y4m.cpp      Fri Sep 13 15:57:26 2019 +0530<br>@@ -388,6 +388,7 @@<br>         pic.bitDepth = depth;<br>         pic.framesize = framesize;<br>         pic.height = height;<br>+        pic.width = width;<br>         pic.colorSpace = colorSpace;<br>         pic.stride[0] = width * pixelbytes;<br>         pic.stride[1] = pic.stride[0] >> x265_cli_csps[colorSpace].width[1];<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/input/yuv.cpp<br>--- a/source/input/yuv.cpp Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/input/yuv.cpp      Fri Sep 13 15:57:26 2019 +0530<br>@@ -204,6 +204,7 @@<br>         pic.bitDepth = depth;<br>         pic.framesize = framesize;<br>         pic.height = height;<br>+        pic.width = width;<br>         pic.stride[0] = width * pixelbytes;<br>         pic.stride[1] = pic.stride[0] >> x265_cli_csps[colorSpace].width[1];<br>         pic.stride[2] = pic.stride[0] >> x265_cli_csps[colorSpace].width[2];<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/test/regression-tests.txt<br>--- a/source/test/regression-tests.txt   Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/test/regression-tests.txt  Fri Sep 13 15:57:26 2019 +0530<br>@@ -156,6 +156,7 @@<br> 720p50_parkrun_ter.y4m,--preset medium --bitrate 400 --hme<br> ducks_take_off_420_1_720p50.y4m,--preset medium --aq-mode 4 --crf 22 --no-cutree<br> ducks_take_off_420_1_720p50.y4m,--preset medium --selective-sao 4 --sao --crf 20<br>+Traffic_4096x2048_30p.y4m, --preset medium --frame-dup --dup-threshold 70 --hrd --bitrate 10000 --vbv-bufsize 15000 --vbv-maxrate 12000<br> <br> # Main12 intraCost overflow bug test<br> 720p50_parkrun_ter.y4m,--preset medium<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/x265.cpp<br>--- a/source/x265.cpp  Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/x265.cpp   Fri Sep 13 15:57:26 2019 +0530<br>@@ -541,10 +541,11 @@<br>         return true;<br>     }<br> <br>-    /* Unconditionally accept height/width/csp from file info */<br>+    /* Unconditionally accept height/width/csp/bitDepth from file info */<br>     param->sourceWidth = info.width;<br>     param->sourceHeight = info.height;<br>     param->internalCsp = info.csp;<br>+    param->sourceBitDepth = info.depth;<br> <br>     /* Accept fps and sar from file info if not specified by user */<br>     if (param->fpsDenom == 0 || param->fpsNum == 0)<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/x265.h<br>--- a/source/x265.h Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/x265.h     Fri Sep 13 15:57:26 2019 +0530<br>@@ -455,16 +455,27 @@<br>      * multi pass ratecontrol mode. */<br>     void*  rcData;<br> <br>-    uint64_t framesize;<br>+    size_t framesize;<br> <br>     int    height;<br> <br>+    int    width;<br>+<br>     // pts is reordered in the order of encoding.<br>     int64_t reorderedPts;<br> <br>     //Dolby Vision RPU metadata<br>     x265_dolby_vision_rpu rpu;<br>+<br>+    //SEI picture structure message<br>+    int picStruct;<br>  <br>+    //Flag used to lock the buffer<br>+    int lock;<br>+<br>+    //Flag to check whether the frame has duplicated<br>+    int dup;<br>+<br>     int fieldNum;<br> } x265_picture;<br> <br>@@ -545,6 +556,7 @@<br> <br> #define X265_BFRAME_MAX         16<br> #define X265_MAX_FRAME_THREADS  16<br>+#define X265_DUP_BUFFER         2<br> <br> #define X265_TYPE_AUTO          0x0000  /* Let x265 choose the right type */<br> #define X265_TYPE_IDR           0x0001<br>@@ -844,6 +856,9 @@<br>      * Future builds may support 12bit pixels. */<br>     int       internalBitDepth;<br> <br>+    /*Input sequence bit depth. It can be either 8bit, 10bit or 12bit.*/<br>+    int       sourceBitDepth;<br>+<br>     /* Color space of internal pictures, must match color space of input<br>      * pictures */<br>     int       internalCsp;<br>@@ -1327,6 +1342,19 @@<br>   * */<br>        int       pictureStructure;  <br> <br>+    /*<br>+    * Signals picture structure SEI timing message for every frame<br>+    * picture structure 7 is signalled for frame doubling<br>+    * picture structure 8 is signalled for frame tripling<br>+    * */<br>+    int       bEnableFrameDuplication;<br>+<br>+    /*<br>+    * For frame duplication, a threshold is set above which the frames are said to be similar.<br>+    * User can set a variable threshold. Default 70.<br>+    * */<br>+    int  dupThreshold;<br>+<br>     struct<br>     {<br>         /* Explicit mode of rate-control, necessary for API users. It must<br>diff -r c4b098f973e6 -r 531f6b03eed0 source/x265cli.h<br>--- a/source/x265cli.h Tue Aug 13 10:51:21 2019 +0530<br>+++ b/source/x265cli.h  Fri Sep 13 15:57:26 2019 +0530<br>@@ -321,6 +321,9 @@<br>     { "hevc-aq", no_argument, NULL, 0 },<br>     { "no-hevc-aq", no_argument, NULL, 0 },<br>     { "qp-adaptation-range", required_argument, NULL, 0 },<br>+    { "frame-dup",            no_argument, NULL, 0 },<br>+    { "no-frame-dup", no_argument, NULL, 0 },<br>+    { "dup-threshold", required_argument, NULL, 0 },<br> #ifdef SVT_HEVC<br>     { "svt",     no_argument, NULL, 0 },<br>     { "no-svt",  no_argument, NULL, 0 },<br>@@ -638,6 +641,8 @@<br>     H1("   --recon-depth <integer>       Bit-depth of reconstructed raw image file. Defaults to input bit depth, or 8 if Y4M\n");<br>     H1("   --recon-y4m-exec <string>     pipe reconstructed frames to Y4M viewer, ex:\"ffplay -i pipe:0 -autoexit\"\n");<br>     H0("   --lowpass-dct                 Use low-pass subband dct approximation. Default %s\n", OPT(param->bLowPassDct));<br>+    H0("   --[no-]frame-dup              Enable Frame duplication. Default %s\n", OPT(param->bEnableFrameDuplication));<br>+    H0("   --dup-threshold <integer>     PSNR threshold for Frame duplication. Default %d\n", param->dupThreshold);<br> #ifdef SVT_HEVC<br>     H0("   --[no]svt                     Enable SVT HEVC encoder %s\n", OPT(param->bEnableSvtHevc));<br>     H0("   --[no-]svt-hme                Enable Hierarchial motion estimation(HME) in SVT HEVC encoder \n");<br></div><div><br></div><br clear="all"><div><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><font face="verdana, sans-serif" color="#0c343d">Thanks & Regards</font><div><font face="verdana, sans-serif" color="#0c343d"><b>Akil R</b></font></div><div><font face="verdana, sans-serif" color="#0c343d" size="1">Video Codec Engineer </font></div><div><font face="verdana, sans-serif" color="#0c343d" size="1">Media & AI Analytics</font></div><div><font color="#0c343d" face="trebuchet ms, sans-serif">+91 978 791 9223</font></div><div><a href="https://multicorewareinc.com/" target="_blank"><img src="https://docs.google.com/uc?export=download&id=1kc3RJu9M8bnIf6Xa5rUw2d-eEVUsPBE5&revid=0B7tw9XJBmynaemR1VUpQUi9DVytRVW5SVkRwVTFjb1hBMUcwPQ"></a><br></div></div></div></div></div></div></div></div></div></div>