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 <[email protected]> wrote: > # HG changeset patch > # User Akil Ayyappan<[email protected]> > # 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/> >
_______________________________________________ x265-devel mailing list [email protected] https://mailman.videolan.org/listinfo/x265-devel
