[x265] [x265 patch] Adaptive Frame Duplication

Akil akil at multicorewareinc.com
Fri Sep 27 08:41:54 CEST 2019


Pls ignore this patch.

Thanks & Regards
*Akil R*
Video Codec Engineer
Media & AI Analytics
<https://multicorewareinc.com/>


On Mon, Sep 23, 2019 at 10:20 AM Akil <akil at multicorewareinc.com> wrote:

> # HG changeset patch
> # User Akil Ayyappan<akil at multicorewareinc.com>
> # Date 1568370446 -19800
> #      Fri Sep 13 15:57:26 2019 +0530
> # Node ID 531f6b03eed0a40a38d3589dec03f14743293146
> # 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 531f6b03eed0 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 531f6b03eed0 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 531f6b03eed0 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 531f6b03eed0 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 531f6b03eed0 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 = 0;
> +
>      /* 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 < 0 || 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 531f6b03eed0 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 531f6b03eed0 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,11 @@
>      m_cR = 1.0;
>      for (int i = 0; i < X265_MAX_FRAME_THREADS; i++)
>          m_frameEncoder[i] = NULL;
> +    for (int i = 0; i < X265_DUP_BUFFER; i++)
> +    {
> +        m_picList[i] = NULL;
> +        m_plane[i] = NULL;
> +    }
>      MotionEstimate::initScales();
>
>  #if ENABLE_HDR10_PLUS
> @@ -160,6 +165,33 @@
>      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)
> +    {
> +#define DUP_THRESHOLD 70
> +        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]));
> +        }
> +
> +        if (!m_param->dupThreshold)
> +            m_param->dupThreshold = DUP_THRESHOLD;
> +
> +        for (int i = 0; i < X265_DUP_BUFFER; i++)
> +        {
> +            m_picList[i] = x265_picture_alloc();
> +            x265_picture_init(p, m_picList[i]);
> +            m_plane[i] = X265_MALLOC(char, framesize);
> +            m_picList[i]->planes[0] = m_plane[i];
> +            m_picList[i]->lock = 0;
> +            m_picList[i]->dup = 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 +803,15 @@
>          m_exportedPic = NULL;
>      }
>
> +    if (m_param->bEnableFrameDuplication)
> +    {
> +        for (int i = 0; i < X265_DUP_BUFFER; i++)
> +        {
> +            x265_picture_free(m_picList[i]);
> +            X265_FREE(m_plane[i]);
> +        }
> +    }
> +
>      for (int i = 0; i < m_param->frameNumThreads; i++)
>      {
>          if (m_frameEncoder[i])
> @@ -981,6 +1022,136 @@
>      }
>  }
>
> +//Find Sum of Squared Diference (SSD) between two pictures
> +uint64_t Encoder::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;
> +}
> +
> +//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 hshift = CHROMA_H_SHIFT(firstPic->colorSpace);
> +    int vshift = CHROMA_V_SHIFT(firstPic->colorSpace);
> +
> +    strideL = widthL = firstPic->width;
> +    heightL = firstPic->height;
> +
> +    strideC = widthC = widthL >> hshift;
> +    heightC = heightL >> vshift;
> +
> +    int size = widthL * heightL;
> +    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);
> +    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);
> +        ssdV = computeSSD(vFirstPic, vSecPic, strideC, widthC, heightC);
> +        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 +1175,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 +1187,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_picList[0]->lock)
> +                {
> +                    copyPicture(m_picList[0], pic_in);
> +                    m_picList[0]->lock = 1;
> +                    written++;
> +                    return 0;
> +                }
> +                else if (!m_picList[1]->lock)
> +                {
> +                    copyPicture(m_picList[1], pic_in);
> +                    m_picList[1]->lock = 1;
> +                    written++;
> +                }
> +
> +                psnrWeight = ComputePSNR(m_picList[0], m_picList[1],
> m_param);
> +
> +                if (psnrWeight >= m_param->dupThreshold)
> +                {
> +                    if (m_picList[0]->dup)
> +                    {
> +                        m_picList[0]->picStruct = tripling;
> +                        m_picList[0]->dup = 0;
> +                        read++;
> +                    }
> +                    else
> +                    {
> +                        m_picList[0]->picStruct = doubling;
> +                        m_picList[0]->dup = 1;
> +                        m_picList[1]->lock = 0;
> +                        read++;
> +                        return 0;
> +                    }
> +                }
> +                else if (m_picList[0]->dup)
> +                    m_picList[0]->dup = 0;
> +                else
> +                    m_picList[0]->picStruct = 0;
> +            }
> +
> +            if (read < written)
> +            {
> +                inputPic = m_picList[0];
> +                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 +1274,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 +1334,38 @@
>          }
>
>          /* 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;
> +        if (m_param->bEnableFrameDuplication)
> +            inFrame->m_pts   = inFrame->m_poc;
> +        else
> +            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 +1387,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 +1397,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 +1418,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 +1442,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 +1468,21 @@
>              }
>              m_param->bUseRcStats = 0;
>          }
> +
> +        if (m_param->bEnableFrameDuplication && ((read < written) ||
> (m_picList[0]->picStruct == tripling && (read <= written))))
> +        {
> +            if (m_picList[0]->picStruct == tripling)
> +            {
> +                m_picList[0]->lock = 0;
> +                m_picList[1]->lock = 0;
> +            }
> +            else
> +            {
> +                copyPicture(m_picList[0], m_picList[1]);
> +                m_picList[1]->lock = 0;
> +            }
> +        }
> +
>          if (m_reconfigureRc)
>              inFrame->m_reconfigureRc = true;
>
> @@ -1262,7 +1517,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 +2870,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 +3429,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)
> +    {
> +        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 interlace mode.\n");
> +        p->interlaceMode = 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 531f6b03eed0 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
> @@ -168,6 +168,7 @@
>      int                m_bframeDelay;
>      int                m_numPools;
>      int                m_curEncoder;
> +    int                m_framesToBeEncoded;
>
>      // weighted prediction
>      int                m_numLumaWPFrames;    // number of P frames with
> weighted luma reference
> @@ -180,6 +181,9 @@
>
>      ThreadPool*        m_threadPool;
>      FrameEncoder*      m_frameEncoder[X265_MAX_FRAME_THREADS];
> +    x265_picture*      m_picList[X265_DUP_BUFFER];
> +    char*              m_plane[X265_DUP_BUFFER];
> +
>      DPB*               m_dpb;
>      Frame*             m_exportedPic;
>      FILE*              m_analysisFileIn;
> @@ -324,6 +328,12 @@
>
>      void calcRefreshInterval(Frame* frameEnc);
>
> +    uint64_t computeSSD(pixel *fenc, pixel *rec, intptr_t stride,
> uint32_t width, uint32_t height);
> +
> +    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 531f6b03eed0 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 531f6b03eed0 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_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);
> +            uint64_t ssdV =
> m_frameEncoder->m_top->computeSSD(fencPic->getCrAddr(cuAddr),
> reconPic->getCrAddr(cuAddr), stride, width, height);
>
>              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 531f6b03eed0 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 531f6b03eed0 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 531f6b03eed0 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 70
> --hrd --bitrate 10000 --vbv-bufsize 15000 --vbv-maxrate 12000
>
>  # Main12 intraCost overflow bug test
>  720p50_parkrun_ter.y4m,--preset medium
> diff -r c4b098f973e6 -r 531f6b03eed0 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 531f6b03eed0 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,27 @@
>       * 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
> +    int picStruct;
>
> +    //Flag used to lock the buffer
> +    int lock;
> +
> +    //Flag to check whether the frame has duplicated
> +    int dup;
> +
>      int fieldNum;
>  } x265_picture;
>
> @@ -545,6 +556,7 @@
>
>  #define X265_BFRAME_MAX         16
>  #define X265_MAX_FRAME_THREADS  16
> +#define X265_DUP_BUFFER         2
>
>  #define X265_TYPE_AUTO          0x0000  /* Let x265 choose the right type
> */
>  #define X265_TYPE_IDR           0x0001
> @@ -844,6 +856,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 +1342,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 frame duplication, a threshold is set above which the frames
> are said to be 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 531f6b03eed0 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
> +91 978 791 9223
> <https://multicorewareinc.com/>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20190927/1095a41d/attachment-0001.html>


More information about the x265-devel mailing list