diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst index 82040d1e6..1f967e63c 100755 --- a/doc/reST/cli.rst +++ b/doc/reST/cli.rst @@ -2536,14 +2536,26 @@ VUI fields must be manually specified. 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. + +.. 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 structure to specify in the framenumber's Picture Timing SEI. + It shall be compatible with the encoder configuration for the given sequence. .. option:: --video-signal-type-preset Specify combinations of color primaries, transfer characteristics, color matrix, range of luma and chroma signals, and chroma sample location. String format: [:] - + This has higher precedence than individual VUI parameters. If any individual VUI option is specified together with this, which changes the values set corresponding to the system-id or color-volume, it will be discarded. @@ -2657,7 +2669,7 @@ Bitstream options Picture Timing SEI messages providing timing information to the decoder. Default disabled - + .. option:: --hrd-concat, --no-hrd-concat Set concatenation flag for the first keyframe in the HRD buffering period SEI. This diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index cbda0ca65..e18930d89 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -32,7 +32,7 @@ option(NATIVE_BUILD "Target the build CPU" OFF) option(STATIC_LINK_CRT "Statically link C and C++ runtimes for release builds" OFF) mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD) # X265_BUILD must be incremented each time the public API is changed -set(X265_BUILD 217) +set(X265_BUILD 218) configure_file("${PROJECT_SOURCE_DIR}/x265.def.in" "${PROJECT_BINARY_DIR}/x265.def") configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in" diff --git a/source/abrEncApp.cpp b/source/abrEncApp.cpp index 146b28dff..dc9d5feab 100644 --- a/source/abrEncApp.cpp +++ b/source/abrEncApp.cpp @@ -763,6 +763,16 @@ 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..a2fbc98a5 100644 --- a/source/abrEncApp.h +++ b/source/abrEncApp.h @@ -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..a6d1a7566 100644 --- a/source/common/frame.cpp +++ b/source/common/frame.cpp @@ -58,7 +58,7 @@ Frame::Frame() m_addOnPrevChange = NULL; m_classifyFrame = false; m_fieldNum = 0; - m_picStruct = 0; + m_picStruct = PIC_STRUCT_AUTO; m_edgePic = NULL; m_gaussianPic = NULL; m_thetaPic = NULL; @@ -85,6 +85,12 @@ Frame::Frame() m_targetBitrate = 0; m_targetCrf = 0; m_targetQp = 0; + + /* HRD */ + m_duration = m_cpbDuration = 0; + m_cpbDelay = m_dpbDelay = 0; + m_displayPicCount = m_codedPicCount = 0; + m_timebase = 0.; } bool Frame::create(x265_param *param, float* quantOffsets) diff --git a/source/common/frame.h b/source/common/frame.h index 9916ba92e..f3f7dd6ba 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 timing for this frame */ + uint32_t m_duration; /* display duration [clock ticks] */ + uint32_t m_cpbDuration; /* lifetime in the CPB [clock ticks] */ + uint32_t m_cpbDelay; /* CPB removal delay [clock ticks] */ + uint32_t m_dpbDelay; /* DPB output delay [clock ticks] */ + int64_t m_displayPicCount; /* PTS in clock ticks */ + int64_t m_codedPicCount; /* DTS in clock ticks */ + double m_timebase; /* 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..049062c70 100644 --- a/source/common/lowres.cpp +++ b/source/common/lowres.cpp @@ -353,6 +353,11 @@ void Lowres::init(PicYuv* origPic, int poc, bool bEnableTemporalFilter) frameNum = poc; leadingBframes = 0; indB = 0; + + cpbDurationSecs = 0.; + dispDurationSecs = 0.; + dispPicCount = durationPicCount = 0; + memset(costEst, -1, sizeof(costEst)); memset(weightedCostDelta, 0, sizeof(weightedCostDelta)); @@ -372,8 +377,13 @@ void Lowres::init(PicYuv* origPic, int poc, bool bEnableTemporalFilter) for (int i = 0; i < bframes + 2; i++) intraMbs[i] = 0; if (origPic->m_param->rc.vbvBufferSize) + { for (int i = 0; i < X265_LOOKAHEAD_MAX + 1; i++) + { plannedType[i] = X265_TYPE_AUTO; + plannedCpbDuration[i] = origPic->m_param->fpsDenom / (double)origPic->m_param->fpsNum; + } + } /* downscale and generate 4 hpel planes for lookahead */ if (bEnableTemporalFilter) @@ -386,7 +396,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..13c2f230f 100644 --- a/source/common/lowres.h +++ b/source/common/lowres.h @@ -207,6 +207,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]; int indB; int bframes; @@ -222,6 +223,11 @@ struct Lowres : public ReferencePlanes double frameVariance; int* edgeInclined; + /* Vbv Lookahead & cuTree durations */ + double cpbDurationSecs; + double dispDurationSecs; + int64_t dispPicCount; + uint32_t durationPicCount; /* cutree intermediate data */ PicQPAdaptationLayer* pAQLayer; diff --git a/source/common/param.cpp b/source/common/param.cpp index 3ba88204a..6007c5bb6 100755 --- a/source/common/param.cpp +++ b/source/common/param.cpp @@ -1020,7 +1020,7 @@ int x265_param_parse(x265_param* p, const char* name, const char* value) else { float fps = (float)atof(value); - if (fps > 0 && fps <= INT_MAX / 1000) + if (fps > 0 && fps <= INT_MAX / 1000 && ceilf(fps) != floorf(fps)) { p->fpsNum = (int)(fps * 1000 + .5); p->fpsDenom = 1000; diff --git a/source/encoder/api.cpp b/source/encoder/api.cpp index 364f832f0..09f86adaa 100644 --- a/source/encoder/api.cpp +++ b/source/encoder/api.cpp @@ -1014,7 +1014,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 ad807262f..0064417d0 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 @@ -129,6 +130,7 @@ Encoder::Encoder() m_encodedFrameNum = 0; m_pocLast = -1; m_firstPts = 0; + m_clockTickCount = 0; m_bframeDelayTime = 0; m_prevReorderedPts[0] = m_prevReorderedPts[1] = 0; m_curEncoder = 0; @@ -217,9 +219,6 @@ 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; - for (uint32_t i = 0; i < DUP_BUFFER; i++) { m_dupBuffer[i] = (AdaptiveFrameDuplication*)x265_malloc(sizeof(AdaptiveFrameDuplication)); @@ -373,7 +372,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 +426,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)); @@ -1063,7 +1062,7 @@ void Encoder::updateVbvPlan(RateControl* rc) int64_t bits = m_param->rc.bEnableConstVbv ? (int64_t)frameSizePlan : (int64_t)X265_MAX(frameSizeEst, frameSizePlan); rc->m_bufferFill -= bits; rc->m_bufferFill = X265_MAX(rc->m_bufferFill, 0); - rc->m_bufferFill += encoder->m_rce.bufferRate; + rc->m_bufferFill += encoder->m_rce.bufferRate * encoder->m_rce.cpbDuration; rc->m_bufferFill = X265_MIN(rc->m_bufferFill, rc->m_bufferSize); if (rc->m_2pass) rc->m_predictedBits += bits; @@ -1565,13 +1564,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++; @@ -1757,7 +1756,26 @@ 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 >= PIC_STRUCT_AUTO) + 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 no HRD) */ + 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_clockTickCount; + + /* update presentation tick count */ + m_clockTickCount += inFrame[layer]->m_duration; /*Copy reconfigured RC parameters to frame*/ if (m_param->rc.rateControlMode == X265_RC_ABR) @@ -1892,9 +1910,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 { @@ -1969,7 +1987,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); @@ -2815,10 +2833,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); @@ -2888,7 +2911,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, @@ -3175,6 +3199,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); @@ -3188,6 +3214,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) @@ -3197,6 +3224,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) @@ -3206,6 +3234,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) @@ -3429,7 +3458,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 @@ -3735,7 +3764,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; @@ -4515,6 +4544,40 @@ 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 have a DeltaToDivisor = 1 and convey a full frame (or pair of fields) + 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. Ignoring the 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 (!m_param->bEmitHRDSEI) + { + x265_log(p, X265_LOG_WARNING, "Pic struct requires HRD Timing information. Disabling picture structure.\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"); @@ -4527,10 +4590,10 @@ void Encoder::configure(x265_param *p) p->bEnableFrameDuplication = 0; } - if (p->bEnableFrameDuplication && p->pictureStructure != 0 && p->pictureStructure != -1) + if (p->bEnableFrameDuplication && p->pictureStructure >= 0) { - 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, "Cannot enforce a picture structure with Frame-duplication. Ignoring specified picture structure.\n"); + p->pictureStructure = -1; } if (m_param->bEnableFrameDuplication && (!bIsVbv || !m_param->bEmitHRDSEI)) @@ -5212,7 +5275,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..2d308724b 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 { @@ -186,6 +188,7 @@ class Encoder : public x265_encoder int64_t m_bframeDelayTime; int64_t m_prevReorderedPts[2]; int64_t m_encodeStartTime; + uint64_t m_clockTickCount; /* Clock ticks elapsed since the first presentation */ int m_pocLast; // time index (POC) int m_encodedFrameNum; @@ -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; @@ -250,7 +252,7 @@ class Encoder : public x265_encoder /* For optimising slice QP */ Lock m_sliceQpLock; - int m_iFrameNum; + int m_iFrameNum; int m_iPPSQpMinus26; int64_t m_iBitsCostSum[QP_MAX_MAX + 1]; Lock m_sliceRefIdxLock; diff --git a/source/encoder/frameencoder.cpp b/source/encoder/frameencoder.cpp index fe2f3dbbd..553c63689 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; } } @@ -183,9 +182,8 @@ 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 != NULL; } if (m_param->noiseReductionIntra || m_param->noiseReductionInter) @@ -761,7 +759,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 +775,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) @@ -798,32 +793,31 @@ void FrameEncoder::compressFrame(int layer) SEIPictureTiming *sei = m_rce.picTimingSEI; const VUI *vui = &slice->m_sps->vuiParameters; const HRDInfo *hrd = &vui->hrdParameters; - int poc = slice->m_poc; if (vui->frameFieldInfoPresentFlag) { - if (m_param->interlaceMode > 0) + if (m_frame[layer]->m_picStruct == PIC_STRUCT_AUTO) { // field_seq_flag is 1, so each picture is a single field: 1 = top field, 2 = bottom field if( m_param->interlaceMode == 2 ) { if (m_param->bField) - sei->m_picStruct = (slice->m_fieldNum == 1) ? 2 /* bottom */ : 1 /* top */; + sei->m_picStruct = (slice->m_fieldNum == 1) ? PIC_STRUCT_FIELD_BOTTOM : PIC_STRUCT_FIELD_TOP; else - sei->m_picStruct = (poc & 1) ? 1 /* top */ : 2 /* bottom */; - } + sei->m_picStruct = (slice->m_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) ? 1 /* top */ : 2 /* bottom */; + sei->m_picStruct = (slice->m_fieldNum == 1) ? PIC_STRUCT_FIELD_TOP : PIC_STRUCT_FIELD_BOTTOM; else - sei->m_picStruct = (poc & 1) ? 2 /* bottom */ : 1 /* top */; + sei->m_picStruct = (slice->m_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; @@ -832,12 +826,11 @@ void FrameEncoder::compressFrame(int layer) 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(1u, m_frame[layer]->m_cpbDelay), (1u << hrd->cpbRemovalDelayLength)); + sei->m_picDpbOutputDelay = m_frame[layer]->m_dpbDelay; } sei->writeSEImessages(m_bs, *slice->m_sps, NAL_UNIT_PREFIX_SEI, m_nalList, m_param->bSingleSeiNal, layer); diff --git a/source/encoder/ratecontrol.cpp b/source/encoder/ratecontrol.cpp index 21fb9e086..ed4fc4089 100644 --- a/source/encoder/ratecontrol.cpp +++ b/source/encoder/ratecontrol.cpp @@ -166,6 +166,8 @@ inline void copyRceData(RateControlEntry* rce, RateControlEntry* rce2Pass) rce->qpNoVbv = rce2Pass->qpNoVbv; rce->newQp = rce2Pass->newQp; rce->qRceq = rce2Pass->qRceq; + rce->frameDuration = rce2Pass->frameDuration; + rce->cpbDuration = rce2Pass->cpbDuration; } } // end anonymous namespace @@ -199,6 +201,7 @@ RateControl::RateControl(x265_param& p, Encoder *top) m_partialResidualCost = 0; m_rateFactorMaxIncrement = 0; m_rateFactorMaxDecrement = 0; + m_timebase = (double)m_param->fpsDenom / m_param->fpsNum; m_fps = (double)m_param->fpsNum / m_param->fpsDenom; m_startEndOrder.set(0); m_bTerminated = false; @@ -235,7 +238,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; @@ -405,9 +407,9 @@ 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)) + 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 +422,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,6 +461,7 @@ 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; @@ -492,7 +495,7 @@ 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; + 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') @@ -743,6 +748,8 @@ bool RateControl::init(const SPS& sps) p = next; } X265_FREE(statsBuf); + m_durationDone = totalDuration; + if (m_param->rc.rateControlMode != X265_RC_CQP) { m_start = 0; @@ -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; @@ -941,7 +948,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 @@ -952,11 +958,14 @@ bool RateControl::analyseABR2Pass(uint64_t allAvailableBits) double cplxSum = 0; double weight = 1.0; double gaussianWeight; + /* weighted average of cplx of future frames */ for (int j = 1; j < cplxBlur * 2 && j < m_numEntries - i; j++) { 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 +979,7 @@ 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 +1095,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 * m_timebase * 1000.), avgq); if (expectedBits < allAvailableBits && avgq < m_param->rc.qpMin + 2) { @@ -1115,7 +1125,7 @@ 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 * m_timebase); int startIndex, endIndex; int fps = X265_MIN(m_param->keyframeMax, (int)(m_fps + 0.5)); int distance = fps << 1; @@ -1138,7 +1148,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_timebase * m_durationDone))); return false; } if (!analyseABR2Pass(allAvailableBits)) @@ -1184,9 +1194,10 @@ 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++; } @@ -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,19 +1544,21 @@ 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; return m_qp; } else - { + { int index = m_encOrder[rce->poc]; + double totalDuration = m_timebase * rce->frameDuration; index++; - double totalDuration = m_frameDuration; for (int j = 0; totalDuration < 1.0 && index < m_numEntries; j++) { - switch (m_rce2Pass[index].sliceType) + RateControlEntry *rceLoop = &m_rce2Pass[index]; + switch (rceLoop->sliceType) { case B_SLICE: - curFrame->m_lowres.plannedType[j] = m_rce2Pass[index].keptAsRef ? X265_TYPE_BREF : X265_TYPE_B; + curFrame->m_lowres.plannedType[j] = rceLoop->keptAsRef ? X265_TYPE_BREF : X265_TYPE_B; break; case P_SLICE: curFrame->m_lowres.plannedType[j] = X265_TYPE_P; @@ -1555,8 +1570,9 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode break; } - curFrame->m_lowres.plannedSatd[j] = m_rce2Pass[index].currentSatd; - totalDuration += m_frameDuration; + curFrame->m_lowres.plannedSatd[j] = rceLoop->currentSatd; + curFrame->m_lowres.plannedCpbDuration[j] = rceLoop->cpbDuration * m_timebase; + totalDuration += m_timebase * rceLoop->frameDuration; index++; } } @@ -1619,7 +1635,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 +1671,7 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode } } m_framesDone++; + m_durationDone += rce->frameDuration; return m_qp; } @@ -1764,8 +1781,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->cpbDuration * m_bufferRate - + qScale2bits(rce, rce->newQScale)) * parity; fill = x265_clip3(0.0, m_bufferSize, fill); fills[i] = fill; if (fill <= bufferMin || i == 0) @@ -1865,16 +1883,17 @@ 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; + /* approximate, assume the other threads have a frame duration of a single tick */ + double timeDone = (double)(m_durationDone - m_param->frameNumThreads + 1) * m_timebase; double wantedBits = timeDone * m_bitrate; int64_t encodedBits = m_totalBits; if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps) { - 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)); @@ -2140,9 +2159,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 * m_timebase * rce->frameDuration); else - m_predictedBits += (int64_t)(m_param->frameNumThreads * m_bitrate / m_fps); + m_predictedBits += (int64_t)(m_param->frameNumThreads * m_bitrate * m_timebase * rce->frameDuration); } /* Adjust ABR buffer based on distance to the end of the video. */ if (m_numEntries > rce->encodeOrder) @@ -2206,7 +2225,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 +2238,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 +2270,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; @@ -2417,7 +2436,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); @@ -2483,6 +2502,7 @@ void RateControl::rateControlUpdateStats(RateControlEntry* rce) void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone) { double abrBuffer = 2 * m_rateTolerance * m_bitrate; + double frameDurSecs = rce->frameDuration * m_timebase; // Check if current Slice is a scene cut that follows low detailed/blank frames if (rce->lastSatd > 4 * rce->movingAvgSum || rce->scenecut || rce->isFadeEnd) @@ -2491,7 +2511,7 @@ void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone) && (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 * frameDurSecs); 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 +2524,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((frameDurSecs)) / BASE_FRAME_DURATION); m_shortTermCplxCount = 1; m_isAbrReset = true; m_lastAbrResetPoc = rce->poc; @@ -2573,11 +2593,11 @@ 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; + double segDur = m_param->keyframeMax * m_timebase; double allowedSize = m_vbvMaxRate * segDur; double remDur = segDur - totalDuration; double remainingBits = frameBitsTotal; @@ -2635,7 +2655,7 @@ 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 totalDuration = rce->cpbDuration * m_timebase; 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; @@ -2646,8 +2666,8 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q) 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; + totalDuration += curFrame->m_lowres.plannedCpbDuration[j]; + double wantedFrameSize = m_vbvMaxRate * curFrame->m_lowres.plannedCpbDuration[j]; if (bufferFillCur + wantedFrameSize <= m_bufferSize) bufferFillCur += wantedFrameSize; int64_t satd = curFrame->m_lowres.plannedSatd[j] >> (X265_DEPTH - 8); @@ -2726,7 +2746,7 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q) // For small VBVs, allow the frame to use up the entire VBV. double maxFillFactor; - maxFillFactor = m_bufferSize >= 5 * m_bufferRate ? 2 : 1; + maxFillFactor = (m_bufferSize >= 5 * m_bufferRate * rce->cpbDuration) ? 2 : 1; // For single-frame VBVs, request that the frame use up the entire VBV. double minFillFactor = m_singleFrameVbv ? 1 : 2; @@ -2737,8 +2757,8 @@ double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q) qf = x265_clip3(0.2, 1.0, m_bufferFill / (maxFillFactor * bits)); q /= qf; bits *= qf; - if (bits < m_bufferRate / minFillFactor) - q *= bits * minFillFactor / m_bufferRate; + if (bits < m_bufferRate * rce->cpbDuration / minFillFactor) + q *= bits * minFillFactor / (m_bufferRate * rce->cpbDuration); bits = predictSize(&m_pred[m_predType], q, (double)m_currentSatd); } @@ -2923,7 +2943,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 +2962,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 +2991,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 * maxFrameError * rce->cpbDuration) || (rce->frameSizeMaximum - accFrameBits < rce->frameSizeMaximum * maxFrameError))) { qpVbv += stepSize; @@ -3003,7 +3023,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; @@ -3020,9 +3040,7 @@ 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); + q = pow(BASE_FRAME_DURATION / CLIP_DURATION((rce->frameDuration * m_timebase)), 1 - m_param->rc.qCompress); } else q = pow(rce->blurredComplexity, 1 - m_param->rc.qCompress); @@ -3078,7 +3096,8 @@ int RateControl::updateVbv(int64_t bits, RateControlEntry* rce) x265_log(m_param, X265_LOG_WARNING, "poc:%d, VBV underflow (%.0f bits)\n", rce->poc, (double)m_bufferFillFinal); m_bufferFillFinal = X265_MAX(double(m_bufferFillFinal), 0.0); - m_bufferFillFinal = double(m_bufferFillFinal) + rce->bufferRate; + double bufferFillThisAU = rce->bufferRate * rce->cpbDuration; + m_bufferFillFinal = double(m_bufferFillFinal) + bufferFillThisAU; if (m_param->csvLogLevel >= 2) m_unclippedBufferFillFinal = m_bufferFillFinal; @@ -3090,14 +3109,14 @@ int RateControl::updateVbv(int64_t bits, RateControlEntry* rce) 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 +3244,12 @@ 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 +3281,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 +3295,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 +3314,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 +3335,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 +3348,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. */ @@ -3522,14 +3517,14 @@ 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); + uint32_t maxWindowSize = uint32_t((m_param->fwdMaxScenecutWindow / 1000.0) * m_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) * m_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..c31073525 100644 --- a/source/encoder/ratecontrol.h +++ b/source/encoder/ratecontrol.h @@ -57,13 +57,6 @@ struct Predictor double offset; }; -struct HRDTiming -{ - double cpbInitialAT; - double cpbFinalAT; - double dpbOutputTime; - double cpbRemovalTime; -}; struct RateControlEntry { @@ -89,8 +82,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 +109,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 +145,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 +171,7 @@ class RateControl int m_qpConstant[3]; int m_lastNonBPictType; int m_framesDone; /* # of frames passed through RateCotrol already */ + uint64_t m_durationDone; /* duration passed through RateCotrol already, in ticks */ int64_t m_iBits; double m_cplxrSum; /* sum of bits*qscale/rceq */ AtomicDouble m_wantedBitsWindow; /* target bitrate * window */ @@ -199,7 +191,8 @@ class RateControl double m_movingSumComplexitySeg[3]; int m_frameCountSeg[3]; double m_segDur; - double m_fps; + double m_fps; /* only a true framerate for CFR content */ + double m_timebase; int64_t m_satdCostWindow[50]; int64_t m_encodedBitsWindow[50]; int m_sliderPos; @@ -233,8 +226,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 f06eb6fe1..e0d563dd2 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,15 @@ Lookahead::Lookahead(x265_param *param, ThreadPool* pool) m_origPicBuf = 0; m_metld = NULL; + m_codedPicCount = 0; + m_cpbDelay = 0; + /* 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 || @@ -1148,6 +1154,8 @@ Lookahead::Lookahead(x265_param *param, ThreadPool* pool) m_countTemporalFilter = 0; #endif + memset(m_frameVariance, 0, sizeof(m_frameVariance)); + m_accHistDiffRunningAvgCb = X265_MALLOC(uint32_t*, NUMBER_OF_SEGMENTS_IN_WIDTH * sizeof(uint32_t*)); m_accHistDiffRunningAvgCb[0] = X265_MALLOC(uint32_t, NUMBER_OF_SEGMENTS_IN_WIDTH * NUMBER_OF_SEGMENTS_IN_HEIGHT); memset(m_accHistDiffRunningAvgCb[0], 0, sizeof(uint32_t) * NUMBER_OF_SEGMENTS_IN_WIDTH * NUMBER_OF_SEGMENTS_IN_HEIGHT); @@ -1985,6 +1993,8 @@ void Lookahead::slicetypeDecide() PreLookaheadGroup pre(*this); Lowres* frames[X265_LOOKAHEAD_MAX + X265_BFRAME_MAX + 4]; Frame* list[X265_BFRAME_MAX + 4]; + Frame* refLowres[X265_LOOKAHEAD_MAX + X265_BFRAME_MAX + 4]; + memset(refLowres, 0, sizeof(refLowres)); memset(frames, 0, sizeof(frames)); memset(list, 0, sizeof(list)); int maxSearch = X265_MIN(m_param->lookaheadDepth, X265_LOOKAHEAD_MAX); @@ -2019,7 +2029,9 @@ void Lookahead::slicetypeDecide() for (j = 0; j < maxSearch; j++) { if (!curFrame) break; + setDurationsToLowres(curFrame); frames[j + 1] = &curFrame->m_lowres; + refLowres[j] = curFrame; if (!curFrame->m_lowresInit) pre.m_preframes[pre.m_jobTotal++] = curFrame; @@ -2051,7 +2063,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; @@ -2093,6 +2105,12 @@ void Lookahead::slicetypeDecide() m_param->rc.cuTree || m_param->scenecutThreshold || m_param->bHistBasedSceneCut || (m_param->lookaheadDepth && m_param->rc.vbvBufferSize))) { + /* post-poned because the lowres struct gets overwritten by the prelookahead */ + for (int j = 0; j < maxSearch && refLowres[j]; j++) + { + setDurationsToLowres(refLowres[j]); + } + if (!m_param->rc.bStatRead) slicetypeAnalyse(frames, false); bool bIsVbv = m_param->rc.vbvBufferSize > 0 && m_param->rc.vbvMaxBitrate > 0; @@ -2116,6 +2134,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; @@ -2135,8 +2158,8 @@ 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) @@ -2164,11 +2187,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 @@ -2183,7 +2207,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) @@ -2227,12 +2252,15 @@ void Lookahead::slicetypeDecide() for (bframes = 0, brefs = 0;; bframes++) { Lowres& frm = list[bframes]->m_lowres; + int64_t keyintTicksDelta = (int64_t)list[bframes]->m_displayPicCount - m_lastKeyframe + 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) && keyintTicksDelta > m_param->keyframeMin) || (frm.frameNum == (m_param->chunkStart - 1)) || (frm.frameNum == m_param->chunkEnd)) { - m_lastKeyframe = frm.frameNum; + m_lastKeyframe = list[bframes]->m_displayPicCount; + m_lastKeyframeNum = frm.frameNum; frm.bKeyframe = true; } if (!IS_X265_TYPE_B(frm.sliceType)) @@ -2304,6 +2332,9 @@ void Lookahead::slicetypeDecide() if (m_param->bEnableTemporalSubLayers > 2) { + int codedFrameOrderedIndex[X265_BFRAME_MAX+1]; + int codedCount = 0; /* running index into codedFrameOrderedIndex across all (sub) mini-GOPs */ + //Split the partial mini GOP into sub mini GOPs when temporal sub layers are enabled if (bframes < m_param->bframes) { @@ -2375,6 +2406,7 @@ void Lookahead::slicetypeDecide() int idx = 0; /* add non-B to output queue */ + codedFrameOrderedIndex[codedCount++] = newbFrames; list[newbFrames]->m_reorderedPts = pts[idx++]; list[newbFrames]->m_gopOffset = 0; list[newbFrames]->m_gopId = gopId; @@ -2390,10 +2422,10 @@ void Lookahead::slicetypeDecide() continue; // Assign gop offset and temporal layer of frames + codedFrameOrderedIndex[codedCount++] = offset; list[offset]->m_gopOffset = j; - list[bframes]->m_gopId = gopId; + list[offset]->m_gopId = gopId; list[offset]->m_tempLayer = x265_gop_ra[gopId][j++].layer; - list[offset]->m_reorderedPts = pts[idx++]; m_outputQueue.pushBack(*list[offset]); i++; @@ -2458,6 +2490,7 @@ void Lookahead::slicetypeDecide() m_inputLock.release(); m_lastNonB = &list[newbFrames]->m_lowres; + codedFrameOrderedIndex[codedCount++] = newbFrames; list[newbFrames]->m_reorderedPts = pts[idx++]; list[newbFrames]->m_gopOffset = 0; list[newbFrames]->m_gopId = -1; @@ -2469,6 +2502,7 @@ void Lookahead::slicetypeDecide() { if (list[i]->m_lowres.sliceType == X265_TYPE_BREF) { + codedFrameOrderedIndex[codedCount++] = i; list[i]->m_reorderedPts = pts[idx++]; list[i]->m_gopOffset = 0; list[i]->m_gopId = -1; @@ -2484,6 +2518,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[codedCount++] = i; list[i]->m_reorderedPts = pts[idx++]; list[i]->m_gopOffset = 0; list[i]->m_gopId = -1; @@ -2546,6 +2581,7 @@ void Lookahead::slicetypeDecide() int idx = 0; /* add non-B to output queue */ + codedFrameOrderedIndex[codedCount++] = bframes; list[bframes]->m_reorderedPts = pts[idx++]; list[bframes]->m_gopOffset = 0; list[bframes]->m_gopId = m_gopId; @@ -2565,12 +2601,24 @@ void Lookahead::slicetypeDecide() list[offset]->m_tempLayer = x265_gop_ra[m_gopId][j++].layer; /* add B frames to output queue */ + codedFrameOrderedIndex[codedCount++] = offset; list[offset]->m_reorderedPts = pts[idx++]; m_outputQueue.pushBack(*list[offset]); i++; } } + /* Compute HRD CpbDpb delays */ + { + Frame *prevFrame = NULL; + X265_CHECK(codedCount == bframes + 1, "coded order covers %d of %d frames\n", codedCount, bframes + 1); + for (int i = 0; i < codedCount; ++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)) { @@ -2580,12 +2628,13 @@ 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(); - frames[j + 1] = NULL; + if (!m_param->rc.bStatRead) slicetypeAnalyse(frames, true); bool bIsVbv = m_param->rc.vbvBufferSize > 0 && m_param->rc.vbvMaxBitrate > 0; @@ -2675,6 +2724,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]; + int codedFrameOrderedIndex[X265_BFRAME_MAX + 1]; for (int i = 0; i <= bframes; i++) { Frame *curFrame; @@ -2686,8 +2736,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]); @@ -2698,6 +2749,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]); } @@ -2710,11 +2762,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)) @@ -2725,12 +2787,13 @@ 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(); - frames[j + 1] = NULL; + if (!m_param->rc.bStatRead) slicetypeAnalyse(frames, true); bool bIsVbv = m_param->rc.vbvBufferSize > 0 && m_param->rc.vbvMaxBitrate > 0; @@ -2751,6 +2814,59 @@ void Lookahead::slicetypeDecide() } } +void Lookahead::calculateDurations(Frame *frame, Frame *prevFrame) +{ + frame->m_cpbDelay = m_cpbDelay; + frame->m_cpbDuration = frame->m_duration; + frame->m_codedPicCount = m_codedPicCount; + + int dpbDelay = (int)((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[m_sps->maxTempSubLayers - 1]; + + if (dpbDelay < 0) + { + frame->m_cpbDelay += dpbDelay; + frame->m_dpbDelay = 0; + + /* Bref and next B-frame cpbRemovalDelay can be equal on long sequence of doubling or tripling. + * larger m_dpbDelay 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_dpbDelay += 1; + } + } + else + { + frame->m_dpbDelay = (uint32_t)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; @@ -2770,6 +2886,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) @@ -2777,7 +2894,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]->cpbDurationSecs; } } idx++; @@ -2807,8 +2925,9 @@ 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; - /* Save the nextB Cost in each B frame of the current miniGop */ + 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++) { if (curBRef && curBRef == i) @@ -2816,7 +2935,8 @@ 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; @@ -2852,12 +2972,23 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) int resetStart; bool bIsVbvLookahead = m_param->rc.vbvBufferSize && m_param->lookaheadDepth; + uint32_t keyintTimeLimit = m_param->keyframeMax + (int)(m_lastKeyframe - (frames[0]->dispPicCount + frames[0]->durationPicCount)); + uint32_t extendedKeyIntTime = keyintTimeLimit + m_param->gopLookahead; + + int keyIntFrameCnt = 0, keyIntFrameCntExtended = 0; + uint32_t 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; + if (durationAhead <= extendedKeyIntTime) + ++keyIntFrameCntExtended; } if (!framecnt && m_param->analysisLoadReuseLevel != 1) @@ -2890,19 +3021,18 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) 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)) + int chunkStart = (m_param->chunkStart - m_lastKeyframeNum - 1); + int chunkEnd = (m_param->chunkEnd - m_lastKeyframeNum); + if ((chunkStart > 0) && (chunkStart < keylimit)) keylimit = chunkStart; - else if ((chunkEnd > 0) && (chunkEnd < m_param->keyframeMax)) + else if ((chunkEnd > 0) && (chunkEnd < keylimit)) keylimit = chunkEnd; } - int keyFrameLimit = keylimit + m_lastKeyframe - frames[0]->frameNum - 1; - if (m_param->gopLookahead && keyFrameLimit <= m_param->bframes + 1) - keyintLimit = keyFrameLimit + m_param->gopLookahead; + if (m_param->gopLookahead && keyIntFrameCnt <= m_param->bframes + 1) + keyintLimit = keyIntFrameCntExtended; else - keyintLimit = keyFrameLimit; + keyintLimit = keyIntFrameCnt; origNumFrames = numFrames = m_param->bIntraRefresh ? framecnt : X265_MIN(framecnt, keyintLimit); @@ -3004,7 +3134,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; @@ -3110,7 +3240,19 @@ 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 nextIRAP = -1; + int minDiff = 1 << 30; + for (int j = numFrames; j > 0; --j) + { + int diff = (int)(frames[j]->dispPicCount - m_lastKeyframe) + (int)frames[j]->durationPicCount; + if (diff >= m_param->keyframeMax && diff < minDiff) + { + minDiff = diff; + nextIRAP = j; + } + } + /* radl in frame counts: frame preceeding RADL in POC order*/ + int preRADL = nextIRAP > 0 ? frames[nextIRAP]->frameNum - radl - 1 : -1; if (bForceRADL && (frames[0]->frameNum == preRADL) && !bLastMiniGop) { int j = 1; @@ -3147,15 +3289,22 @@ void Lookahead::slicetypeAnalyse(Lowres **frames, bool bKeyframe) if (m_param->rc.cuTree) cuTree(frames, X265_MIN(numFrames, m_param->keyframeMax), bKeyframe); - if (m_param->gopLookahead && (keyFrameLimit >= 0) && (keyFrameLimit <= m_param->bframes + 1) && !m_extendGopBoundary) - keyintLimit = keyFrameLimit; + if (m_param->gopLookahead && (keyIntFrameCnt >= 0) && (keyIntFrameCnt <= m_param->bframes + 1) && !m_extendGopBoundary) + keyintLimit = keyIntFrameCnt; if (!m_param->bIntraRefresh) - for (int j = keyintLimit + 1; j <= numFrames; j += m_param->keyframeMax) + { + uint32_t duration = 0; + for (int j = keyintLimit + 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); @@ -3287,7 +3436,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 gopSize = (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); @@ -3672,7 +3821,7 @@ void Lookahead::cuTree(Lowres **frames, int numframes, bool bIntra) x265_emms(); double totalDuration = 0.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); @@ -3779,7 +3928,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) @@ -3876,7 +4025,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; diff --git a/source/encoder/slicetype.h b/source/encoder/slicetype.h index 36fbeb457..6202778e5 100644 --- a/source/encoder/slicetype.h +++ b/source/encoder/slicetype.h @@ -169,7 +169,8 @@ class Lookahead : public JobProvider /* pre-lookahead */ int m_fullQueueSize; - int m_lastKeyframe; + int64_t m_lastKeyframe; + int m_lastKeyframeNum; int m_8x8Width; int m_8x8Height; int m_8x8Blocks; @@ -211,7 +212,11 @@ class Lookahead : public JobProvider OrigPicBuffer* m_origPicBuf; MotionEstimatorTLD* m_metld; - Lookahead(x265_param *param, ThreadPool *pool); + SPS *m_sps; /* for maximum picture re-ordering setting */ + uint32_t m_cpbDelay; /* latest cpb delay in lookahead (ticks) */ + int64_t m_codedPicCount; /* latest coded picture count in lookahead (ticks) */ + + Lookahead(x265_param *param, ThreadPool *pool, SPS* sps); #if DETAILED_CU_STATS int64_t m_slicetypeDecideElapsedTime; int64_t m_preLookaheadElapsedTime; @@ -257,6 +262,10 @@ 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); + + void calculateDurations(Frame *frame, Frame *prevFrame); + void setDurationsToLowres(Frame *frame); + int64_t vbvFrameCost(Lowres **frames, int p0, int p1, int b); void vbvLookahead(Lowres **frames, int numFrames, int keyframes); void aqMotion(Lowres **frames, bool bintra); diff --git a/source/x265.h b/source/x265.h index 54ed2a2fc..44470a669 100644 --- a/source/x265.h +++ b/source/x265.h @@ -1698,17 +1698,17 @@ typedef struct x265_param * Default is 0, which is recommended */ int crQpOffset; - /* Specifies the preferred transfer characteristics syntax element in the - * alternative transfer characteristics SEI message (see. D.2.38 and D.3.38 of - * JCTVC-W1005 http://phenix.it-sudparis.eu/jct/doc_end_user/documents/23_San%20Diego/wg11/JCTVC-W1005-v4.zip - * */ - int preferredTransferCharacteristics; - - /* - * Specifies the value for the pic_struc syntax element of the picture timing SEI message (See D2.3 and D3.3) - * of the HEVC spec. for a detailed explanation - * */ - int pictureStructure; + /* Specifies the preferred transfer characteristics syntax element in the + * alternative transfer characteristics SEI message (see. D.2.38 and D.3.38 of + * JCTVC-W1005 http://phenix.it-sudparis.eu/jct/doc_end_user/documents/23_San%20Diego/wg11/JCTVC-W1005-v4.zip + * */ + int preferredTransferCharacteristics; + + /* + * Specifies the value for the pic_struc syntax element of the picture timing SEI message (See D2.3 and D3.3) + * of the HEVC spec. for a detailed explanation + * */ + int pictureStructure; struct { @@ -2199,8 +2199,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; @@ -2813,6 +2813,27 @@ static const char * const x265_api_query_errnames[] = { "libx265 has an invalid bitdepth" }; +/* Picture Structure conveyed in Pic Timing SEI. + * The enumeration is defined in H.265 Table D.2 */ +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 0b1a2b63c..7e3665874 100755 --- a/source/x265cli.cpp +++ b/source/x265cli.cpp @@ -66,7 +66,7 @@ namespace X265_NS { H0("\nInput Options:\n"); H0(" --input Raw YUV or Y4M input file name. `-` for stdin\n"); H1(" --y4m Force parsing of input stream as YUV4MPEG2 regardless of file extension\n"); - H0(" --fps Source frame rate (float or num/denom), auto-detected if Y4M\n"); + H0(" --fps Source frame rate (float or num/denom), auto-detected if Y4M. Defines the bitstream timescale.\n"); H0(" --input-res WxH Source picture size [w x h], auto-detected if Y4M\n"); H1(" --input-depth Bit-depth of input file. Default 8\n"); H1(" --input-csp Chroma subsampling, auto-detected if Y4M\n"); @@ -379,7 +379,11 @@ 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(" Format of each line: framenum ffo picstruct.\n"); + H0(" ffo is the frame field order (0: progressive, 1: bff, 2: tff) and shall match the encode settings.\n"); + H0(" picstruct value, from H.265 Table D.2 enumeration, shall be compatible with the encode settings.\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)); @@ -472,6 +476,9 @@ namespace X265_NS { recon[i]->release(); recon[i] = NULL; } + if (psfile) + fclose(psfile); + psfile = NULL; if (qpfile) fclose(qpfile); qpfile = NULL; @@ -803,6 +810,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->psfile) + 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"); @@ -1120,6 +1133,46 @@ namespace X265_NS { return false; } + bool CLIOptions::parsePSFile(x265_picture &pic_org, int fieldOrder, bool fieldSequence) + { + int32_t num = -1; + uint32_t frameFieldCoding, pictureStructure; + + uint32_t validPicStructMask = 0x181; /* progressive, doubling, tripling */ + if (fieldOrder > 0) + validPicStructMask = fieldSequence ? 0x1e06 : 0x78; /* D.2 field_seq_flag = 1 or 0 */ + + while (num < pic_org.poc) + { + uint32_t filePos = ftell(psfile); + int32_t 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; + else + x265_log(NULL, X265_LOG_WARNING, "Ignored structure for pic %u: not compatible with %s.\n", + num, (!fieldOrder ? "progressive" : (fieldSequence ? "field sequences" : "interlaced"))); + return 1; + } + if (ret < 3) + return 0; + } + /* not changed, use frame's initial value (default) */ + 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..50e6b8c56 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 }, @@ -339,14 +340,14 @@ static const struct option long_options[] = { "refine-analysis-type", required_argument, NULL, 0 }, { "copy-pic", no_argument, NULL, 0 }, { "no-copy-pic", no_argument, NULL, 0 }, - { "max-ausize-factor", required_argument, NULL, 0 }, + { "max-ausize-factor", required_argument, NULL, 0 }, { "idr-recovery-sei", no_argument, NULL, 0 }, { "no-idr-recovery-sei", no_argument, NULL, 0 }, - { "single-sei", no_argument, NULL, 0 }, - { "no-single-sei", no_argument, NULL, 0 }, - { "atc-sei", required_argument, NULL, 0 }, - { "pic-struct", required_argument, NULL, 0 }, - { "nalu-file", required_argument, NULL, 0 }, + { "single-sei", no_argument, NULL, 0 }, + { "no-single-sei", no_argument, NULL, 0 }, + { "atc-sei", required_argument, NULL, 0 }, + { "pic-struct", required_argument, NULL, 0 }, + { "nalu-file", required_argument, NULL, 0 }, { "dolby-vision-rpu", required_argument, NULL, 0 }, { "hrd-concat", no_argument, NULL, 0}, { "no-hrd-concat", 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; @@ -501,6 +504,7 @@ static const struct option long_options[] = void printStatus(uint32_t frameNum); bool parse(int argc, char **argv); bool parseZoneParam(int argc, char **argv, x265_param* globalParam, int zonefileCount); + bool parsePSFile(x265_picture &pic_org, int fieldOrder, bool fieldSequence); bool parseQPFile(x265_picture &pic_org); bool parseZoneFile(); int rpuParser(x265_picture * pic);