diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst index 82040d1e6..4bb90d012 100755 --- a/doc/reST/cli.rst +++ b/doc/reST/cli.rst @@ -2523,7 +2523,7 @@ VUI fields must be manually specified. .. option:: --nalu-file Text file containing userSEI in POC order : / - Parse the input file specified and inserts SEI messages into the bitstream. + Parse the input file specified and inserts SEI messages into the bitstream. Currently, we support only PREFIX SEI messages. This is an "application-only" feature. .. option:: --atc-sei @@ -2535,8 +2535,19 @@ VUI fields must be manually specified. .. option:: --pic-struct Set the picture structure and emits it in the picture timing SEI message. - Values in the range 0..12. See D.3.3 of the HEVC spec. for a detailed explanation. - Required for HLG (Hybrid Log Gamma) signaling. Not signaled by default. + Values in the range 0..12. See Table D.2 and D.3.3 of the HEVC spec. for a detailed explanation. + +.. option:: --psfile + + Specify a text file which contains the picture structure for some or all frames. + Allows for dynamic pulldown (VFR in CFR containers). The format of each line is + + framenumber framefieldcoding picstruct + + Framefieldcoding shall be 0, 1, 2 (progressive, bottom-first or top-first, resp.), + It shall not change for an encoded sequence and match the encoder configuration. + + Picstruct is the picture structure to use in the framenumber's Picture Timing SEI. .. option:: --video-signal-type-preset diff --git a/source/abrEncApp.cpp b/source/abrEncApp.cpp index d9b2cedec..8c491cdfa 100644 --- a/source/abrEncApp.cpp +++ b/source/abrEncApp.cpp @@ -535,7 +535,7 @@ namespace X265_NS { int analysisWrite = m_parent->m_analysisWriteCnt[analysisQId].get(); int written = analysisWrite * m_parent->m_passEnc[analysisQId]->m_cliopt.numRefs; int analysisRead = m_parent->m_analysisReadCnt[analysisQId].get(); - + while (m_threadActive && written == analysisRead) { analysisWrite = m_parent->m_analysisWriteCnt[analysisQId].waitForChange(analysisWrite); @@ -763,6 +763,17 @@ namespace X265_NS { } } + if (m_cliopt.psfile) + { + if (!m_cliopt.parsePSFile(pic_orig[view], m_param->interlaceMode, m_param->bField)) + { + x265_log(NULL, X265_LOG_ERROR, "can't parse psfile for frame %d in %s\n", + pic_in[view]->poc, profileName); + fclose(m_cliopt.psfile); + m_cliopt.psfile = NULL; + } + } + if (m_cliopt.framesToBeEncoded && inFrameCount >= m_cliopt.framesToBeEncoded) pic_in[view] = NULL; else if (readPicture(pic_in[view], view)){ diff --git a/source/abrEncApp.h b/source/abrEncApp.h index 4c3ededc0..7fbc51e64 100644 --- a/source/abrEncApp.h +++ b/source/abrEncApp.h @@ -3,7 +3,7 @@ * * Authors: Pooja Venkatesan * Aruna Matheswaran -* +* * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -91,6 +91,7 @@ namespace X265_NS { CLIOptions m_cliopt; InputFile* m_input[MAX_VIEWS]; const char* m_reconPlayCmd; + FILE* m_psfile; FILE* m_qpfile; FILE* m_zoneFile; FILE* m_dolbyVisionRpu;/* File containing Dolby Vision BL RPU metadata */ diff --git a/source/common/frame.cpp b/source/common/frame.cpp index ca25e25bd..8a4a01c55 100644 --- a/source/common/frame.cpp +++ b/source/common/frame.cpp @@ -85,6 +85,12 @@ Frame::Frame() m_targetBitrate = 0; m_targetCrf = 0; m_targetQp = 0; + + // HRD management + m_duration = 0; + m_cpbDelay = 0; + m_dpbOutputDelay = 0; + m_cpbDuration = 0; } bool Frame::create(x265_param *param, float* quantOffsets) diff --git a/source/common/frame.h b/source/common/frame.h index 9916ba92e..76a824420 100644 --- a/source/common/frame.h +++ b/source/common/frame.h @@ -182,6 +182,15 @@ class Frame /* target QP for this picture.*/ int m_targetQp; + /*HRD delays for this frame*/ + unsigned int m_duration; // display duration of picture in ticks (VUI timebase) + unsigned int m_cpbDuration; // lifetime in the CPB (VUI timebase) + unsigned int m_cpbDelay; // removal delay of the picture (VUI timebase) + unsigned int m_dpbOutputDelay; // output delay of the picture (VUI timebase) + uint64_t m_displayPicCount; // display picture count (counted in decoder ticks) + uint64_t m_codedPicCount; // coded picture count (counted in decoder ticks) + double m_timebase; // the timebase for this picture [seconds] + Frame(); bool create(x265_param *param, float* quantOffsets); diff --git a/source/common/lowres.cpp b/source/common/lowres.cpp index 233f63665..3903c470b 100644 --- a/source/common/lowres.cpp +++ b/source/common/lowres.cpp @@ -351,6 +351,9 @@ void Lowres::init(PicYuv* origPic, int poc, bool bEnableTemporalFilter) filterThisGOP = false; noiseScore = -1; frameNum = poc; + cpbDurationSecs = 0.; + dispDurationSecs = 0.; + dispPicCount = durationPicCount = 0; leadingBframes = 0; indB = 0; memset(costEst, -1, sizeof(costEst)); @@ -386,7 +389,7 @@ void Lowres::init(PicYuv* origPic, int poc, bool bEnableTemporalFilter) extendPicBorder(lowresPlane[1], lumaStride, width, lines, origPic->m_lumaMarginX, origPic->m_lumaMarginY); extendPicBorder(lowresPlane[2], lumaStride, width, lines, origPic->m_lumaMarginX, origPic->m_lumaMarginY); extendPicBorder(lowresPlane[3], lumaStride, width, lines, origPic->m_lumaMarginX, origPic->m_lumaMarginY); - + if (origPic->m_param->bEnableHME || origPic->m_param->bEnableTemporalFilter) { if (bEnableTemporalFilter) diff --git a/source/common/lowres.h b/source/common/lowres.h index fa64ea4f5..6387756d1 100644 --- a/source/common/lowres.h +++ b/source/common/lowres.h @@ -182,6 +182,12 @@ struct Lowres : public ReferencePlanes double ipCostRatio; + /* Vbv Lookahead & cuTree durations */ + double cpbDurationSecs; + double dispDurationSecs; + int64_t dispPicCount; + int64_t durationPicCount; + /* lookahead output data */ int64_t costEst[X265_BFRAME_MAX + 2][X265_BFRAME_MAX + 2]; int64_t costEstAq[X265_BFRAME_MAX + 2][X265_BFRAME_MAX + 2]; @@ -207,6 +213,7 @@ struct Lowres : public ReferencePlanes /* used for vbvLookahead */ int plannedType[X265_LOOKAHEAD_MAX + 1]; int64_t plannedSatd[X265_LOOKAHEAD_MAX + 1]; + double plannedCpbDuration[X265_LOOKAHEAD_MAX + 1]; /* durations of frames ahead for VBV */ int indB; int bframes; diff --git a/source/encoder/api.cpp b/source/encoder/api.cpp index efcc6bdb6..e5c2d4183 100644 --- a/source/encoder/api.cpp +++ b/source/encoder/api.cpp @@ -1011,7 +1011,7 @@ void x265_picture_init(x265_param *param, x265_picture *pic) pic->userSEI.numPayloads = 0; pic->rpu.payloadSize = 0; pic->rpu.payload = NULL; - pic->picStruct = 0; + pic->picStruct = PIC_STRUCT_AUTO; pic->vbvEndFlag = 0; if ((strlen(param->analysisSave) || strlen(param->analysisLoad)) || (param->bAnalysisType == AVC_INFO)) diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp index 25a776250..801551905 100644 --- a/source/encoder/encoder.cpp +++ b/source/encoder/encoder.cpp @@ -50,6 +50,7 @@ namespace X265_NS { const char g_sliceTypeToChar[] = {'B', 'P', 'I'}; +const uint8_t g_deltaToDivisor[PIC_STRUCT_COUNT] = {1, 1, 1, 2, 2, 3, 3, 2, 3, 1, 1, 1, 1}; /* Dolby Vision profile specific settings */ typedef struct @@ -178,6 +179,8 @@ Encoder::Encoder() m_startPoint = 0; m_saveCTUSize = 0; m_zoneIndex = 0; + + m_dispPicCount = 0; } inline char *strcatFilename(const char *input, const char *suffix) @@ -217,8 +220,8 @@ void Encoder::create() framesize += (stride * (p->sourceHeight >> x265_cli_csps[p->internalCsp].height[i])); } - //Sets the picture structure and emits it in the picture timing SEI message - m_param->pictureStructure = 0; + // Override user setting, let the dup logic decide. + m_param->pictureStructure = -1; for (uint32_t i = 0; i < DUP_BUFFER; i++) { @@ -373,7 +376,7 @@ void Encoder::create() } else lookAheadThreadPool = m_threadPool ? &m_threadPool[m_numTmePools] : NULL; - m_lookahead = new Lookahead(m_param, lookAheadThreadPool); + m_lookahead = new Lookahead(m_param, lookAheadThreadPool, &m_sps); m_lookahead->m_numPools = lookaheadPools; if (lookaheadPools) { @@ -427,7 +430,7 @@ void Encoder::create() initVPS(&m_vps); initSPS(&m_sps); initPPS(&m_pps); - + if (m_param->rc.vbvBufferSize) { m_offsetEmergency = (uint16_t(*)[MAX_NUM_TR_CATEGORIES][MAX_NUM_TR_COEFFS])X265_MALLOC(uint16_t, MAX_NUM_TR_CATEGORIES * MAX_NUM_TR_COEFFS * (QP_MAX_MAX - QP_MAX_SPEC)); @@ -1265,7 +1268,7 @@ uint64_t Encoder::computeSSD(pixel *fenc, pixel *rec, intptr_t stride, uint32_t } } - /* Handle last few rows of frames for videos + /* Handle last few rows of frames for videos with height not divisble by 4 */ uint32_t h = height % y; if (param->bEnableFrameDuplication && h) @@ -1567,13 +1570,13 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out) { if (m_dupBuffer[0]->bDup) { - m_dupBuffer[0]->dupPic->picStruct = tripling; + m_dupBuffer[0]->dupPic->picStruct = PIC_STRUCT_TRIPLING; m_dupBuffer[0]->bDup = false; read++; } else { - m_dupBuffer[0]->dupPic->picStruct = doubling; + m_dupBuffer[0]->dupPic->picStruct = PIC_STRUCT_DOUBLING; m_dupBuffer[0]->bDup = true; m_dupBuffer[1]->bOccupied = false; read++; @@ -1756,10 +1759,28 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out) } } } - inFrame[layer]->m_forceqp = inputPic[0]->forceqp; inFrame[layer]->m_param = (m_reconfigure || m_reconfigureRc || m_param->bConfigRCFrame) ? m_latestParam : m_param; - inFrame[layer]->m_picStruct = inputPic[0]->picStruct; + + if (inFrame[layer]->m_param->pictureStructure >= 0) + inFrame[layer]->m_picStruct = inFrame[layer]->m_param->pictureStructure; + else + inFrame[layer]->m_picStruct = inputPic[0]->picStruct; + + if (inFrame[layer]->m_picStruct >= PIC_STRUCT_COUNT) + inFrame[layer]->m_picStruct = PIC_STRUCT_PROGRESSIVE_FRAME; + + /* Set up frame timing info for slicetype and ratecontrol and the frame timebase (could change across cvs) */ + if (inFrame[layer]->m_param->bEmitVUITimingInfo) + inFrame[layer]->m_timebase = ((double)m_sps.vuiParameters.timingInfo.numUnitsInTick / (double)m_sps.vuiParameters.timingInfo.timeScale); + else + inFrame[layer]->m_timebase = ((double)inFrame[layer]->m_param->fpsDenom / (double)inFrame[layer]->m_param->fpsNum); + + inFrame[layer]->m_duration = g_deltaToDivisor[inFrame[layer]->m_picStruct]; + inFrame[layer]->m_displayPicCount = m_dispPicCount; + + /* update presentation count (in-display decoder ticks count) */ + m_dispPicCount += inFrame[layer]->m_duration; /*Copy reconfigured RC parameters to frame*/ if (m_param->rc.rateControlMode == X265_RC_ABR) @@ -1894,9 +1915,9 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out) m_param->bUseRcStats = 0; } - if (m_param->bEnableFrameDuplication && ((read < written) || (m_dupBuffer[0]->dupPic->picStruct == tripling && (read <= written)))) + if (m_param->bEnableFrameDuplication && ((read < written) || (m_dupBuffer[0]->dupPic->picStruct == PIC_STRUCT_TRIPLING && (read <= written)))) { - if (m_dupBuffer[0]->dupPic->picStruct == tripling) + if (m_dupBuffer[0]->dupPic->picStruct == PIC_STRUCT_TRIPLING) m_dupBuffer[0]->bOccupied = m_dupBuffer[1]->bOccupied = false; else { @@ -1971,7 +1992,7 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out) //TODO: Add subsampling here if required inFrame[0]->m_mcstffencPic->copyFromFrame(inFrame[0]->m_fencPic); - m_lookahead->m_origPicBuf->addPicture(inFrame[0]);; + m_lookahead->m_origPicBuf->addPicture(inFrame[0]); } m_lookahead->addPicture(*inFrame[0], sliceType); @@ -2817,10 +2838,15 @@ void EncStats::addQP(double aveQp) m_totalQp += aveQp; } +void EncStats::addDuration(unsigned int durationInVuiTB) +{ + m_totDuration += durationInVuiTB; +} + char* Encoder::statsString(EncStats& stat, char* buffer, size_t bufferSize) { double fps = (double)m_param->fpsNum / m_param->fpsDenom; - double scale = fps / 1000 / (double)stat.m_numPics; + double scale = (fps / 1000.) / double(stat.m_totDuration); int len = snprintf(buffer, bufferSize, "%6u, ", stat.m_numPics); @@ -2890,7 +2916,8 @@ void Encoder::printSummary() { int p = 0; double elapsedEncodeTime = (double)(x265_mdate() - m_encodeStartTime) / 1000000; - double elapsedVideoTime = (double)m_analyzeAll[layer].m_numPics * m_param->fpsDenom / m_param->fpsNum; + /* with temporal layering totDuration and numPics of each layers are equal. Else they can differ (VFR or pulldown) */ + double elapsedVideoTime = double(m_analyzeAll[layer].m_totDuration) * double(m_param->fpsDenom) / double(m_param->fpsNum); double bitrate = (0.001f * m_analyzeAll[layer].m_accBits) / elapsedVideoTime; p += snprintf(buffer + p, sizeof(buffer) - p,"\nencoded %d frames in %.2fs (%.2f fps), %.2f kb/s, Avg QP:%2.2lf", m_analyzeAll[layer].m_numPics, @@ -3177,6 +3204,8 @@ void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_f m_analyzeAll[layer].addBits(bits); m_analyzeAll[layer].addQP(curEncData.m_avgQpAq); + m_analyzeAll[layer].addDuration(curFrame->m_duration); + if (m_param->bEnablePsnr) m_analyzeAll[layer].addPsnr(psnrY, psnrU, psnrV); @@ -3190,6 +3219,7 @@ void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_f { m_analyzeI[layer].addBits(bits); m_analyzeI[layer].addQP(curEncData.m_avgQpAq); + m_analyzeI[layer].addDuration(curFrame->m_duration); if (m_param->bEnablePsnr) m_analyzeI[layer].addPsnr(psnrY, psnrU, psnrV); if (m_param->bEnableSsim) @@ -3199,6 +3229,7 @@ void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_f { m_analyzeP[layer].addBits(bits); m_analyzeP[layer].addQP(curEncData.m_avgQpAq); + m_analyzeP[layer].addDuration(curFrame->m_duration); if (m_param->bEnablePsnr) m_analyzeP[layer].addPsnr(psnrY, psnrU, psnrV); if (m_param->bEnableSsim) @@ -3208,6 +3239,7 @@ void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, x265_f { m_analyzeB[layer].addBits(bits); m_analyzeB[layer].addQP(curEncData.m_avgQpAq); + m_analyzeB[layer].addDuration(curFrame->m_duration); if (m_param->bEnablePsnr) m_analyzeB[layer].addPsnr(psnrY, psnrU, psnrV); if (m_param->bEnableSsim) @@ -3431,7 +3463,7 @@ void Encoder::getStreamHeaders(NALList& list, Entropy& sbacCoder, Bitstream& bs) bs.write(0x10, 8); list.serialize(NAL_UNIT_ACCESS_UNIT_DELIMITER, bs); } - + /* headers for start of bitstream */ bs.resetBits(); #if ENABLE_ALPHA || ENABLE_MULTIVIEW @@ -3737,7 +3769,7 @@ void Encoder::initSPS(SPS *sps) 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->bEmitHRDSEI); vui.fieldSeqFlag = !!m_param->interlaceMode; vui.hrdParametersPresentFlag = m_param->bEmitHRDSEI; @@ -4517,6 +4549,34 @@ void Encoder::configure(x265_param *p) x265_log(p, X265_LOG_WARNING, "Dynamic-rd disabled, requires RD <= 4, VBV and aq-mode enabled\n"); } + // Cannot use temporal layers with a picture structure whose DeltaToDivisor is not 1: we would have to drop access units in higher layers + // and enforce that structures in higher layers do not hide a frame in the lower layers: it's easier to just forbid it. + if (p->bEnableTemporalSubLayers) + { + // PF, TB and BT each have DeltaToDivisor = 1 and convey convey a full frame (or a field pair) + if (p->pictureStructure > PIC_STRUCT_PROGRESSIVE_FRAME && p->pictureStructure != PIC_STRUCT_TOP_BOTTOM && p->pictureStructure != PIC_STRUCT_BOTTOM_TOP) + { + x265_log(p, X265_LOG_WARNING, "Specified picture structure is not compatible with temporal sub layers. Not using the user-provided pic-struct.\n"); + p->pictureStructure = -1; + } + if (p->bEnableFrameDuplication) + { + x265_log(p, X265_LOG_WARNING, "Frame-duplication is not compatible with temporal sub layers. Disabling Frame Duplication.\n"); + p->bEnableFrameDuplication = 0; + p->dupThreshold = 0; // prevent it from being enabled below + } + } + + if (p->pictureStructure >= 0) + { + // reject any configuration that leads to orphaned fields (1, 2, 5, 6, 9, 10, 11, 12) + if (p->pictureStructure >= PIC_STRUCT_COUNT || ((1 << p->pictureStructure) & 0b1111001100110)) + { + x265_log(p, X265_LOG_WARNING, "Invalid or illegal picture structure, not using the user-provided value.\n"); + p->pictureStructure = -1; + } + } + if (!p->bEnableFrameDuplication && p->dupThreshold && p->dupThreshold != 70) { x265_log(p, X265_LOG_WARNING, "Frame-duplication threshold works only with frame-duplication enabled. Enabling frame-duplication.\n"); @@ -4529,10 +4589,10 @@ void Encoder::configure(x265_param *p) p->bEnableFrameDuplication = 0; } - if (p->bEnableFrameDuplication && p->pictureStructure != 0 && p->pictureStructure != -1) + if (p->bEnableFrameDuplication && p->pictureStructure > PIC_STRUCT_AUTO) { - x265_log(p, X265_LOG_WARNING, "Frame-duplication works only with pic_struct = 0. Setting pic-struct = 0.\n"); - p->pictureStructure = 0; + x265_log(p, X265_LOG_WARNING, "Frame-duplication works only without forced pic-struct value. Ignoring the picture structure.\n"); + p->pictureStructure = PIC_STRUCT_AUTO; } if (m_param->bEnableFrameDuplication && (!bIsVbv || !m_param->bEmitHRDSEI)) @@ -5214,7 +5274,7 @@ void Encoder::readAnalysisFile(x265_analysis_data* analysis, int curPoc, const x X265_FREAD(&analysis->satdCost, sizeof(int64_t), 1, m_analysisFileIn, &(picData->satdCost)); X265_FREAD(&analysis->numCUsInFrame, sizeof(int), 1, m_analysisFileIn, &(picData->numCUsInFrame)); X265_FREAD(&analysis->numPartitions, sizeof(int), 1, m_analysisFileIn, &(picData->numPartitions)); - + if (m_param->bDisableLookahead) { X265_FREAD(&analysis->numCuInHeight, sizeof(uint32_t), 1, m_analysisFileIn, &(picData->numCuInHeight)); diff --git a/source/encoder/encoder.h b/source/encoder/encoder.h index 9101b79b1..9f38f3eab 100644 --- a/source/encoder/encoder.h +++ b/source/encoder/encoder.h @@ -70,6 +70,7 @@ struct EncStats double m_totalQp; double m_maxFALL; uint64_t m_accBits; + uint64_t m_totDuration; uint32_t m_numPics; uint16_t m_maxCLL; @@ -81,6 +82,7 @@ struct EncStats m_totalQp = 0; m_maxCLL = 0; m_maxFALL = 0; + m_totDuration = 0; } void addQP(double aveQp); @@ -90,12 +92,12 @@ struct EncStats void addBits(uint64_t bits); void addSsim(double ssim); + + void addDuration(unsigned int durationInVuiTB); }; #define MAX_NUM_REF_IDX 64 #define DUP_BUFFER 2 -#define doubling 7 -#define tripling 8 struct RefIdxLastGOP { @@ -194,6 +196,7 @@ class Encoder : public x265_encoder int m_numPools; int m_numTmePools; int m_curEncoder; + uint64_t m_dispPicCount; /* displayed picture count (implied by explicit or implicit picture structure) */ // weighted prediction int m_numLumaWPFrames; // number of P frames with weighted luma reference @@ -201,7 +204,6 @@ class Encoder : public x265_encoder int m_numLumaWPBiFrames; // number of B frames with weighted luma reference int m_numChromaWPBiFrames; // number of B frames with weighted chroma reference int m_conformanceMode; - int m_lastBPSEI; uint32_t m_numDelayedPic; ThreadPool* m_threadPool; diff --git a/source/encoder/frameencoder.cpp b/source/encoder/frameencoder.cpp index 5b64950b4..db0e91e08 100644 --- a/source/encoder/frameencoder.cpp +++ b/source/encoder/frameencoder.cpp @@ -106,7 +106,6 @@ void FrameEncoder::destroy() if (m_param->bEmitHRDSEI || !!m_param->interlaceMode) { delete m_rce.picTimingSEI; - delete m_rce.hrdTiming; } } @@ -129,7 +128,7 @@ bool FrameEncoder::init(Encoder *top, int numRows, int numCols) m_vbvResetTriggerRow = X265_MALLOC(int, m_param->maxSlices); ok &= !!m_sliceBaseRow; m_sliceGroupSize = (uint16_t)(m_numRows + m_param->maxSlices - 1) / m_param->maxSlices; - uint32_t sliceGroupSizeAccu = (m_numRows << 8) / m_param->maxSlices; + uint32_t sliceGroupSizeAccu = (m_numRows << 8) / m_param->maxSlices; uint32_t rowSum = sliceGroupSizeAccu; uint32_t sidx = 0; for (uint32_t i = 0; i < m_numRows; i++) @@ -183,9 +182,7 @@ bool FrameEncoder::init(Encoder *top, int numRows, int numCols) if (m_param->bEmitHRDSEI || !!m_param->interlaceMode) { m_rce.picTimingSEI = new SEIPictureTiming; - m_rce.hrdTiming = new HRDTiming; - - ok &= m_rce.picTimingSEI && m_rce.hrdTiming; + ok &= !!m_rce.picTimingSEI; } if (m_param->noiseReductionIntra || m_param->noiseReductionInter) @@ -543,7 +540,7 @@ void FrameEncoder::compressFrame(int layer) #endif if (strlen(m_param->analysisLoad)) { - for (int list = 0; list < slice->isInterB() + 1; list++) + for (int list = 0; list < slice->isInterB() + 1; list++) { for (int plane = 0; plane < (m_param->internalCsp != X265_CSP_I400 ? 3 : 1); plane++) { @@ -727,9 +724,9 @@ void FrameEncoder::compressFrame(int layer) WaveFront::setLayerId(layer); /* reset entropy coders and compute slice id */ m_entropyCoder.load(m_initSliceContext); - for (uint32_t sliceId = 0; sliceId < m_param->maxSlices; sliceId++) + for (uint32_t sliceId = 0; sliceId < m_param->maxSlices; sliceId++) for (uint32_t row = m_sliceBaseRow[sliceId]; row < m_sliceBaseRow[sliceId + 1]; row++) - m_rows[row].init(m_initSliceContext, sliceId); + m_rows[row].init(m_initSliceContext, sliceId); // reset slice counter for rate control update m_sliceCnt = 0; @@ -761,7 +758,6 @@ void FrameEncoder::compressFrame(int layer) } m_rce.encodeOrder = m_frame[layer]->m_encodeOrder; - int prevBPSEI = m_rce.encodeOrder ? m_top->m_lastBPSEI : 0; if (m_frame[layer]->m_lowres.bKeyframe) { @@ -778,8 +774,6 @@ void FrameEncoder::compressFrame(int layer) // hrdFullness() calculates the initial CPB removal delay and offset m_top->m_rateControl->hrdFullness(bpSei); bpSei->writeSEImessages(m_bs, *slice->m_sps, NAL_UNIT_PREFIX_SEI, m_nalList, m_param->bSingleSeiNal, layer); - - m_top->m_lastBPSEI = m_rce.encodeOrder; } if (m_frame[layer]->m_lowres.sliceType == X265_TYPE_IDR && m_param->bEmitIDRRecoverySEI) @@ -802,43 +796,44 @@ void FrameEncoder::compressFrame(int layer) if (vui->frameFieldInfoPresentFlag) { - if (m_param->interlaceMode > 0) + if (m_frame[layer]->m_picStruct == PIC_STRUCT_AUTO) { - if( m_param->interlaceMode == 2 ) - { - // m_picStruct should be set to 3 or 4 when field feature is enabled - if (m_param->bField) - // 3: Top field, bottom field, in that order; 4: Bottom field, top field, in that order - sei->m_picStruct = (slice->m_fieldNum == 1) ? 4 : 3; - else - sei->m_picStruct = (poc & 1) ? 1 /* top */ : 2 /* bottom */; - } - else if (m_param->interlaceMode == 1) + if (m_param->interlaceMode > 0) { - if (m_param->bField) - sei->m_picStruct = (slice->m_fieldNum == 1) ? 3: 4; - else - sei->m_picStruct = (poc & 1) ? 2 /* bottom */ : 1 /* top */; + if( m_param->interlaceMode == 2 ) + { + // m_picStruct should be set to 3 or 4 when field feature is enabled + if (m_param->bField) + // 3: Top field, bottom field, in that order; 4: Bottom field, top field, in that order + sei->m_picStruct = (slice->m_fieldNum == 1) ? PIC_STRUCT_BOTTOM_TOP : PIC_STRUCT_TOP_BOTTOM; + else + sei->m_picStruct = (poc & 1) ? PIC_STRUCT_FIELD_TOP : PIC_STRUCT_FIELD_BOTTOM; + } + else if (m_param->interlaceMode == 1) + { + if (m_param->bField) + sei->m_picStruct = (slice->m_fieldNum == 1) ? PIC_STRUCT_TOP_BOTTOM: PIC_STRUCT_BOTTOM_TOP; + else + sei->m_picStruct = (poc & 1) ? PIC_STRUCT_FIELD_BOTTOM : PIC_STRUCT_FIELD_TOP; + } } + else + sei->m_picStruct = PIC_STRUCT_PROGRESSIVE_FRAME; } - else if (m_param->bEnableFrameDuplication) - sei->m_picStruct = m_frame[layer]->m_picStruct; else - sei->m_picStruct = m_param->pictureStructure; + sei->m_picStruct = m_frame[layer]->m_picStruct; sei->m_sourceScanType = m_param->interlaceMode ? 0 : 1; - sei->m_duplicateFlag = false; } if (vui->hrdParametersPresentFlag) { - // The m_aucpbremoval delay specifies how many clock ticks the - // access unit associated with the picture timing SEI message has to - // wait after removal of the access unit with the most recent - // buffering period SEI message - sei->m_auCpbRemovalDelay = X265_MIN(X265_MAX(1, m_rce.encodeOrder - prevBPSEI), (1 << hrd->cpbRemovalDelayLength)); - sei->m_picDpbOutputDelay = slice->m_sps->numReorderPics[m_frame[layer]->m_tempLayer] + poc - m_rce.encodeOrder; + /* The m_aucpbremoval delay specifies how many clock ticks the access unit + * with the picture timing SEI message has to wait after removal of the + * access unit with the most recent buffering period SEI message */ + sei->m_auCpbRemovalDelay = X265_MIN(X265_MAX(1, m_frame[layer]->m_cpbDelay), (1 << hrd->cpbRemovalDelayLength)); + sei->m_picDpbOutputDelay = m_frame[layer]->m_dpbOutputDelay; } sei->writeSEImessages(m_bs, *slice->m_sps, NAL_UNIT_PREFIX_SEI, m_nalList, m_param->bSingleSeiNal, layer); @@ -923,7 +918,7 @@ void FrameEncoder::compressFrame(int layer) for (uint32_t sliceId = 0; sliceId < m_param->maxSlices; sliceId++) m_rows[m_sliceBaseRow[sliceId]].active = true; - + if (m_param->bEnableWavefront) { int i = 0; @@ -981,7 +976,7 @@ void FrameEncoder::compressFrame(int layer) m_mref[l][ref].applyWeight(rowIdx, m_numRows, sliceEndRow, sliceId); } } - + enableRowEncoder(m_row_to_idx[row]); /* clear external dependency for this row */ if (m_top->m_threadedME && !slice->isIntra()) @@ -1069,7 +1064,7 @@ void FrameEncoder::compressFrame(int layer) PicYuv *reconPic = m_frame[layer]->m_reconPic[0]; uint32_t height = reconPic->m_picHeight; initDecodedPictureHashSEI(0, 0, height, layer); - } + } if (m_param->bDynamicRefine && m_top->m_startPoint <= m_frame[layer]->m_encodeOrder) //Avoid collecting data that will not be used by future frames. collectDynDataFrame(layer); @@ -1755,7 +1750,7 @@ void FrameEncoder::processRowEncoder(int intRow, ThreadLocalData& tld, int layer { // Delay one row to avoid intra prediction conflict if (m_pool && !bFirstRowInSlice) - { + { int allowCol = col; // avoid race condition on last column @@ -1837,9 +1832,9 @@ void FrameEncoder::processRowEncoder(int intRow, ThreadLocalData& tld, int layer x265_emms(); if (!layer && bIsVbv) - { + { // Update encoded bits, satdCost, baseQP for each CU if tune grain is disabled - FrameData::RCStatCU& cuStat = curEncData.m_cuStat[cuAddr]; + FrameData::RCStatCU& cuStat = curEncData.m_cuStat[cuAddr]; if ((m_param->bEnableWavefront && ((cuAddr == m_sliceBaseRow[sliceId] * numCols) || !m_param->rc.bEnableConstVbv)) || !m_param->bEnableWavefront) { curEncData.m_rowStat[row].rowSatd.fetchAdd(cuStat.vbvCost); @@ -1848,7 +1843,7 @@ void FrameEncoder::processRowEncoder(int intRow, ThreadLocalData& tld, int layer curEncData.m_rowStat[row].sumQpRc += cuStat.baseQp; curEncData.m_rowStat[row].numEncodedCUs = cuAddr; } - + // If current block is at row end checkpoint, call vbv ratecontrol. if (!m_param->bEnableWavefront && col == numCols - 1) { @@ -2026,7 +2021,7 @@ void FrameEncoder::processRowEncoder(int intRow, ThreadLocalData& tld, int layer uint32_t maxRows = m_sliceBaseRow[sliceId + 1] - m_sliceBaseRow[sliceId]; if (!m_rce.encodeOrder) - rowCount = maxRows - 1; + rowCount = maxRows - 1; else if ((uint32_t)m_rce.encodeOrder <= 2 * (m_param->fpsNum / m_param->fpsDenom)) rowCount = X265_MIN((maxRows + 1) / 2, maxRows - 1); else @@ -2044,7 +2039,7 @@ void FrameEncoder::processRowEncoder(int intRow, ThreadLocalData& tld, int layer { uint32_t startAddr = m_sliceBaseRow[sliceId] * numCols; uint32_t finishAddr = startAddr + rowCount * numCols; - + for (uint32_t cuAddr = startAddr; cuAddr < finishAddr; cuAddr++) m_rowSliceTotalBits[sliceId] += curEncData.m_cuStat[cuAddr].totalBits; } diff --git a/source/encoder/frameencoder.h b/source/encoder/frameencoder.h index d06c22f12..37216775e 100644 --- a/source/encoder/frameencoder.h +++ b/source/encoder/frameencoder.h @@ -217,7 +217,7 @@ class FrameEncoder : public WaveFront, public Thread CTURow* m_rows; uint16_t m_sliceAddrBits; uint32_t m_sliceGroupSize; - uint32_t* m_sliceBaseRow; + uint32_t* m_sliceBaseRow; uint32_t* m_sliceMaxBlockRow; int64_t m_rowSliceTotalBits[2]; RateControlEntry m_rce; diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp index 21fb9e086..30779b9ee 100644 --- a/source/encoder/ratecontrol.cpp +++ b/source/encoder/ratecontrol.cpp @@ -199,7 +199,7 @@ RateControl::RateControl(x265_param& p, Encoder *top) m_partialResidualCost = 0; m_rateFactorMaxIncrement = 0; m_rateFactorMaxDecrement = 0; - m_fps = (double)m_param->fpsNum / m_param->fpsDenom; + m_timebase = (double)m_param->fpsDenom / m_param->fpsNum; m_startEndOrder.set(0); m_bTerminated = false; m_finalFrameCount.set(0); @@ -235,7 +235,6 @@ RateControl::RateControl(x265_param& p, Encoder *top) m_isAbr = m_param->rc.rateControlMode != X265_RC_CQP && !m_param->rc.bStatRead; m_2pass = m_param->rc.rateControlMode != X265_RC_CQP && m_param->rc.bStatRead; m_bitrate = m_param->rc.bitrate * 1000; - m_frameDuration = (double)m_param->fpsDenom / m_param->fpsNum; m_qp = m_param->rc.qp; m_lastRceq = 1; /* handles the cmplxrsum when the previous frame cost is zero */ m_shortTermCplxSum = 0; @@ -385,7 +384,7 @@ bool RateControl::initCUTreeSharedMem() itemSize += sizeof(uint16_t) * m_ncu; } - int32_t itemCnt = X265_MIN(m_param->keyframeMax, (int)(m_fps + 0.5)); + int32_t itemCnt = X265_MIN(m_param->keyframeMax, (int)(1./m_timebase + 0.5)); itemCnt *= GOP_CNT_CU_TREE; char shrname[MAX_SHR_NAME_LEN] = { 0 }; @@ -404,10 +403,10 @@ bool RateControl::initCUTreeSharedMem() void RateControl::initVBV(const SPS& sps) { /* We don't support changing the ABR bitrate right now, - * so if the stream starts as CBR, keep it CBR. */ - if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate / m_fps)) + * so if the stream starts as CBR, keep it CBR. */ + if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate * m_timebase)) { - m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate / m_fps); + m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate * m_timebase); x265_log(m_param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n", m_param->rc.vbvBufferSize); } @@ -420,7 +419,7 @@ void RateControl::initVBV(const SPS& sps) vbvBufferSize = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT); vbvMaxBitrate = hrd->bitRateValue << (hrd->bitRateScale + BR_SHIFT); } - m_bufferRate = static_cast(vbvMaxBitrate) / m_fps; + m_bufferRate = static_cast(vbvMaxBitrate) * m_timebase; m_vbvMaxRate = static_cast(vbvMaxBitrate); m_bufferSize = static_cast(vbvBufferSize); m_singleFrameVbv = m_bufferRate * 1.1 > m_bufferSize; @@ -459,13 +458,16 @@ bool RateControl::init(const SPS& sps) m_encodedBits = 0; m_encodedSegmentBits = 0; m_framesDone = 0; + m_durationDone = 0; m_segDur = 0; m_residualCost = 0; m_partialResidualCost = 0; m_amortizeFraction = 0.85; m_amortizeFrames = 75; m_bRcReConfig = false; - if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps && m_param->rc.bStrictCbr) /* Strict CBR segment encode */ + + /* subpar if the frames' duration does not match the timebase */ + if (m_param->totalFrames && m_param->totalFrames <= 2. / m_timebase && m_param->rc.bStrictCbr) /* Strict CBR segment encode */ { m_amortizeFraction = 0.85; m_amortizeFrames = m_param->totalFrames / 2; @@ -492,7 +494,8 @@ bool RateControl::init(const SPS& sps) /* estimated ratio that produces a reasonable QP for the first I-frame */ m_cplxrSum = .01 * pow(7.0e5, m_qCompress) * pow(m_ncu, 0.5) * tuneCplxFactor; - m_wantedBitsWindow = m_bitrate * m_frameDuration; + /* possibly a bad start if the first frame is very long */ + m_wantedBitsWindow = m_bitrate * m_timebase; m_accumPNorm = .01; m_accumPQp = (m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN) * m_accumPNorm; @@ -672,6 +675,7 @@ bool RateControl::init(const SPS& sps) } /* read stats */ p = statsIn; + uint64_t totalDuration = 0; for (int i = 0; i < m_numEntries; i++) { RateControlEntry *rce, *rcePocOrder; @@ -696,10 +700,10 @@ bool RateControl::init(const SPS& sps) if (!m_param->bMultiPassOptRPS) { int scenecut = 0; - e += sscanf(p, " in:%*d out:%*d type:%c q:%lf q-aq:%lf q-noVbv:%lf q-Rceq:%lf tex:%d mv:%d misc:%d icu:%lf pcu:%lf scu:%lf sc:%d", + e += sscanf(p, " in:%*d out:%*d type:%c q:%lf q-aq:%lf q-noVbv:%lf q-Rceq:%lf tex:%d mv:%d misc:%d icu:%lf pcu:%lf scu:%lf sc:%d cpbd:%u dspd:%u", &picType, &qpRc, &qpAq, &qNoVbv, &qRceq, &rce->coeffBits, &rce->mvBits, &rce->miscBits, &rce->iCuCount, &rce->pCuCount, - &rce->skipCuCount, &scenecut); + &rce->skipCuCount, &scenecut, &rce->cpbDuration, &rce->frameDuration); rcePocOrder->scenecut = scenecut != 0; } else @@ -708,14 +712,15 @@ bool RateControl::init(const SPS& sps) char bUsed[40]; memset(deltaPOC, 0, sizeof(deltaPOC)); memset(bUsed, 0, sizeof(bUsed)); - e += sscanf(p, " in:%*d out:%*d type:%c q:%lf q-aq:%lf q-noVbv:%lf q-Rceq:%lf tex:%d mv:%d misc:%d icu:%lf pcu:%lf scu:%lf nump:%d numnegp:%d numposp:%d deltapoc:%127s bused:%39s", + e += sscanf(p, " in:%*d out:%*d type:%c q:%lf q-aq:%lf q-noVbv:%lf q-Rceq:%lf tex:%d mv:%d misc:%d icu:%lf pcu:%lf scu:%lf nump:%d numnegp:%d numposp:%d deltapoc:%127s bused:%39s cpbd:%u dspd:%u", &picType, &qpRc, &qpAq, &qNoVbv, &qRceq, &rce->coeffBits, &rce->mvBits, &rce->miscBits, &rce->iCuCount, &rce->pCuCount, - &rce->skipCuCount, &rce->rpsData.numberOfPictures, &rce->rpsData.numberOfNegativePictures, &rce->rpsData.numberOfPositivePictures, deltaPOC, bUsed); + &rce->skipCuCount, &rce->rpsData.numberOfPictures, &rce->rpsData.numberOfNegativePictures, &rce->rpsData.numberOfPositivePictures, deltaPOC, bUsed, &rce->cpbDuration, &rce->frameDuration); splitdeltaPOC(deltaPOC, rce); splitbUsed(bUsed, rce); rce->rpsIdx = -1; } + totalDuration += rce->frameDuration; rce->keptAsRef = true; rce->isIdr = false; if (picType == 'b' || picType == 'p') @@ -742,6 +747,8 @@ bool RateControl::init(const SPS& sps) rce->qRceq = qRceq; p = next; } + m_durationDone = (double)totalDuration * m_timebase; + X265_FREE(statsBuf); if (m_param->rc.rateControlMode != X265_RC_CQP) { @@ -837,7 +844,7 @@ void RateControl::reconfigureRC() m_param->rc.vbvBufferSize = x265_clip3(0, 8000000, m_param->rc.vbvBufferSize); m_param->rc.vbvMaxBitrate = x265_clip3(0, 8000000, m_param->rc.vbvMaxBitrate); if (m_param->reconfigWindowSize) - m_param->rc.vbvMaxBitrate = (int)(m_param->rc.vbvMaxBitrate * (double)(m_fps / m_param->reconfigWindowSize)); + m_param->rc.vbvMaxBitrate = (int)(m_param->rc.vbvMaxBitrate * (double)(1. / (m_timebase * m_param->reconfigWindowSize))); if (m_param->rc.vbvMaxBitrate < m_param->rc.bitrate && m_param->rc.rateControlMode == X265_RC_ABR) { @@ -845,15 +852,15 @@ void RateControl::reconfigureRC() m_param->rc.bitrate = m_param->rc.vbvMaxBitrate; } - if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate / m_fps)) + if (m_param->rc.vbvBufferSize < (int)(m_param->rc.vbvMaxBitrate * m_timebase)) { - m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate / m_fps); + m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate * m_timebase); x265_log(m_param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n", m_param->rc.vbvBufferSize); } uint64_t vbvBufferSize = m_param->rc.vbvBufferSize * 1000ULL; uint64_t vbvMaxBitrate = m_param->rc.vbvMaxBitrate * 1000ULL; - m_bufferRate = static_cast(vbvMaxBitrate) / m_fps; + m_bufferRate = static_cast(vbvMaxBitrate) * m_timebase; m_vbvMaxRate = static_cast(vbvMaxBitrate); m_bufferSize = static_cast(vbvBufferSize); m_singleFrameVbv = m_bufferRate * 1.1 > m_bufferSize; @@ -920,9 +927,8 @@ void RateControl::initHRD(SPS& sps) // arbitrary #define MAX_DURATION 0.5 - TimingInfo *time = &sps.vuiParameters.timingInfo; - int maxCpbOutputDelay = (int)(X265_MIN(m_param->keyframeMax * MAX_DURATION * time->timeScale / time->numUnitsInTick, INT_MAX)); - int maxDpbOutputDelay = (int)(sps.maxDecPicBuffering[sps.maxTempSubLayers - 1] * MAX_DURATION * time->timeScale / time->numUnitsInTick); + int maxCpbOutputDelay = (int)(X265_MIN(m_param->keyframeMax * MAX_DURATION / m_timebase, INT_MAX)); + int maxDpbOutputDelay = (int)(sps.maxDecPicBuffering[sps.maxTempSubLayers - 1] * MAX_DURATION / m_timebase); int maxDelay = (int)(90000.0 * cpbSizeUnscale / bitRateUnscale + 0.5); hrd->initialCpbRemovalDelayLength = 2 + x265_clip3(4, 22, 32 - calcLength(maxDelay)); @@ -941,7 +947,6 @@ bool RateControl::analyseABR2Pass(uint64_t allAvailableBits) double expectedBits; double *qScale, *blurredQscale; double baseCplx = m_ncu * (m_param->bframes ? 120 : 80); - double clippedDuration = CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION; /* Blur complexities, to reduce local fluctuation of QP. * We don't blur the QPs directly, because then one very simple frame * could drag down the QP of a nearby complex frame and give it more @@ -957,6 +962,8 @@ bool RateControl::analyseABR2Pass(uint64_t allAvailableBits) { int index = i+j; RateControlEntry *rcj = &m_rce2Pass[index]; + double clippedDuration = CLIP_DURATION(rcj->frameDuration * m_timebase) / BASE_FRAME_DURATION; + weight *= 1 - pow(rcj->iCuCount / m_ncu, 2); if (weight < 0.0001) break; @@ -970,6 +977,8 @@ bool RateControl::analyseABR2Pass(uint64_t allAvailableBits) { int index = i-j; RateControlEntry *rcj = &m_rce2Pass[index]; + double clippedDuration = CLIP_DURATION(rcj->frameDuration * m_timebase) / BASE_FRAME_DURATION; + gaussianWeight = weight * exp(-j * j / 200.0); weightSum += gaussianWeight; cplxSum += gaussianWeight * (qScale2bits(rcj, 1) - rcj->miscBits) / clippedDuration; @@ -1085,7 +1094,7 @@ bool RateControl::analyseABR2Pass(uint64_t allAvailableBits) x265_log(m_param, X265_LOG_WARNING, "Error: 2pass curve failed to converge\n"); x265_log(m_param, X265_LOG_WARNING, "target: %.2f kbit/s, expected: %.2f kbit/s, avg QP: %.4f\n", (double)m_param->rc.bitrate, - expectedBits * m_fps / (m_numEntries * 1000.), + expectedBits / (m_durationDone * 1000.), avgq); if (expectedBits < allAvailableBits && avgq < m_param->rc.qpMin + 2) { @@ -1115,9 +1124,9 @@ bool RateControl::analyseABR2Pass(uint64_t allAvailableBits) bool RateControl::initPass2() { uint64_t allConstBits = 0; - uint64_t allAvailableBits = uint64_t(m_param->rc.bitrate * 1000. * m_numEntries * m_frameDuration); + uint64_t allAvailableBits = uint64_t(m_param->rc.bitrate * 1000. * m_durationDone); int startIndex, endIndex; - int fps = X265_MIN(m_param->keyframeMax, (int)(m_fps + 0.5)); + int fps = X265_MIN(m_param->keyframeMax, (int)(1./m_timebase + 0.5)); int distance = fps << 1; distance = distance > m_param->keyframeMax ? (m_param->keyframeMax << 1) : m_param->keyframeMax; startIndex = endIndex = 0; @@ -1138,7 +1147,7 @@ bool RateControl::initPass2() if (allAvailableBits < allConstBits) { x265_log(m_param, X265_LOG_ERROR, "requested bitrate is too low. estimated minimum is %d kbps\n", - (int)(allConstBits * m_fps / (m_numEntries - m_start) * 1000.)); + (int)(1000. * allConstBits / m_durationDone)); return false; } if (!analyseABR2Pass(allAvailableBits)) @@ -1184,9 +1193,11 @@ bool RateControl::initPass2() for (startIndex = m_start; startIndex < m_numEntries; startIndex++) { + RateControlEntry *rce = &m_rce2Pass[startIndex]; + m_shortTermCplxSum *= 0.5; m_shortTermCplxCount *= 0.5; - m_shortTermCplxSum += m_rce2Pass[startIndex].currentSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION); + m_shortTermCplxSum += rce->currentSatd / (CLIP_DURATION(rce->frameDuration * m_timebase) / BASE_FRAME_DURATION); m_shortTermCplxCount++; } @@ -1330,7 +1341,7 @@ void RateControl::initFramePredictors() } m_pred[0].coeff = m_pred[3].coeff = 0.75; m_pred[0].coeffMin = m_pred[3].coeffMin = 0.75 / 4; - if (m_isGrainEnabled) // when tuned for grain + if (m_isGrainEnabled) // when tuned for grain { m_pred[1].coeffMin = 0.75 / 4; m_pred[1].coeff = 0.75; @@ -1366,6 +1377,8 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode rce->keptAsRef = IS_REFERENCED(curFrame); m_predType = getPredictorType(curFrame->m_lowres.sliceType, m_sliceType); rce->poc = m_curSlice->m_poc; + rce->cpbDuration = curFrame->m_cpbDuration; + rce->frameDuration = curFrame->m_duration; if (m_param->bEnableSBRC) { @@ -1407,8 +1420,8 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode } } } - - + + if (m_param->bResetZoneConfig) { /* change ratecontrol stats for next zone if specified */ @@ -1504,7 +1517,7 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode else { /* 1.5 * MaxLumaSr * (AuCpbRemovalTime[ n ] - AuCpbRemovalTime[ n - 1 ]) / MinCr */ - rce->frameSizeMaximum = 8 * 1.5 * enc->m_vps.ptl.maxLumaSrForLevel * m_frameDuration / mincr; + rce->frameSizeMaximum = 8 * 1.5 * enc->m_vps.ptl.maxLumaSrForLevel * rce->cpbDuration * m_timebase / mincr; } rce->frameSizeMaximum *= m_param->maxAUSizeFactor; } @@ -1531,13 +1544,14 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode m_qp = int(rce->qpaRc + 0.5); rce->frameSizePlanned = qScale2bits(rce, rce->qScale); m_framesDone++; + m_durationDone += rce->frameDuration * m_timebase; return m_qp; } else - { + { int index = m_encOrder[rce->poc]; + double totalDuration = m_rce2Pass[index].frameDuration * m_timebase; index++; - double totalDuration = m_frameDuration; for (int j = 0; totalDuration < 1.0 && index < m_numEntries; j++) { switch (m_rce2Pass[index].sliceType) @@ -1554,9 +1568,9 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode default: break; } - + curFrame->m_lowres.plannedCpbDuration[j] = m_rce2Pass[index].cpbDuration * m_timebase; curFrame->m_lowres.plannedSatd[j] = m_rce2Pass[index].currentSatd; - totalDuration += m_frameDuration; + totalDuration += m_rce2Pass[index].frameDuration * m_timebase; index++; } } @@ -1594,7 +1608,7 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode m_isPatternPresent = true; } /* For a scenecut that occurs within the mini-gop, enable scene transition - * switch until the next mini-gop to ensure a min qp for all the frames within + * switch until the next mini-gop to ensure a min qp for all the frames within * the scene-transition mini-gop */ double q = x265_qScale2qp(rateEstimateQscale(curFrame, rce)); @@ -1619,7 +1633,7 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode else m_qp = m_qpConstant[m_sliceType]; curEncData.m_avgQpAq = curEncData.m_avgQpRc = m_qp; - + x265_zone* zone = getZone(); if (zone) { @@ -1655,6 +1669,7 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode } } m_framesDone++; + m_durationDone += rce->frameDuration * m_timebase; return m_qp; } @@ -1764,8 +1779,9 @@ bool RateControl::findUnderflow(double *fills, int *t0, int *t1, int over, int e int start = -1, end = -1; for (int i = *t0; i <= endPos; i++) { - fill += (m_frameDuration * m_vbvMaxRate - - qScale2bits(&m_rce2Pass[i], m_rce2Pass[i].newQScale)) * parity; + RateControlEntry *rce = &m_rce2Pass[i]; + fill += (rce->frameDuration * m_timebase * m_vbvMaxRate - + qScale2bits(rce, rce->newQScale)) * parity; fill = x265_clip3(0.0, m_bufferSize, fill); fills[i] = fill; if (fill <= bufferMin || i == 0) @@ -1865,16 +1881,18 @@ double RateControl::tuneAbrQScaleFromFeedback(double qScale) double abrBuffer = 2 * m_rateTolerance * m_bitrate; /* use framesDone instead of POC as poc count is not serial with bframes enabled */ double overflow = 1.0; - double timeDone = (double)(m_framesDone - m_param->frameNumThreads + 1) * m_frameDuration; + /* - m_param->frameNumThreads + 1 ? */ + double timeDone = m_durationDone; double wantedBits = timeDone * m_bitrate; int64_t encodedBits = m_totalBits; - if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps) + /* */ + if (m_param->totalFrames && m_param->totalFrames <= 2. / m_timebase) { - abrBuffer = m_param->totalFrames * (m_bitrate / m_fps); + abrBuffer = m_param->totalFrames * (m_bitrate * m_timebase); encodedBits = m_encodedBits; } - if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames || + if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames || m_param->rc.bStrictCbr || m_isGrainEnabled)) { abrBuffer *= X265_MAX(1, sqrt(timeDone)); @@ -1911,13 +1929,17 @@ double RateControl::tuneQScaleForZone(RateControlEntry *rce, double qScale) double RateControl::tuneQScaleForGrain(double rcOverflow) { + /* change to moving average framerate */ + double fpsFactor = 1. / m_timebase; + double qpstep = rcOverflow > 1.1 ? rcOverflow : m_lstep; double qScaleAvg = x265_qp2qScale(m_avgPFrameQp); double q = m_lastQScaleFor[P_SLICE]; int curQp = int (x265_qScale2qp(m_lastQScaleFor[P_SLICE]) + 0.5); - double curBitrate = m_qpToEncodedBits[curQp] * int(m_fps + 0.5); + /* replace 1/timebase with the running average of the framerate */ + double curBitrate = m_qpToEncodedBits[curQp] * int(fpsFactor + 0.5); int newQp = rcOverflow > 1.1 ? curQp + 2 : rcOverflow > 1 ? curQp + 1 : curQp - 1 ; - double projectedBitrate = int(m_fps + 0.5) * m_qpToEncodedBits[newQp]; + double projectedBitrate = int(fpsFactor + 0.5) * m_qpToEncodedBits[newQp]; if (curBitrate > 0 && projectedBitrate > 0) q = std::abs(projectedBitrate - m_bitrate) < std::abs (curBitrate - m_bitrate) ? x265_qp2qScale(newQp) : m_lastQScaleFor[P_SLICE]; else @@ -2024,7 +2046,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) if (m_isSceneTransition) { q = X265_MAX(ABR_SCENECUT_INIT_QP_MIN, q); - double minScenecutQscale =x265_qp2qScale(ABR_SCENECUT_INIT_QP_MIN); + double minScenecutQscale =x265_qp2qScale(ABR_SCENECUT_INIT_QP_MIN); m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]); } @@ -2096,7 +2118,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) if (!m_2pass || m_param->bliveVBV2pass || (m_2pass && m_param->rc.rateControlMode == X265_RC_CRF && m_param->rc.bEncFocusedFramesOnly)) { - /* clip qp to permissible range after vbv-lookahead estimation to avoid possible + /* clip qp to permissible range after vbv-lookahead estimation to avoid possible * mispredictions by initial frame size predictors */ qScale = clipQscale(curFrame, rce, qScale); @@ -2140,9 +2162,9 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) { m_predictedBits = m_totalBits; if (rce->encodeOrder < m_param->frameNumThreads) - m_predictedBits += (int64_t)(rce->encodeOrder * m_bitrate / m_fps); + m_predictedBits += (int64_t)(rce->encodeOrder * m_bitrate * rce->cpbDuration * m_timebase); else - m_predictedBits += (int64_t)(m_param->frameNumThreads * m_bitrate / m_fps); + m_predictedBits += (int64_t)(m_param->frameNumThreads * m_bitrate * rce->cpbDuration * m_timebase); } /* Adjust ABR buffer based on distance to the end of the video. */ if (m_numEntries > rce->encodeOrder) @@ -2206,7 +2228,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) { /* Do not overflow vbv */ double expectedSize = qScale2bits(rce, q); - double expectedVbv = m_bufferFill + m_bufferRate - expectedSize; + double expectedVbv = m_bufferFill + m_bufferRate * rce->cpbDuration - expectedSize; double expectedFullness = rce->expectedVbv / m_bufferSize; double qmax = q * (2 - expectedFullness); double sizeConstraint = 1 + expectedFullness; @@ -2219,7 +2241,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) { q *= 1.05; expectedSize = qScale2bits(rce, q); - expectedVbv = m_bufferFill + m_bufferRate - expectedSize; + expectedVbv = m_bufferFill + m_bufferRate * rce->cpbDuration - expectedSize; } } else @@ -2251,7 +2273,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) double lqmax = m_lmax[m_sliceType]; m_shortTermCplxSum *= 0.5; m_shortTermCplxCount *= 0.5; - m_shortTermCplxSum += m_currentSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION); + m_shortTermCplxSum += m_currentSatd / (CLIP_DURATION(rce->frameDuration * m_timebase) / BASE_FRAME_DURATION); m_shortTermCplxCount++; /* coeffBits to be used in 2-pass */ rce->coeffBits = (int)m_currentSatd; @@ -2312,7 +2334,8 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) q = (!m_partialResidualFrames) ? tunedQScale : initialQScale; bool isEncodeEnd = (m_param->totalFrames && m_framesDone > 0.75 * m_param->totalFrames) ? 1 : 0; - bool isEncodeBeg = m_framesDone < (int)(m_fps + 0.5); + /* beginning: less than one gop encoded */ + bool isEncodeBeg = m_durationDone < m_param->keyframeMax * m_timebase; if (m_isGrainEnabled) { if(m_sliceType!= I_SLICE && m_framesDone && !isEncodeEnd && @@ -2417,7 +2440,7 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce) /* Always use up the whole VBV in this case. */ if (m_singleFrameVbv) - rce->frameSizePlanned = m_bufferRate; + rce->frameSizePlanned = m_bufferRate * rce->cpbDuration; /* Limit planned size by MinCR */ if (m_isVbv) rce->frameSizePlanned = X265_MIN(rce->frameSizePlanned, rce->frameSizeMaximum); @@ -2487,11 +2510,12 @@ void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone) // Check if current Slice is a scene cut that follows low detailed/blank frames if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut || rce->isFadeEnd) { + double frameDurSec = rce->frameDuration * m_timebase; if (!m_isAbrReset && rce->movingAvgSum > 0 && (m_isPatternPresent || !m_param->bframes)) { int pos = X265_MAX(m_sliderPos - m_param->frameNumThreads, 0); - int64_t shrtTermWantedBits = (int64_t) (X265_MIN(pos, s_slidingWindowFrames) * m_bitrate * m_frameDuration); + int64_t shrtTermWantedBits = (int64_t) (X265_MIN(pos, s_slidingWindowFrames) * m_bitrate * frameDurSec); int64_t shrtTermTotalBitsSum = 0; // Reset ABR if prev frames are blank to prevent further sudden overflows/ high bit rate spikes. for (int i = 0; i < s_slidingWindowFrames ; i++) @@ -2504,7 +2528,7 @@ void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone) // Reduce tune complexity factor for scenes that follow blank frames double tuneCplxFactor = (m_ncu > 3600 && m_param->rc.cuTree && !m_param->rc.hevcAq) ? 2.5 : m_param->rc.hevcAq ? 1.5 : m_isGrainEnabled ? 1.9 : 1.0; m_cplxrSum /= tuneCplxFactor; - m_shortTermCplxSum = rce->lastSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION); + m_shortTermCplxSum = rce->lastSatd / (CLIP_DURATION(frameDurSec) / BASE_FRAME_DURATION); m_shortTermCplxCount = 1; m_isAbrReset = true; m_lastAbrResetPoc = rce->poc; @@ -2573,11 +2597,13 @@ double RateControl::tuneQscaleForSBRC(Frame* curFrame, double q) double curBits = predictSize(&m_pred[predType], q, (double)satd); frameBitsTotal += curBits; lookaheadBits += curBits; - lookaheadDur += m_frameDuration; - totalDuration += m_frameDuration; + lookaheadDur += curFrame->m_lowres.plannedCpbDuration[i]; + totalDuration += curFrame->m_lowres.plannedCpbDuration[i]; } //Check for segment buffer overflow and adjust QP accordingly - double segDur = m_param->keyframeMax / m_fps; + + /* should this use lookaheadDur rather? */ + double segDur = m_param->keyframeMax * m_timebase; double allowedSize = m_vbvMaxRate * segDur; double remDur = segDur - totalDuration; double remainingBits = frameBitsTotal; @@ -2635,7 +2661,8 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q) curBits = predictSize(&m_pred[m_predType], q, (double)m_currentSatd); double bufferFillCur = m_bufferFill - curBits; double targetFill; - double totalDuration = m_frameDuration; + double lastDuration = curFrame->m_cpbDuration * m_timebase; + double totalDuration = 0; frameQ[P_SLICE] = m_sliceType == I_SLICE ? q * m_param->rc.ipFactor : (m_sliceType == B_SLICE ? q / m_param->rc.pbFactor : q); frameQ[B_SLICE] = frameQ[P_SLICE] * m_param->rc.pbFactor; frameQ[I_SLICE] = frameQ[P_SLICE] / m_param->rc.ipFactor; @@ -2643,21 +2670,27 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q) bool iter = true; for (int j = 0; bufferFillCur >= 0 && iter ; j++) { + totalDuration += lastDuration; + double thisCpbFill = bufferFillCur + m_vbvMaxRate * lastDuration; + bufferFillCur += X265_MIN(thisCpbFill, X265_MAX(0, m_bufferSize - thisCpbFill)); + int type = curFrame->m_lowres.plannedType[j]; if (type == X265_TYPE_AUTO || totalDuration >= 1.0) break; - totalDuration += m_frameDuration; - double wantedFrameSize = m_vbvMaxRate * m_frameDuration; - if (bufferFillCur + wantedFrameSize <= m_bufferSize) - bufferFillCur += wantedFrameSize; + int64_t satd = curFrame->m_lowres.plannedSatd[j] >> (X265_DEPTH - 8); type = IS_X265_TYPE_I(type) ? I_SLICE : IS_X265_TYPE_B(type) ? B_SLICE : P_SLICE; int predType = getPredictorType(curFrame->m_lowres.plannedType[j], type); + curBits = predictSize(&m_pred[predType], frameQ[type], (double)satd); bufferFillCur -= curBits; + if (!m_param->bResetZoneConfig && ((uint64_t)j == (m_param->reconfigWindowSize - 1))) iter = false; + lastDuration = curFrame->m_lowres.plannedCpbDuration[j]; } + totalDuration = X265_MAX(totalDuration, m_timebase); /* if bufferFillCurr had a underflow already */ + if (rce->vbvEndAdj) { bool loopBreak = false; @@ -2749,8 +2782,8 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q) double pbits = predictSize(&m_pred[m_predType], q, (double)m_currentSatd); if (pbits > rce->frameSizeMaximum) q *= pbits / rce->frameSizeMaximum; - /* To detect frames that are more complex in SATD costs compared to prev window, yet - * lookahead vbv reduces its qscale by half its value. Be on safer side and avoid drastic + /* To detect frames that are more complex in SATD costs compared to prev window, yet + * lookahead vbv reduces its qscale by half its value. Be on safer side and avoid drastic * qscale reductions for frames high in complexity */ bool mispredCheck = rce->movingAvgSum && m_currentSatd >= rce->movingAvgSum && q <= q0 / 2; if (!m_isCbr || ((m_isAbr || m_2pass) && mispredCheck)) @@ -2825,8 +2858,8 @@ double RateControl::predictRowsSizeSum(Frame* curFrame, RateControlEntry* rce, d if (picType == I_SLICE || qScale >= refQScale) { - if (picType == P_SLICE - && refFrame + if (picType == P_SLICE + && refFrame && refFrame->m_encData->m_slice->m_sliceType == picType && refQScale > 0 && refRowBits > 0 @@ -2923,7 +2956,7 @@ int RateControl::rowVbvRateControl(Frame* curFrame, uint32_t row, RateControlEnt totalBits = (double)(int64_t)m_totalBits; double totalBitsNeeded = wantedBits; if (m_param->totalFrames) - totalBitsNeeded = (m_param->totalFrames * m_bitrate) / m_fps; + totalBitsNeeded = (m_param->totalFrames * m_bitrate) * m_timebase; double abrOvershoot = (accFrameBits + totalBits - wantedBits) / totalBitsNeeded; while (qpVbv < qpMax @@ -2942,7 +2975,7 @@ int RateControl::rowVbvRateControl(Frame* curFrame, uint32_t row, RateControlEnt while (qpVbv > qpMin && (qpVbv > row0Qp || m_singleFrameVbv) && (((accFrameBits < rce->frameSizePlanned * 0.8f && qpVbv <= prevRowQp) - || accFrameBits < (rce->bufferFill - m_bufferSize + m_bufferRate) * 1.1 + || accFrameBits < (rce->bufferFill - m_bufferSize + m_bufferRate * rce->cpbDuration) * 1.1 || (rce->vbvEndAdj && ((rce->bufferFill - accFrameBits) > (rce->targetFill * vbvEndBias)))) && (!m_param->rc.bStrictCbr ? 1 : abrOvershoot < 0))) { @@ -2971,7 +3004,7 @@ int RateControl::rowVbvRateControl(Frame* curFrame, uint32_t row, RateControlEnt /* avoid VBV underflow or MinCr violation */ while ((qpVbv < qpAbsoluteMax) - && ((rce->bufferFill - accFrameBits < m_bufferRate * maxFrameError) || + && ((rce->bufferFill - accFrameBits < m_bufferRate * rce->cpbDuration * maxFrameError) || (rce->frameSizeMaximum - accFrameBits < rce->frameSizeMaximum * maxFrameError))) { qpVbv += stepSize; @@ -3003,7 +3036,7 @@ int RateControl::rowVbvRateControl(Frame* curFrame, uint32_t row, RateControlEnt /* Last-ditch attempt: if the last row of the frame underflowed the VBV, * try again. */ - if ((rce->frameSizeEstimated > (rce->bufferFill - m_bufferRate * maxFrameError) && + if ((rce->frameSizeEstimated > (rce->bufferFill - m_bufferRate * rce->cpbDuration * maxFrameError) && qpVbv < qpMax && canReencodeRow)) { qpVbv = qpMax; @@ -3021,8 +3054,8 @@ double RateControl::getQScale(RateControlEntry *rce, double rateFactor) if (m_param->rc.cuTree && !m_param->rc.hevcAq) { // Scale and units are obtained from rateNum and rateDenom for videos with fixed frame rates. - double timescale = (double)m_param->fpsDenom / (2 * m_param->fpsNum); - q = pow(BASE_FRAME_DURATION / CLIP_DURATION(2 * timescale), 1 - m_param->rc.qCompress); + double timescale = m_timebase * (double)rce->cpbDuration; + q = pow(BASE_FRAME_DURATION / CLIP_DURATION(timescale), 1 - m_param->rc.qCompress); } else q = pow(rce->blurredComplexity, 1 - m_param->rc.qCompress); @@ -3077,8 +3110,10 @@ int RateControl::updateVbv(int64_t bits, RateControlEntry* rce) if (m_bufferFillFinal < 0) x265_log(m_param, X265_LOG_WARNING, "poc:%d, VBV underflow (%.0f bits)\n", rce->poc, (double)m_bufferFillFinal); + double bufferFillThisAU = rce->bufferRate * rce->cpbDuration; m_bufferFillFinal = X265_MAX(double(m_bufferFillFinal), 0.0); - m_bufferFillFinal = double(m_bufferFillFinal) + rce->bufferRate; + m_bufferFillFinal = double(m_bufferFillFinal) + bufferFillThisAU; + if (m_param->csvLogLevel >= 2) m_unclippedBufferFillFinal = m_bufferFillFinal; @@ -3089,15 +3124,17 @@ int RateControl::updateVbv(int64_t bits, RateControlEntry* rce) filler = (int)(m_bufferFillFinal - m_bufferSize); filler += FILLER_OVERHEAD * 8; } + m_bufferFillFinal = double(m_bufferFillFinal) - filler; - bufferBits = X265_MIN(bits + filler + m_bufferExcess, rce->bufferRate); + bufferBits = X265_MIN(bits + filler + m_bufferExcess, bufferFillThisAU); m_bufferExcess = X265_MAX(m_bufferExcess - bufferBits + bits + filler, 0); m_bufferFillActual = double(m_bufferFillActual) + bufferBits - bits - filler; } else { + m_bufferFillFinal = X265_MIN(double(m_bufferFillFinal), m_bufferSize); - bufferBits = X265_MIN(bits + m_bufferExcess, rce->bufferRate); + bufferBits = X265_MIN(bits + m_bufferExcess, bufferFillThisAU); m_bufferExcess = X265_MAX(m_bufferExcess - bufferBits + bits, 0); m_bufferFillActual = double(m_bufferFillActual) + bufferBits - bits; m_bufferFillActual = X265_MIN(double(m_bufferFillActual), m_bufferSize); @@ -3225,11 +3262,13 @@ int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry* * Not perfectly accurate with B-refs, but good enough. */ m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / (rce->qRceq * fabs(m_param->rc.pbFactor))) - (rce->rowCplxrSum); } - m_wantedBitsWindow = double(m_wantedBitsWindow) + m_frameDuration * (m_bRcReConfig ? (curFrame->m_targetBitrate * 1000) : m_bitrate); + + double frameDurSecs = rce->frameDuration * m_timebase; + m_wantedBitsWindow = double(m_wantedBitsWindow) + frameDurSecs * (m_bRcReConfig ? (curFrame->m_targetBitrate * 1000) : m_bitrate); m_totalBits.add(bits - rce->rowTotalBits); m_encodedBits += actualBits; m_encodedSegmentBits += actualBits; - m_segDur += m_frameDuration; + m_segDur += frameDurSecs; int pos = m_sliderPos - m_param->frameNumThreads; if (pos >= 0) m_encodedBitsWindow[pos % s_slidingWindowFrames] = actualBits; @@ -3261,31 +3300,6 @@ int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry* curFrame->m_rcData->count[i] = m_pred[i].count; curFrame->m_rcData->offset[i] = m_pred[i].offset; } - if (m_param->bEmitHRDSEI) - { - const VUI *vui = &curEncData.m_slice->m_sps->vuiParameters; - const HRDInfo *hrd = &vui->hrdParameters; - const TimingInfo *time = &vui->timingInfo; - if (!curFrame->m_poc) - { - // first access unit initializes the HRD - rce->hrdTiming->cpbInitialAT = 0; - rce->hrdTiming->cpbRemovalTime = m_nominalRemovalTime = (double)m_bufPeriodSEI.m_initialCpbRemovalDelay / 90000; - } - else - { - rce->hrdTiming->cpbRemovalTime = m_nominalRemovalTime + (double)rce->picTimingSEI->m_auCpbRemovalDelay * time->numUnitsInTick / time->timeScale; - double cpbEarliestAT = rce->hrdTiming->cpbRemovalTime - (double)m_bufPeriodSEI.m_initialCpbRemovalDelay / 90000; - if (!curFrame->m_lowres.bKeyframe) - cpbEarliestAT -= (double)m_bufPeriodSEI.m_initialCpbRemovalDelayOffset / 90000; - - rce->hrdTiming->cpbInitialAT = hrd->cbrFlag ? m_prevCpbFinalAT : X265_MAX(m_prevCpbFinalAT, cpbEarliestAT); - } - int filler_bits = *filler ? (*filler - START_CODE_OVERHEAD * 8) : 0; - uint32_t cpbsizeUnscale = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT); - rce->hrdTiming->cpbFinalAT = m_prevCpbFinalAT = rce->hrdTiming->cpbInitialAT + (actualBits + filler_bits)/ cpbsizeUnscale; - rce->hrdTiming->dpbOutputTime = (double)rce->picTimingSEI->m_picDpbOutputDelay * time->numUnitsInTick / time->timeScale + rce->hrdTiming->cpbRemovalTime; - } } if (rce->sliceType == I_SLICE) { @@ -3300,16 +3314,16 @@ int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry* /* called to write out the rate control frame stats info in multipass encodes */ int RateControl::writeRateControlFrameStats(Frame* curFrame, RateControlEntry* rce) { - FrameData& curEncData = *curFrame->m_encData; + FrameData& curEncData = *curFrame->m_encData; int ncu = (m_param->rc.qgSize == 8) ? m_ncu * 4 : m_ncu; char cType = rce->sliceType == I_SLICE ? (curFrame->m_lowres.sliceType == X265_TYPE_IDR ? 'I' : 'i') : rce->sliceType == P_SLICE ? 'P' : IS_REFERENCED(curFrame) ? 'B' : 'b'; - + if (!curEncData.m_param->bMultiPassOptRPS) { if (fprintf(m_statFileOut, - "in:%d out:%d type:%c q:%.2f q-aq:%.2f q-noVbv:%.2f q-Rceq:%.2f tex:%d mv:%d misc:%d icu:%.2f pcu:%.2f scu:%.2f sc:%d ;\n", + "in:%d out:%d type:%c q:%.2f q-aq:%.2f q-noVbv:%.2f q-Rceq:%.2f tex:%d mv:%d misc:%d icu:%.2f pcu:%.2f scu:%.2f sc:%d cpbd:%u dspd:%u ;\n", rce->poc, rce->encodeOrder, cType, curEncData.m_avgQpRc, curEncData.m_avgQpAq, rce->qpNoVbv, rce->qRceq, @@ -3319,7 +3333,7 @@ int RateControl::writeRateControlFrameStats(Frame* curFrame, RateControlEntry* r curFrame->m_encData->m_frameStats.percent8x8Intra * m_ncu, curFrame->m_encData->m_frameStats.percent8x8Inter * m_ncu, curFrame->m_encData->m_frameStats.percent8x8Skip * m_ncu, - curFrame->m_lowres.bScenecut) < 0) + curFrame->m_lowres.bScenecut, rce->cpbDuration, rce->frameDuration) < 0) goto writeFailure; } else @@ -3340,7 +3354,7 @@ int RateControl::writeRateControlFrameStats(Frame* curFrame, RateControlEntry* r } if (fprintf(m_statFileOut, - "in:%d out:%d type:%c q:%.2f q-aq:%.2f q-noVbv:%.2f q-Rceq:%.2f tex:%d mv:%d misc:%d icu:%.2f pcu:%.2f scu:%.2f nump:%d numnegp:%d numposp:%d %s %s ;\n", + "in:%d out:%d type:%c q:%.2f q-aq:%.2f q-noVbv:%.2f q-Rceq:%.2f tex:%d mv:%d misc:%d icu:%.2f pcu:%.2f scu:%.2f nump:%d numnegp:%d numposp:%d %s %s cpbd:%u dspd:%u ;\n", rce->poc, rce->encodeOrder, cType, curEncData.m_avgQpRc, curEncData.m_avgQpAq, rce->qpNoVbv, rce->qRceq, @@ -3353,7 +3367,7 @@ int RateControl::writeRateControlFrameStats(Frame* curFrame, RateControlEntry* r rpsWriter->numberOfPictures, rpsWriter->numberOfNegativePictures, rpsWriter->numberOfPositivePictures, - deltaPOC, bUsed) < 0) + deltaPOC, bUsed, rce->cpbDuration, rce->frameDuration) < 0) goto writeFailure; } /* Don't re-write the data in multi-pass mode. */ @@ -3380,7 +3394,7 @@ int RateControl::writeRateControlFrameStats(Frame* curFrame, RateControlEntry* r shrItem.type = &sliceType; shrItem.stats = m_cuTreeStats.qpBuffer[0]; m_cutreeShrMem->writeData(&shrItem, WriteSharedCUTreeData); - } + } } return 0; @@ -3467,7 +3481,7 @@ void RateControl::destroy() X265_FREE(m_encOrder); for (int i = 0; i < 2; i++) X265_FREE(m_cuTreeStats.qpBuffer[i]); - + if (m_relativeComplexity) X265_FREE(m_relativeComplexity); @@ -3522,14 +3536,17 @@ void RateControl::splitbUsed(char bused[], RateControlEntry *rce) double RateControl::forwardMasking(Frame* curFrame, double q) { double qp = x265_qScale2qp(q); - uint32_t maxWindowSize = uint32_t((m_param->fwdMaxScenecutWindow / 1000.0) * (m_param->fpsNum / m_param->fpsDenom) + 0.5); + double fps = 1. / m_timebase; + /* todo: consider exponential moving average for the window sizes, looking at each frame's duration */ + fps = m_framesDone < fps ? 1/(curFrame->m_duration * m_timebase) : m_durationDone / m_framesDone; + uint32_t maxWindowSize = uint32_t((m_param->fwdMaxScenecutWindow / 1000.0) * fps + 0.5); uint32_t windowSize[6], prevWindow = 0; int lastScenecut = m_top->m_rateControl->m_lastScenecut; double fwdRefQpDelta[6], fwdNonRefQpDelta[6], sliceTypeDelta[6]; for (int i = 0; i < 6; i++) { - windowSize[i] = prevWindow + (uint32_t((m_param->fwdScenecutWindow[i] / 1000.0) * (m_param->fpsNum / m_param->fpsDenom) + 0.5)); + windowSize[i] = prevWindow + (uint32_t((m_param->fwdScenecutWindow[i] / 1000.0) * (fps) + 0.5)); fwdRefQpDelta[i] = double(m_param->fwdRefQpDelta[i]); fwdNonRefQpDelta[i] = double(m_param->fwdNonRefQpDelta[i]); sliceTypeDelta[i] = SLICE_TYPE_DELTA * fwdRefQpDelta[i]; diff --git a/source/encoder/ratecontrol.h b/source/encoder/ratecontrol.h index a8fe14064..d50f7b1d8 100644 --- a/source/encoder/ratecontrol.h +++ b/source/encoder/ratecontrol.h @@ -57,14 +57,6 @@ struct Predictor double offset; }; -struct HRDTiming -{ - double cpbInitialAT; - double cpbFinalAT; - double dpbOutputTime; - double cpbRemovalTime; -}; - struct RateControlEntry { Predictor rowPreds[3][2]; @@ -89,8 +81,6 @@ struct RateControlEntry double bufferFillActual; double targetFill; bool vbvEndAdj; - double frameDuration; - double clippedDuration; AtomicDouble frameSizeEstimated; /* hold frameSize, updated from cu level vbv rc */ double frameSizeMaximum; /* max frame Size according to minCR restrictions and level of the video */ int sliceType; @@ -118,7 +108,8 @@ struct RateControlEntry bool scenecut; bool isIdr; SEIPictureTiming *picTimingSEI; - HRDTiming *hrdTiming; + unsigned int cpbDuration; /* in clock ticks */ + unsigned int frameDuration; /* in clock ticks */ int rpsIdx; RPS rpsData; bool isFadeEnd; @@ -153,14 +144,13 @@ class RateControl int m_lastScenecut; int m_lastScenecutAwareIFrame; double m_rateTolerance; - double m_frameDuration; /* current frame duration in seconds */ double m_bitrate; double m_rateFactorConstant; double m_bufferSize; AtomicDouble m_bufferFillFinal; /* real buffer as of the last finished frame */ double m_unclippedBufferFillFinal; /* real unclipped buffer as of the last finished frame used to log in CSV*/ double m_bufferFill; /* planned buffer, if all in-progress frames hit their bit budget */ - double m_bufferRate; /* # of bits added to buffer_fill after each frame */ + double m_bufferRate; /* # of bits added to buffer_fill after each tick */ double m_vbvMaxRate; /* in kbps */ double m_rateFactorMaxIncrement; /* Don't allow RF above (CRF + this value). */ double m_rateFactorMaxDecrement; /* don't allow RF below (this value). */ @@ -180,6 +170,7 @@ class RateControl int m_qpConstant[3]; int m_lastNonBPictType; int m_framesDone; /* # of frames passed through RateCotrol already */ + double m_durationDone; /* total duration pass through RateControl already, in seconds */ int64_t m_iBits; double m_cplxrSum; /* sum of bits*qscale/rceq */ AtomicDouble m_wantedBitsWindow; /* target bitrate * window */ @@ -199,7 +190,7 @@ class RateControl double m_movingSumComplexitySeg[3]; int m_frameCountSeg[3]; double m_segDur; - double m_fps; + double m_timebase; /* bitstream timebase (num_units_in_tick / time_scale) */ int64_t m_satdCostWindow[50]; int64_t m_encodedBitsWindow[50]; int m_sliderPos; @@ -234,7 +225,6 @@ class RateControl /* hrd stuff */ SEIBufferingPeriod m_bufPeriodSEI; double m_nominalRemovalTime; - double m_prevCpbFinalAT; /* 2 pass */ bool m_2pass; diff --git a/source/encoder/slicetype.cpp b/source/encoder/slicetype.cpp index 9e2beb0b3..12d5e4c5f 100644 --- a/source/encoder/slicetype.cpp +++ b/source/encoder/slicetype.cpp @@ -1041,11 +1041,13 @@ int32_t Lookahead::estimateNoise(Frame* curFrame) return (int32_t)((sum * 82137) / (6 * num * (1 << (X265_DEPTH - 8)))); } -Lookahead::Lookahead(x265_param *param, ThreadPool* pool) +Lookahead::Lookahead(x265_param *param, ThreadPool* pool, SPS* sps) { m_param = param; m_pool = pool; + m_sps = sps; + m_numPools = 0; m_lastNonB = NULL; m_isSceneTransition = false; m_scratch = NULL; @@ -1070,11 +1072,18 @@ Lookahead::Lookahead(x265_param *param, ThreadPool* pool) m_origPicBuf = 0; m_metld = NULL; + /* HRD counters */ + m_codedPicCount = 0; + m_cpbDelay = 0; + + memset(m_frameVariance, 0, sizeof(m_frameVariance)); + /* Allow the strength to be adjusted via qcompress, since the two concepts * are very similar. */ m_cuTreeStrength = (m_param->rc.hevcAq ? 6.0 : 5.0) * (1.0 - m_param->rc.qCompress); m_lastKeyframe = -m_param->keyframeMax; + m_lastKeyframeNum = -m_param->keyframeMax; m_sliceTypeBusy = false; m_fullQueueSize = X265_MAX(1, m_param->lookaheadDepth); m_bAdaptiveQuant = m_param->rc.aqMode || @@ -1466,14 +1475,14 @@ void Lookahead::getEstimatedPictureCost(Frame *curFrame) frames[b] = &curFrame->m_lowres; frames[p1] = &slice->m_refFrameList[1][0]->m_lowres; } - else + else { p0 = b = 0; p1 = b + l1poc - poc; frames[p0] = frames[b] = &curFrame->m_lowres; frames[p1] = &slice->m_refFrameList[1][0]->m_lowres; } - + break; default: @@ -2050,7 +2059,7 @@ void Lookahead::slicetypeDecide() { if (m_frameVariance[k] == -1) break; - if((k > 0 && m_frameVariance[k] >= m_frameVariance[k - 1]) || + if((k > 0 && m_frameVariance[k] >= m_frameVariance[k - 1]) || (k == 0 && m_frameVariance[k] >= m_frameVariance[length - 1])) { m_isFadeIn = true; @@ -2115,6 +2124,11 @@ void Lookahead::slicetypeDecide() for (bframes = 0, brefs = 0;; bframes++) { Lowres& frm = list[bframes]->m_lowres; + /* frame timing. duration must be accounted for else a keyframe limit could be overshoot + * during the display of the said frame. */ + int64_t displayPicCount = (int64_t)list[bframes]->m_displayPicCount; + int64_t durationPicCount = (int64_t)list[bframes]->m_duration; + int64_t keyintTicksDelta = displayPicCount - m_lastKeyframe + durationPicCount; if (frm.sliceTypeReq != X265_TYPE_AUTO && frm.sliceTypeReq != frm.sliceType) frm.sliceType = frm.sliceTypeReq; @@ -2124,7 +2138,6 @@ void Lookahead::slicetypeDecide() x265_log(m_param, X265_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid\n", frm.frameNum); } - /* pyramid with multiple B-refs needs a big enough dpb that the preceding P-frame stays available. * smaller dpb could be supported by smart enough use of mmco, but it's easier just to forbid it. */ else if (frm.sliceType == X265_TYPE_BREF && m_param->bBPyramid && brefs && @@ -2134,8 +2147,9 @@ void Lookahead::slicetypeDecide() x265_log(m_param, X265_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid and %d reference frames\n", frm.sliceType, m_param->maxNumReferences); } - if (((!m_param->bIntraRefresh || frm.frameNum == 0) && frm.frameNum - m_lastKeyframe >= m_param->keyframeMax && - (!m_extendGopBoundary || frm.frameNum - m_lastKeyframe >= m_param->keyframeMax + m_param->gopLookahead)) || + + if (((!m_param->bIntraRefresh || frm.frameNum == 0) && keyintTicksDelta > m_param->keyframeMax && + (!m_extendGopBoundary || keyintTicksDelta > m_param->keyframeMax + m_param->gopLookahead)) || (frm.frameNum == (m_param->chunkStart - 1)) || (frm.frameNum == m_param->chunkEnd)) { if (frm.sliceType == X265_TYPE_AUTO || frm.sliceType == X265_TYPE_I) @@ -2163,11 +2177,12 @@ void Lookahead::slicetypeDecide() frm.sliceType = X265_TYPE_IDR; } } - if ((frm.sliceType == X265_TYPE_I && frm.frameNum - m_lastKeyframe >= m_param->keyframeMin) || (frm.frameNum == (m_param->chunkStart - 1)) || (frm.frameNum == m_param->chunkEnd)) + if ((frm.sliceType == X265_TYPE_I && keyintTicksDelta > m_param->keyframeMin) || (frm.frameNum == (m_param->chunkStart - 1)) || (frm.frameNum == m_param->chunkEnd)) { if (m_param->bOpenGOP) { - m_lastKeyframe = frm.frameNum; + m_lastKeyframe = displayPicCount; + m_lastKeyframeNum = frm.frameNum; frm.bKeyframe = true; } else @@ -2182,7 +2197,8 @@ void Lookahead::slicetypeDecide() if (frm.sliceType == X265_TYPE_IDR) { /* Closed GOP */ - m_lastKeyframe = frm.frameNum; + m_lastKeyframe = displayPicCount; + m_lastKeyframeNum = frm.frameNum; frm.bKeyframe = true; int zoneRadl = 0; if (m_param->bResetZoneConfig) @@ -2226,12 +2242,15 @@ void Lookahead::slicetypeDecide() for (bframes = 0, brefs = 0;; bframes++) { Lowres& frm = list[bframes]->m_lowres; + int64_t displayPicCount = (int64_t)list[bframes]->m_displayPicCount; + int64_t durationPicCount = (int64_t)list[bframes]->m_duration; if (frm.sliceType == X265_TYPE_BREF) brefs++; - if ((IS_X265_TYPE_I(frm.sliceType) && frm.frameNum - m_lastKeyframe >= m_param->keyframeMin) + if ((IS_X265_TYPE_I(frm.sliceType) && displayPicCount - m_lastKeyframe + durationPicCount > m_param->keyframeMin) || (frm.frameNum == (m_param->chunkStart - 1)) || (frm.frameNum == m_param->chunkEnd)) { - m_lastKeyframe = frm.frameNum; + m_lastKeyframe = displayPicCount; + m_lastKeyframeNum = frm.frameNum; frm.bKeyframe = true; } if (!IS_X265_TYPE_B(frm.sliceType)) @@ -2303,6 +2322,8 @@ void Lookahead::slicetypeDecide() if (m_param->bEnableTemporalSubLayers > 2) { + uint8_t codedFrameOrderedIndex[X265_BFRAME_MAX+1]; + //Split the partial mini GOP into sub mini GOPs when temporal sub layers are enabled if (bframes < m_param->bframes) { @@ -2374,6 +2395,7 @@ void Lookahead::slicetypeDecide() int idx = 0; /* add non-B to output queue */ + codedFrameOrderedIndex[idx] = newbFrames; list[newbFrames]->m_reorderedPts = pts[idx++]; list[newbFrames]->m_gopOffset = 0; list[newbFrames]->m_gopId = gopId; @@ -2393,6 +2415,7 @@ void Lookahead::slicetypeDecide() list[bframes]->m_gopId = gopId; list[offset]->m_tempLayer = x265_gop_ra[gopId][j++].layer; + codedFrameOrderedIndex[idx] = offset; list[offset]->m_reorderedPts = pts[idx++]; m_outputQueue.pushBack(*list[offset]); i++; @@ -2457,6 +2480,7 @@ void Lookahead::slicetypeDecide() m_inputLock.release(); m_lastNonB = &list[newbFrames]->m_lowres; + codedFrameOrderedIndex[idx] = newbFrames; list[newbFrames]->m_reorderedPts = pts[idx++]; list[newbFrames]->m_gopOffset = 0; list[newbFrames]->m_gopId = -1; @@ -2468,6 +2492,7 @@ void Lookahead::slicetypeDecide() { if (list[i]->m_lowres.sliceType == X265_TYPE_BREF) { + codedFrameOrderedIndex[idx] = i; list[i]->m_reorderedPts = pts[idx++]; list[i]->m_gopOffset = 0; list[i]->m_gopId = -1; @@ -2483,6 +2508,7 @@ void Lookahead::slicetypeDecide() /* push all the B frames into output queue except B-ref, which already pushed into output queue */ if (list[i]->m_lowres.sliceType != X265_TYPE_BREF) { + codedFrameOrderedIndex[idx] = i; list[i]->m_reorderedPts = pts[idx++]; list[i]->m_gopOffset = 0; list[i]->m_gopId = -1; @@ -2545,6 +2571,7 @@ void Lookahead::slicetypeDecide() int idx = 0; /* add non-B to output queue */ + codedFrameOrderedIndex[idx] = bframes; list[bframes]->m_reorderedPts = pts[idx++]; list[bframes]->m_gopOffset = 0; list[bframes]->m_gopId = m_gopId; @@ -2564,12 +2591,24 @@ void Lookahead::slicetypeDecide() list[offset]->m_tempLayer = x265_gop_ra[m_gopId][j++].layer; /* add B frames to output queue */ + codedFrameOrderedIndex[idx] = offset; list[offset]->m_reorderedPts = pts[idx++]; m_outputQueue.pushBack(*list[offset]); i++; } } + /* Compute HRD CpbDpb delays */ + { + Frame *prevFrame = NULL; + for (int i = 0; i <= bframes; ++i) + { + calculateDurations(list[codedFrameOrderedIndex[i]], prevFrame); + prevFrame = list[codedFrameOrderedIndex[i]]; + } + } + + bool isKeyFrameAnalyse = (m_param->rc.cuTree || (m_param->rc.vbvBufferSize && m_param->lookaheadDepth)); if (isKeyFrameAnalyse && IS_X265_TYPE_I(m_lastNonB->sliceType)) { @@ -2606,7 +2645,6 @@ void Lookahead::slicetypeDecide() } else { - if (bframes) list[bframes - 1]->m_lowres.bLastMiniGopBFrame = true; list[bframes]->m_lowres.leadingBframes = bframes; @@ -2674,6 +2712,7 @@ void Lookahead::slicetypeDecide() * in the output queue. The order is important because Frame can * only be in one list at a time */ int64_t pts[X265_BFRAME_MAX + 1]; + uint8_t codedFrameOrderedIndex[X265_BFRAME_MAX + 1]; for (int i = 0; i <= bframes; i++) { Frame *curFrame; @@ -2685,8 +2724,9 @@ void Lookahead::slicetypeDecide() m_outputLock.acquire(); - /* add non-B to output queue */ int idx = 0; + /* add non-B to output queue */ + codedFrameOrderedIndex[idx] = bframes; list[bframes]->m_reorderedPts = pts[idx++]; m_outputQueue.pushBack(*list[bframes]); @@ -2697,6 +2737,7 @@ void Lookahead::slicetypeDecide() { if (list[i]->m_lowres.sliceType == X265_TYPE_BREF) { + codedFrameOrderedIndex[idx] = i; list[i]->m_reorderedPts = pts[idx++]; m_outputQueue.pushBack(*list[i]); } @@ -2709,11 +2750,21 @@ void Lookahead::slicetypeDecide() /* push all the B frames into output queue except B-ref, which already pushed into output queue */ if (list[i]->m_lowres.sliceType != X265_TYPE_BREF) { + codedFrameOrderedIndex[idx] = i; list[i]->m_reorderedPts = pts[idx++]; m_outputQueue.pushBack(*list[i]); } } + /* Compute HRD CpbDpb delays */ + { + Frame *prevFrame = NULL; + for (int i = 0; i <= bframes; ++i) + { + calculateDurations(list[codedFrameOrderedIndex[i]], prevFrame); + prevFrame = list[codedFrameOrderedIndex[i]]; + } + } bool isKeyFrameAnalyse = (m_param->rc.cuTree || (m_param->rc.vbvBufferSize && m_param->lookaheadDepth)); if (isKeyFrameAnalyse && IS_X265_TYPE_I(m_lastNonB->sliceType)) @@ -2724,8 +2775,10 @@ void Lookahead::slicetypeDecide() int j; for (j = 0; j < maxSearch; j++) { + setDurationsToLowres(curFrame); frames[j + 1] = &curFrame->m_lowres; curFrame = curFrame->m_next; + } m_inputLock.release(); @@ -2750,6 +2803,60 @@ void Lookahead::slicetypeDecide() } } +void Lookahead::calculateDurations(Frame *frame, Frame *prevFrame) +{ + frame->m_cpbDelay = m_cpbDelay; + frame->m_dpbOutputDelay = frame->m_displayPicCount - m_codedPicCount; + frame->m_cpbDuration = frame->m_duration; + frame->m_codedPicCount = m_codedPicCount; + + int dpbDelay = (int64_t)frame->m_displayPicCount - (int64_t)m_codedPicCount; + /* largest re-ordering at highest temporal layer */ + dpbDelay += ((m_param->bframes > 0) ? 1 : 0) + m_sps->numReorderPics[X265_MAX(0, (m_param->bEnableTemporalSubLayers - 1))]; + + if (dpbDelay < 0) + { + frame->m_cpbDelay += dpbDelay; + frame->m_dpbOutputDelay = 0; + + /* Bref and next B-frame cpbRemovalDelay can be equal on long sequence of doubling or tripling. + * larger m_dpbOutputDelay offset could solve this, but numReorder+1 is the max allowed on UHD BD. + * To avoid two access units with the same cpb removal time, shift prior frame back by a tick. */ + if (prevFrame && (prevFrame->m_cpbDelay == frame->m_cpbDelay)) + { + prevFrame->m_cpbDelay -= 1; + prevFrame->m_dpbOutputDelay += 1; + } + } + else + { + frame->m_dpbOutputDelay = (unsigned int)dpbDelay; + } + + setDurationsToLowres(frame); + + /* Buffering Period SEI (attached with the keyframe) */ + if (!m_param->bIntraRefresh && frame->m_lowres.bKeyframe) + { + m_cpbDelay = 0; + } + + m_cpbDelay += frame->m_cpbDuration; + m_codedPicCount += frame->m_duration; +} + +void Lookahead::setDurationsToLowres(Frame *frame) +{ + frame->m_lowres.dispPicCount = frame->m_displayPicCount; + frame->m_lowres.durationPicCount = frame->m_duration; + + /* fair assumption: assume the cpb duration is equal to the display duration. + * It's only an issue with extreme VFR or low delay applications */ + double durationSecs = frame->m_duration * frame->m_timebase; + frame->m_lowres.dispDurationSecs = durationSecs; + frame->m_lowres.cpbDurationSecs = durationSecs; +} + void Lookahead::vbvLookahead(Lowres **frames, int numFrames, int keyframe) { int prevNonB = 0, curNonB = 1, idx = 0; @@ -2769,6 +2876,7 @@ void Lookahead::vbvLookahead(Lowres **frames, int numFrames, int keyframe) int p0 = IS_X265_TYPE_I(frames[curNonB]->sliceType) ? curNonB : prevNonB; frames[nextNonB]->plannedSatd[idx] = vbvFrameCost(frames, p0, curNonB, curNonB); frames[nextNonB]->plannedType[idx] = frames[curNonB]->sliceType; + frames[nextNonB]->plannedCpbDuration[idx] = frames[curNonB]->cpbDurationSecs; /* Save the nextNonB Cost in each B frame of the current miniGop */ if (curNonB > miniGopEnd) @@ -2776,7 +2884,8 @@ void Lookahead::vbvLookahead(Lowres **frames, int numFrames, int keyframe) for (int j = nextB; j < miniGopEnd; j++) { frames[j]->plannedSatd[frames[j]->indB] = frames[nextNonB]->plannedSatd[idx]; - frames[j]->plannedType[frames[j]->indB++] = frames[nextNonB]->plannedType[idx]; + frames[j]->plannedType[frames[j]->indB] = frames[nextNonB]->plannedType[idx]; + frames[j]->plannedCpbDuration[frames[j]->indB++] = frames[nextNonB]->plannedCpbDuration[idx]; } } idx++; @@ -2806,6 +2915,8 @@ void Lookahead::vbvLookahead(Lowres **frames, int numFrames, int keyframe) satdCost = vbvFrameCost(frames, prevNonB, curNonB, i); frames[nextNonB]->plannedSatd[idx] = satdCost; frames[nextNonB]->plannedType[idx] = type; + frames[nextNonB]->plannedCpbDuration[idx] = frames[i]->cpbDurationSecs; + /* Save the nextB Cost in each B frame of the current miniGop */ for (int j = nextB; j < miniGopEnd; j++) @@ -2815,7 +2926,9 @@ void Lookahead::vbvLookahead(Lowres **frames, int numFrames, int keyframe) if (j >= i && j !=nextBRef) continue; frames[j]->plannedSatd[frames[j]->indB] = satdCost; - frames[j]->plannedType[frames[j]->indB++] = type; + frames[j]->plannedType[frames[j]->indB] = type; + frames[j]->plannedCpbDuration[frames[j]->indB++] = frames[nextB]->cpbDurationSecs; + } } prevNonB = curNonB; @@ -2845,20 +2958,29 @@ int64_t Lookahead::vbvFrameCost(Lowres **frames, int p0, int p1, int b) void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) { - int numFrames, origNumFrames, keyintLimit, framecnt; + int numFrames, origNumFrames, framecnt; int maxSearch = X265_MIN(m_param->lookaheadDepth, X265_LOOKAHEAD_MAX); int cuCount = m_8x8Blocks; int resetStart; bool bIsVbvLookahead = m_param->rc.vbvBufferSize && m_param->lookaheadDepth; + int keyintTimeLimit = m_param->keyframeMax + (int)(m_lastKeyframe - (frames[0]->dispPicCount + frames[0]->durationPicCount)); + int keyIntFrameCnt = 0, durationAhead = 0; + /* count undecided frames */ for (framecnt = 0; framecnt < maxSearch; framecnt++) { Lowres *fenc = frames[framecnt + 1]; if (!fenc || fenc->sliceType != X265_TYPE_AUTO) break; + durationAhead += fenc->durationPicCount; + if (durationAhead <= keyintTimeLimit) + ++keyIntFrameCnt; } + /* protect against runs where lowres durations aren't ready */ + keyIntFrameCnt = X265_MIN(keyIntFrameCnt, m_param->keyframeMax); + if (!framecnt && m_param->analysisLoadReuseLevel != 1) { if (m_param->rc.cuTree) @@ -2868,7 +2990,6 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) if (framecnt) { - frames[framecnt + 1] = NULL; if (m_param->bResetZoneConfig) @@ -2879,31 +3000,29 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) curZoneStart += curZoneStart ? m_param->rc.zones[i].zoneParam->radl : 0; nextZoneStart += (i + 1 < m_param->rc.zonefileCount) ? m_param->rc.zones[i + 1].startFrame + m_param->rc.zones[i + 1].zoneParam->radl : m_param->totalFrames; if (curZoneStart <= frames[0]->frameNum && nextZoneStart > frames[0]->frameNum) - m_param->keyframeMax = nextZoneStart - curZoneStart; + keyIntFrameCnt = X265_MIN(keyIntFrameCnt, nextZoneStart - curZoneStart); if (m_param->rc.zones[m_param->rc.zonefileCount - 1].startFrame <= frames[0]->frameNum && nextZoneStart == 0) - m_param->keyframeMax = m_param->rc.zones[0].keyframeMax; + keyIntFrameCnt = X265_MIN(keyIntFrameCnt, m_param->rc.zones[0].keyframeMax); } } } - int keylimit = m_param->keyframeMax; if (frames[0]->frameNum < m_param->chunkEnd) { - int chunkStart = (m_param->chunkStart - m_lastKeyframe - 1); - int chunkEnd = (m_param->chunkEnd - m_lastKeyframe); - if ((chunkStart > 0) && (chunkStart < m_param->keyframeMax)) - keylimit = chunkStart; - else if ((chunkEnd > 0) && (chunkEnd < m_param->keyframeMax)) - keylimit = chunkEnd; + int chunkStart = (m_param->chunkStart - m_lastKeyframeNum - 1); + int chunkEnd = (m_param->chunkEnd - m_lastKeyframeNum); + if ((chunkStart > 0) && (chunkStart < keyIntFrameCnt)) + keyIntFrameCnt = chunkStart; + else if ((chunkEnd > 0) && (chunkEnd < keyIntFrameCnt)) { + keyIntFrameCnt = chunkEnd; + } } - int keyFrameLimit = keylimit + m_lastKeyframe - frames[0]->frameNum - 1; - if (m_param->gopLookahead && keyFrameLimit <= m_param->bframes + 1) - keyintLimit = keyFrameLimit + m_param->gopLookahead; - else - keyintLimit = keyFrameLimit; + int keyIntFrameCntExtended = keyIntFrameCnt; + if (m_param->gopLookahead && keyIntFrameCnt <= m_param->bframes + 1) + keyIntFrameCntExtended += m_param->gopLookahead; - origNumFrames = numFrames = m_param->bIntraRefresh ? framecnt : X265_MIN(framecnt, keyintLimit); + origNumFrames = numFrames = m_param->bIntraRefresh ? framecnt : X265_MIN(framecnt, keyIntFrameCntExtended); if (framecnt) { @@ -3003,7 +3122,7 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) frames[1]->sliceType = X265_TYPE_I; return; } - if (m_param->gopLookahead && (keyFrameLimit >= 0) && (keyFrameLimit <= m_param->bframes + 1)) + if (m_param->gopLookahead && (keyIntFrameCnt >= 0) && (keyIntFrameCnt <= m_param->bframes + 1)) { bool sceneTransition = m_isSceneTransition; m_extendGopBoundary = false; @@ -3109,7 +3228,20 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) bool bForceRADL = zoneRadl || (m_param->radl && (m_param->keyframeMax == m_param->keyframeMin)); bool bLastMiniGop = (framecnt >= m_param->bframes + 1) ? false : true; int radl = m_param->radl ? m_param->radl : zoneRadl; - int preRADL = m_lastKeyframe + m_param->keyframeMax - radl - 1; /*Frame preceeding RADL in POC order*/ + + int preRADL = frames[0]->frameNum; + for (int j = numBFrames; j > 0; --j) + { + /*Frame preceeding RADL in POC order*/ + if (frames[j]->dispPicCount + frames[j]->durationPicCount - m_lastKeyframe < m_param->keyframeMax) + { + preRADL = frames[j]->frameNum; + break; + } + } + /* radl in frame counts */ + preRADL -= radl; + if (bForceRADL && (frames[0]->frameNum == preRADL) && !bLastMiniGop) { int j = 1; @@ -3144,17 +3276,25 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) aqMotion(frames, bKeyframe); if (m_param->rc.cuTree) - cuTree(frames, X265_MIN(numFrames, m_param->keyframeMax), bKeyframe); + cuTree(frames, X265_MIN(numFrames, keyIntFrameCnt), bKeyframe); - if (m_param->gopLookahead && (keyFrameLimit >= 0) && (keyFrameLimit <= m_param->bframes + 1) && !m_extendGopBoundary) - keyintLimit = keyFrameLimit; + /* why? */ + if (m_param->gopLookahead && (keyIntFrameCnt >= 0) && (keyIntFrameCnt <= m_param->bframes + 1) && !m_extendGopBoundary) + keyIntFrameCntExtended = keyIntFrameCnt; if (!m_param->bIntraRefresh) - for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax) + { + unsigned int duration = 0; + for (int j = keyIntFrameCntExtended + 1; j <= numFrames; ++j) { - frames[j]->sliceType = X265_TYPE_I; - resetStart = X265_MIN(resetStart, j + 1); + if (duration % m_param->keyframeMax == 0) + { + frames[j]->sliceType = X265_TYPE_I; + resetStart = X265_MIN(resetStart, j + 1); + } + duration += frames[j]->durationPicCount; } + } if (bIsVbvLookahead) vbvLookahead(frames, numFrames, bKeyframe); @@ -3179,7 +3319,7 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) if (!shhh) break; } - cuTree(frames, X265_MIN(framecnt, m_param->keyframeMax), bKeyframe); + cuTree(frames, X265_MIN(framecnt, keyIntFrameCnt), bKeyframe); } } @@ -3240,7 +3380,7 @@ bool Lookahead::scenecut(Lowres **frames, int p0, int p1, bool bRealScenecut, in { int64_t curCost = frames[i]->costEst[i - p0][0]; int64_t prevCost = frames[i - 1]->costEst[i - 1 - p0][0]; - if (fabs((double)(curCost - avgSatdCost)) > 0.1 * avgSatdCost || + if (fabs((double)(curCost - avgSatdCost)) > 0.1 * avgSatdCost || fabs((double)(curCost - prevCost)) > 0.1 * prevCost) { fluctuate = true; @@ -3268,8 +3408,8 @@ bool Lookahead::scenecut(Lowres **frames, int p0, int p1, bool bRealScenecut, in } /* A frame is always analysed with bRealScenecut = true first, and then bRealScenecut = false, - the former for I decisions and the latter for P/B decisions. It's possible that the first - analysis detected scenecuts which were later nulled due to scene transitioning, in which + the former for I decisions and the latter for P/B decisions. It's possible that the first + analysis detected scenecuts which were later nulled due to scene transitioning, in which case do not return a true scenecut for this frame */ if (!frames[p1]->bScenecut && m_param->bframes) @@ -3286,7 +3426,7 @@ bool Lookahead::scenecutInternal(Lowres **frames, int p0, int p1, bool bRealScen estGroup.singleCost(p0, p1, p1); int64_t icost = frame->costEst[0][0]; int64_t pcost = frame->costEst[p1 - p0][0]; - int gopSize = (frame->frameNum - m_lastKeyframe) % m_param->keyframeMax; + int gopDuration = (frame->dispPicCount - m_lastKeyframe) % m_param->keyframeMax; float threshMax = (float)(m_param->scenecutThreshold / 100.0); /* magic numbers pulled out of thin air */ float threshMin = (float)(threshMax * 0.25); @@ -3296,15 +3436,15 @@ bool Lookahead::scenecutInternal(Lowres **frames, int p0, int p1, bool bRealScen { if (m_param->keyframeMin == m_param->keyframeMax) threshMin = threshMax; - if (gopSize <= m_param->keyframeMin / 4 || m_param->bIntraRefresh) + if (gopDuration <= m_param->keyframeMin / 4 || m_param->bIntraRefresh) bias = threshMin / 4; - else if (gopSize <= m_param->keyframeMin) - bias = threshMin * gopSize / m_param->keyframeMin; + else if (gopDuration <= m_param->keyframeMin) + bias = threshMin * gopDuration / m_param->keyframeMin; else { bias = threshMin + (threshMax - threshMin) - * (gopSize - m_param->keyframeMin) + * (gopDuration - m_param->keyframeMin) / (m_param->keyframeMax - m_param->keyframeMin); } } @@ -3315,7 +3455,7 @@ bool Lookahead::scenecutInternal(Lowres **frames, int p0, int p1, bool bRealScen int pmb = m_8x8Blocks - imb; frame->bScenecut = res; // for csv log x265_log(m_param, X265_LOG_DEBUG, "scene cut at %d Icost:%d Pcost:%d ratio:%.4f bias:%.4f gop:%d (imb:%d pmb:%d)\n", - frame->frameNum, icost, pcost, 1. - (double)pcost / icost, bias, gopSize, imb, pmb); + frame->frameNum, icost, pcost, 1. - (double)pcost / icost, bias, gopDuration, imb, pmb); } return res; } @@ -3669,9 +3809,9 @@ void Lookahead::cuTree(Lowres **frames, int numframes, bool bIntra) int bframes = 0; x265_emms(); - double totalDuration = 0.0; + double totalDuration = 0.; for (int j = 0; j <= numframes; j++) - totalDuration += (double)m_param->fpsDenom / m_param->fpsNum; + totalDuration += frames[j]->dispDurationSecs; double averageDuration = totalDuration / (numframes + 1); @@ -3778,7 +3918,7 @@ void Lookahead::estimateCUPropagate(Lowres **frames, double averageDuration, int uint16_t *propagateCost = frames[b]->propagateCost; x265_emms(); - double fpsFactor = CLIP_DURATION((double)m_param->fpsDenom / m_param->fpsNum) / CLIP_DURATION(averageDuration); + double fpsFactor = CLIP_DURATION(frames[b]->dispDurationSecs) / CLIP_DURATION(averageDuration); /* For non-referred frames the source costs are always zero, so just memset one row and re-use it. */ if (!referenced) @@ -3875,7 +4015,7 @@ void Lookahead::estimateCUPropagate(Lowres **frames, double averageDuration, int void Lookahead::computeCUTreeQpOffset(Lowres *frame, double averageDuration, int ref0Distance) { - int fpsFactor = (int)(CLIP_DURATION(averageDuration) / CLIP_DURATION((double)m_param->fpsDenom / m_param->fpsNum) * 256); + int fpsFactor = (int)(CLIP_DURATION(averageDuration) / CLIP_DURATION(frame->dispDurationSecs) * 256); uint32_t loopIncr = (m_param->rc.qgSize == 8) ? 8 : 16; double weightdelta = 0.0; @@ -3946,7 +4086,7 @@ void Lookahead::computeCUTreeQpOffset(Lowres *frame, double averageDuration, int uint32_t idx = ((block_yy / loopIncr) * (maxCols)) + (block_xx / loopIncr); log2_ratio += *(pcCuTree8x8 + idx); - + blockXY++; } } @@ -4127,7 +4267,7 @@ int64_t Lookahead::frameCostRecalculate(Lowres** frames, int p0, int p1, int b) qp_offset[cux * 2 + cuy * m_8x8Width * 4 + 1] + qp_offset[cux * 2 + cuy * m_8x8Width * 4 + frames[b]->maxBlocksInRowFullRes] + qp_offset[cux * 2 + cuy * m_8x8Width * 4 + frames[b]->maxBlocksInRowFullRes + 1]) / 4; - else + else qp_adj = qp_offset[cuxy]; cuCost = (cuCost * x265_exp2fix8(qp_adj) + 128) >> 8; rowSatd[cuy] += cuCost; diff --git a/source/encoder/slicetype.h b/source/encoder/slicetype.h index 36fbeb457..838320a50 100644 --- a/source/encoder/slicetype.h +++ b/source/encoder/slicetype.h @@ -161,6 +161,7 @@ class Lookahead : public JobProvider Event m_outputSignal; LookaheadTLD* m_tld; x265_param* m_param; + SPS* m_sps; // needed for pic timing used in ratecontrol Lowres* m_lastNonB; int* m_scratch; // temp buffer for cutree propagate pixel* m_noiseBlurBuf; // persistent blur buffer for estimateNoise() fallback path @@ -169,7 +170,8 @@ class Lookahead : public JobProvider /* pre-lookahead */ int m_fullQueueSize; - int m_lastKeyframe; + int64_t m_lastKeyframe; /* in decoder ticks */ + int m_lastKeyframeNum; int m_8x8Width; int m_8x8Height; int m_8x8Blocks; @@ -211,7 +213,7 @@ class Lookahead : public JobProvider OrigPicBuffer* m_origPicBuf; MotionEstimatorTLD* m_metld; - Lookahead(x265_param *param, ThreadPool *pool); + Lookahead(x265_param *param, ThreadPool *pool, SPS* sps); #if DETAILED_CU_STATS int64_t m_slicetypeDecideElapsedTime; int64_t m_preLookaheadElapsedTime; @@ -224,6 +226,9 @@ class Lookahead : public JobProvider void getWorkerStats(int64_t& framecostBatchElapsedTime, int64_t& coopSliceElapsedTime, int64_t& mcstfBatchElapsedTime); #endif + int64_t m_cpbDelay; /* current cpb delay in clock ticks */ + uint64_t m_codedPicCount; /* coded picture count in clock ticks */ + bool create(); void destroy(); void stopJobs(); @@ -258,6 +263,8 @@ class Lookahead : public JobProvider void slicetypePath(Lowres **frames, int length, char(*best_paths)[X265_LOOKAHEAD_MAX + 1]); int64_t slicetypePathCost(Lowres **frames, char *path, int64_t threshold); int64_t vbvFrameCost(Lowres **frames, int p0, int p1, int b); + void calculateDurations(Frame *frame, Frame *prevFrame); + void setDurationsToLowres(Frame *frame); void vbvLookahead(Lowres **frames, int numFrames, int keyframes); void aqMotion(Lowres **frames, bool bintra); void calcMotionAdaptiveQuantFrame(Lowres **frames, int p0, int p1, int b); diff --git a/source/x265.h b/source/x265.h index 990cf9a8a..e17f72f15 100644 --- a/source/x265.h +++ b/source/x265.h @@ -780,7 +780,7 @@ typedef struct x265_zone struct x265_param* zoneParam; double* relativeComplexity; } x265_zone; - + /* data to calculate aggregate VMAF score */ typedef struct x265_vmaf_data { @@ -797,8 +797,8 @@ typedef struct x265_vmaf_framedata { int width; int height; - int frame_set; - int internalBitDepth; + int frame_set; + int internalBitDepth; void *reference_frame; /* points to fenc of particular frame */ void *distorted_frame; /* points to recon of particular frame */ }x265_vmaf_framedata; @@ -2198,8 +2198,8 @@ typedef struct x265_param /* * 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 + * picture structure 7 is signalled for frame doubling (PIC_STRUCT_DOUBLING) + * picture structure 8 is signalled for frame tripling (PIC_STRUCT_TRIPLING) * */ int bEnableFrameDuplication; @@ -2812,6 +2812,25 @@ static const char * const x265_api_query_errnames[] = { "libx265 has an invalid bitdepth" }; +enum PicStruct +{ + PIC_STRUCT_PROGRESSIVE_FRAME = 0, + PIC_STRUCT_FIELD_TOP = 1, + PIC_STRUCT_FIELD_BOTTOM = 2, + PIC_STRUCT_TOP_BOTTOM = 3, + PIC_STRUCT_BOTTOM_TOP = 4, + PIC_STRUCT_TOP_BOTTOM_TOP = 5, + PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, + PIC_STRUCT_DOUBLING = 7, + PIC_STRUCT_TRIPLING = 8, + PIC_STRUCT_TOP_PREVBOTTOM = 9, + PIC_STRUCT_BOTTOM_PREVTOP = 10, + PIC_STRUCT_TOP_NEXTBOTTOM = 11, + PIC_STRUCT_BOTTOM_NEXTTOP = 12, + PIC_STRUCT_COUNT, + PIC_STRUCT_AUTO = PIC_STRUCT_PROGRESSIVE_FRAME, +}; + #ifdef __cplusplus } #endif diff --git a/source/x265cli.cpp b/source/x265cli.cpp index b8d314332..b289b50a4 100755 --- a/source/x265cli.cpp +++ b/source/x265cli.cpp @@ -379,7 +379,8 @@ namespace X265_NS { H0(" --[no-]eos Emit end of sequence nal unit at the end of every coded video sequence. Default %s\n", OPT(param->bEnableEndOfSequence)); H1(" --hash Decoded Picture Hash SEI 0: disabled, 1: MD5, 2: CRC, 3: Checksum. Default %d\n", param->decodedPictureHashSEI); H0(" --atc-sei Emit the alternative transfer characteristics SEI message where the integer is the preferred transfer characteristic. Default disabled\n"); - H0(" --pic-struct Set the picture structure and emit it in the picture timing SEI message. Values in the range 0..12. See D.3.3 of the HEVC spec. for a detailed explanation.\n"); + H0(" --pic-struct Specify a unique picture structure to emit in every frames' picture timing SEI message. Values in the range 0..12. See D.3.3 of the HEVC spec. for a detailed explanation.\n"); + H0(" --psfile PicStruct file specifying the picture structure for some or all frames.\n"); H0(" --log2-max-poc-lsb Maximum of the picture order count\n"); H0(" --[no-]vui-timing-info Emit VUI timing information in the bitstream. Default %s\n", OPT(param->bEmitVUITimingInfo)); H0(" --[no-]vui-hrd-info Emit VUI HRD information in the bitstream. Default %s\n", OPT(param->bEmitVUIHRDInfo)); @@ -475,6 +476,9 @@ namespace X265_NS { if (qpfile) fclose(qpfile); qpfile = NULL; + if (psfile) + fclose(psfile); + psfile = NULL; if (zoneFile) fclose(zoneFile); zoneFile = NULL; @@ -803,6 +807,12 @@ namespace X265_NS { OPT("output-depth") /* handled above */; OPT("recon-y4m-exec") reconPlayCmd = optarg; OPT("svt") /* handled above */; + OPT("psfile") + { + this->psfile = x265_fopen(optarg, "rb"); + if (!this->qpfile) + x265_log_file(param, X265_LOG_ERROR, "%s psfile not found or error in opening ps file\n", optarg); + } OPT("qpfile") { this->qpfile = x265_fopen(optarg, "rb"); @@ -1115,6 +1125,42 @@ namespace X265_NS { return false; } + bool CLIOptions::parsePSFile(x265_picture &pic_org, int fieldOrder, bool frameFields) + { + int32_t num = -1; + uint32_t frameFieldCoding, pictureStructure; + + uint32_t validPicStructMask = 0x181; //progressive, doubling, tripling + if (fieldOrder > 0) + validPicStructMask = frameFields ? 0x78 : 0x1e06; /* D.2 field_seq_flag = 0 or 1 */ + + while (num < pic_org.poc) + { + uint32_t filePos = ftell(psfile); + int ret = fscanf(psfile, "%d %u%*[ \t]%u\n", &num, &frameFieldCoding, &pictureStructure); + + if (num > pic_org.poc || ret == EOF) + { + fseek(psfile, filePos, SEEK_SET); + break; + } + if (num < pic_org.poc && ret >= 1) + continue; + if (ret == 3 && pictureStructure < PIC_STRUCT_COUNT) + { + /* don't allow to change frame_field coding */ + if ((frameFieldCoding > 0) ^ (fieldOrder > 0)) + return 0; + if ((1 << pictureStructure) & validPicStructMask) + pic_org.picStruct = pictureStructure; + return 1; + } + if (ret < 3) + return 0; + } + return 1; + } + bool CLIOptions::parseQPFile(x265_picture &pic_org) { int32_t num = -1, qp, ret; diff --git a/source/x265cli.h b/source/x265cli.h index 6ac7d2ab1..1420d9da0 100644 --- a/source/x265cli.h +++ b/source/x265cli.h @@ -273,6 +273,7 @@ static const struct option long_options[] = { "info", no_argument, NULL, 0 }, { "no-info", no_argument, NULL, 0 }, { "zones", required_argument, NULL, 0 }, + { "psfile", required_argument, NULL, 0 }, { "qpfile", required_argument, NULL, 0 }, { "zonefile", required_argument, NULL, 0 }, { "no-zonefile-rc-init", no_argument, NULL, 0 }, @@ -416,6 +417,7 @@ static const struct option long_options[] = InputFile* input[MAX_VIEWS]; ReconFile* recon[MAX_LAYERS]; OutputFile* output; + FILE* psfile; FILE* qpfile; FILE* zoneFile; FILE* dolbyVisionRpu; /* File containing Dolby Vision BL RPU metadata */ @@ -464,6 +466,7 @@ static const struct option long_options[] = for (int i = 0; i < MAX_VIEWS; i++) inputfn[i] = NULL; output = NULL; + psfile = NULL; qpfile = NULL; zoneFile = NULL; dolbyVisionRpu = NULL; @@ -502,6 +505,7 @@ static const struct option long_options[] = bool parse(int argc, char **argv); bool parseZoneParam(int argc, char **argv, x265_param* globalParam, int zonefileCount); bool parseQPFile(x265_picture &pic_org); + bool parsePSFile(x265_picture &pic_org, int fieldOrder, bool frameFields); bool parseZoneFile(); int rpuParser(x265_picture * pic); bool parseScenecutAwareQpConfig();