[x265] [PATCH] SEI: Insert buffering period and picture timing SEI messages

kavitha at multicorewareinc.com kavitha at multicorewareinc.com
Tue Jun 3 15:26:43 CEST 2014


# HG changeset patch
# User Kavitha Sampath <kavitha at multicorewareinc.com>
# Date 1401798795 -19800
#      Tue Jun 03 18:03:15 2014 +0530
# Node ID f9f553c8bd6aa17a3bb3a5025b99e5ccd748f8e7
# Parent  d78f38b707babf597d55859f620911410a25b19d
SEI: Insert buffering period and picture timing SEI messages

The buffering period SEI message is inserted for every key frame and the
picture timing SEI is inserted for every frame. The commit also computes the
HRD parameters as well as HRD Timing parameters that are calculated using the
delay components(cpb removal delay and dpb output delay) carried in these
SEI messages. HRD parameters can be signalled by enabling --hrd.

diff -r d78f38b707ba -r f9f553c8bd6a doc/reST/cli.rst
--- a/doc/reST/cli.rst	Tue Jun 03 16:15:32 2014 +0530
+++ b/doc/reST/cli.rst	Tue Jun 03 18:03:15 2014 +0530
@@ -858,6 +858,13 @@
 	to keep the stream headers for you and you want keyframes to be
 	random access points. Default disabled
 
+.. option:: --hrd, --no-hrd
+
+	Enable the signalling of HRD parameters to the decoder. The HRD
+	parameters are carried by the Buffering Period SEI messages and
+	Picture Timing SEI messages providing timing information to the
+	decoder. Default disabled
+
 .. option:: --aud, --no-aud
 
 	Emit an access unit delimiter NAL at the start of each slice access
diff -r d78f38b707ba -r f9f553c8bd6a source/CMakeLists.txt
--- a/source/CMakeLists.txt	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/CMakeLists.txt	Tue Jun 03 18:03:15 2014 +0530
@@ -19,7 +19,7 @@
 include(CheckCXXCompilerFlag)
 
 # X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 21)
+set(X265_BUILD 22)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r d78f38b707ba -r f9f553c8bd6a source/Lib/TLibCommon/SEI.h
--- a/source/Lib/TLibCommon/SEI.h	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/Lib/TLibCommon/SEI.h	Tue Jun 03 18:03:15 2014 +0530
@@ -185,14 +185,7 @@
 
     PayloadType payloadType() const { return PICTURE_TIMING; }
 
-    SEIPictureTiming()
-        : m_picStruct(0)
-        , m_sourceScanType(0)
-        , m_duplicateFlag(false)
-        , m_picDpbOutputDuDelay(0)
-        , m_numNalusInDuMinus1(NULL)
-        , m_duCpbRemovalDelayMinus1(NULL)
-    {}
+    SEIPictureTiming() {}
 
     virtual ~SEIPictureTiming()
     {
diff -r d78f38b707ba -r f9f553c8bd6a source/Lib/TLibCommon/TComPic.h
--- a/source/Lib/TLibCommon/TComPic.h	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/Lib/TLibCommon/TComPic.h	Tue Jun 03 18:03:15 2014 +0530
@@ -45,6 +45,7 @@
 #include "lowres.h"
 #include "threading.h"
 #include "md5.h"
+#include "SEI.h"
 
 namespace x265 {
 // private namespace
@@ -120,6 +121,8 @@
     double                m_avgQpAq;    // avg QP as decided by AQ in addition to ratecontrol
     double                m_rateFactor; // calculated based on the Frame QP
     int32_t               m_forceqp;    // Force to use the qp specified in qp file
+    SEIPictureTiming      m_sei;
+    HRDTiming             m_hrdTiming;
 
     TComPic();
     virtual ~TComPic();
diff -r d78f38b707ba -r f9f553c8bd6a source/Lib/TLibCommon/TComSlice.h
--- a/source/Lib/TLibCommon/TComSlice.h	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/Lib/TLibCommon/TComSlice.h	Tue Jun 03 18:03:15 2014 +0530
@@ -460,6 +460,14 @@
     void setNumTicksPocDiffOneMinus1(int x)     { m_numTicksPocDiffOneMinus1 = x; }
 };
 
+struct HRDTiming
+{
+    double cpbInitialAT;
+    double cpbFinalAT;
+    double dpbOutputTime;
+    double cpbRemovalTime;
+};
+
 class TComVPS
 {
 private:
diff -r d78f38b707ba -r f9f553c8bd6a source/common/param.cpp
--- a/source/common/param.cpp	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/common/param.cpp	Tue Jun 03 18:03:15 2014 +0530
@@ -623,6 +623,7 @@
     OPT("hash") p->decodedPictureHashSEI = atoi(value);
     OPT("aud") p->bEnableAccessUnitDelimiters = atobool(value);
     OPT("b-pyramid") p->bBPyramid = atobool(value);
+    OPT("hrd") p->bEmitHRDSEI = atobool(value);
     OPT("aq-mode") p->rc.aqMode = atoi(value);
     OPT("aq-strength") p->rc.aqStrength = atof(value);
     OPT("vbv-maxrate") p->rc.vbvMaxBitrate = atoi(value);
diff -r d78f38b707ba -r f9f553c8bd6a source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/encoder/encoder.cpp	Tue Jun 03 18:03:15 2014 +0530
@@ -186,6 +186,7 @@
             }
         }
     }
+    m_rateControl->init(&m_frameEncoder[0].m_sps);
     m_lookahead->init();
     m_encodeStartTime = x265_mdate();
     m_totalFrameThreads = param->frameNumThreads;
@@ -1123,8 +1124,8 @@
     vui->setFrameFieldInfoPresentFlag(!!param->interlaceMode);
     vui->setFieldSeqFlag(!!param->interlaceMode);
 
-    vui->setHrdParametersPresentFlag(false);
-    vui->getHrdParameters()->setNalHrdParametersPresentFlag(false);
+    vui->setHrdParametersPresentFlag(param->bEmitHRDSEI);
+    vui->getHrdParameters()->setNalHrdParametersPresentFlag(param->bEmitHRDSEI);
     vui->getHrdParameters()->setSubPicHrdParamsPresentFlag(false);
 
     vui->getTimingInfo()->setTimingInfoPresentFlag(true);
@@ -1426,12 +1427,10 @@
 
     m_nonPackedConstraintFlag = false;
     m_frameOnlyConstraintFlag = false;
-    m_bufferingPeriodSEIEnabled = 0;
     m_displayOrientationSEIAngle = 0;
     m_gradualDecodingRefreshInfoEnabled = 0;
     m_decodingUnitInfoSEIEnabled = 0;
     m_useScalingListId = 0;
-    m_activeParameterSetsSEIEnabled = 0;
     m_minSpatialSegmentationIdc = 0;
     m_neutralChromaIndicationFlag = false;
     m_pocProportionalToTimingFlag = false;
diff -r d78f38b707ba -r f9f553c8bd6a source/encoder/encoder.h
--- a/source/encoder/encoder.h	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/encoder/encoder.h	Tue Jun 03 18:03:15 2014 +0530
@@ -82,7 +82,6 @@
     int64_t            m_firstPts;
     int64_t            m_bframeDelayTime;
     int64_t            m_prevReorderedPts[2];
-    int64_t            m_encodedFrameNum;
 
     ThreadPool*        m_threadPool;
     Lookahead*         m_lookahead;
@@ -157,7 +156,8 @@
     bool               m_bPCMFilterDisableFlag;
     bool               m_loopFilterAcrossTilesEnabledFlag;
 
-    int                m_bufferingPeriodSEIEnabled;
+    int64_t            m_encodedFrameNum;
+    int                m_lastBPSEI;
     int                m_displayOrientationSEIAngle;
     int                m_gradualDecodingRefreshInfoEnabled;
     int                m_decodingUnitInfoSEIEnabled;
@@ -168,7 +168,6 @@
 
     bool               m_TransquantBypassEnableFlag;   ///< transquant_bypass_enable_flag setting in PPS.
     bool               m_CUTransquantBypassFlagValue;  ///< if transquant_bypass_enable_flag, the fixed value to use for the per-CU cu_transquant_bypass_flag.
-    int                m_activeParameterSetsSEIEnabled; ///< enable active parameter set SEI message
 
     bool               m_neutralChromaIndicationFlag;
     bool               m_pocProportionalToTimingFlag;
diff -r d78f38b707ba -r f9f553c8bd6a source/encoder/frameencoder.cpp
--- a/source/encoder/frameencoder.cpp	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/encoder/frameencoder.cpp	Tue Jun 03 18:03:15 2014 +0530
@@ -143,11 +143,17 @@
     {
         m_sps.setHrdParameters(m_cfg->param->fpsNum, m_cfg->param->fpsDenom, 0, m_cfg->param->rc.bitrate, m_cfg->param->bframes > 0);
     }
-    if (m_cfg->m_bufferingPeriodSEIEnabled || m_cfg->m_decodingUnitInfoSEIEnabled)
+    if (m_cfg->param->bEmitHRDSEI || m_cfg->m_decodingUnitInfoSEIEnabled)
     {
         m_sps.getVuiParameters()->setHrdParametersPresentFlag(true);
     }
 
+    // initialize HRD parameters of SPS
+    if (m_cfg->param->bEmitHRDSEI)
+    {
+        top->m_rateControl->initHRD(&m_sps);
+    }
+
     m_sps.setTMVPFlagsPresent(true);
 
     // set default slice level flag to the same as SPS level flag
@@ -266,7 +272,7 @@
     nalunits[count]->init(nalu);
     count++;
 
-    if (m_cfg->m_activeParameterSetsSEIEnabled)
+    if (m_cfg->param->bEmitHRDSEI)
     {
         SEIActiveParameterSets sei;
         sei.activeVPSId = m_top->m_vps.getVPSId();
@@ -275,6 +281,7 @@
         sei.numSpsIdsMinus1 = 0;
         sei.activeSeqParamSetId = m_sps.getSPSId();
 
+        nalu.resetToType(NAL_UNIT_PREFIX_SEI);
         entropyCoder->setBitstream(&nalu.m_bitstream);
         m_seiWriter.writeSEImessage(nalu.m_bitstream, sei, &m_sps);
         writeRBSPTrailingBits(nalu.m_bitstream);
@@ -404,6 +411,7 @@
     TEncEntropy* entropyCoder      = getEntropyCoder(0);
     TComSlice*   slice             = m_pic->getSlice();
     int          chFmt             = slice->getSPS()->getChromaFormatIdc();
+    int          totalCoded        = (int)m_top->m_encodedFrameNum - 1;
 
     m_nalCount = 0;
     entropyCoder->setEntropyCoder(&m_sbacCoder, NULL);
@@ -526,6 +534,34 @@
 
     if (slice->getPic()->m_lowres.bKeyframe)
     {
+        if (m_cfg->param->bEmitHRDSEI)
+        {
+            OutputNALUnit nalu(NAL_UNIT_PREFIX_SEI);
+            SEIBufferingPeriod* sei_buffering_period = &m_top->m_rateControl->sei;
+            sei_buffering_period->m_bpSeqParameterSetId = m_sps.getSPSId();
+            sei_buffering_period->m_rapCpbParamsPresentFlag = 0;
+
+            // for the concatenation, it can be set to one during splicing.
+            sei_buffering_period->m_concatenationFlag = 0;
+
+            // since the temporal layer HRD is not ready, we assumed it is fixed
+            sei_buffering_period->m_auCpbRemovalDelayDelta = 1;
+            sei_buffering_period->m_cpbDelayOffset = 0;
+            sei_buffering_period->m_dpbDelayOffset = 0;
+
+            // hrdFullness() calculates the initial CPB removal delay and offset
+            m_top->m_rateControl->hrdFullness(sei_buffering_period);
+            m_seiWriter.writeSEImessage(nalu.m_bitstream, *sei_buffering_period, &m_sps);
+            writeRBSPTrailingBits(nalu.m_bitstream);
+            m_nalList[m_nalCount] = X265_MALLOC(NALUnitEBSP, 1);
+            if (m_nalList[m_nalCount])
+            {
+                m_nalList[m_nalCount]->init(nalu);
+                m_nalCount++;
+            }
+
+            m_top->m_lastBPSEI = totalCoded;
+        }
         if (m_cfg->m_gradualDecodingRefreshInfoEnabled && !slice->getRapPicFlag())
         {
             // Gradual decoding refresh SEI
@@ -568,24 +604,38 @@
         }
     }
 
-    if (!!m_cfg->param->interlaceMode)
+    if (m_cfg->param->bEmitHRDSEI || !!m_cfg->param->interlaceMode)
     {
+        // Picture Timing SEI
         OutputNALUnit nalu(NAL_UNIT_PREFIX_SEI);
 
-        SEIPictureTiming sei;
+        SEIPictureTiming *sei = &m_pic->m_sei;
+        int poc = slice->getPOC();
+        int cpbDelayLength = slice->getSPS()->getVuiParameters()->getHrdParameters()->getCpbRemovalDelayLengthMinus1() + 1;
         if (m_cfg->param->interlaceMode == 2)
         {
-            sei.m_picStruct = (slice->getPOC() & 1) ? 1 /* top */ : 2 /* bottom */;
+            sei->m_picStruct = (poc & 1) ? 1 /* top */ : 2 /* bottom */;
+        }
+        else if (m_cfg->param->interlaceMode == 1)
+        {
+            sei->m_picStruct = (poc & 1) ? 2 /* bottom */ : 1 /* top */;
         }
         else
         {
-            sei.m_picStruct = (slice->getPOC() & 1) ? 2 /* bottom */ : 1 /* top */;
+            sei->m_picStruct = 0;
         }
-        sei.m_sourceScanType = 0;
-        sei.m_duplicateFlag = 0;
+        sei->m_sourceScanType = 0;
+        sei->m_duplicateFlag = false;
+        sei->m_picDpbOutputDuDelay = 0;
+        sei->m_numNalusInDuMinus1 = NULL;
+        sei->m_duCpbRemovalDelayMinus1 = NULL;
 
+        // 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, totalCoded - m_top->m_lastBPSEI), (1 << cpbDelayLength));
+        sei->m_picDpbOutputDelay = slice->getSPS()->getNumReorderPics(0) + poc - totalCoded;
         entropyCoder->setBitstream(&nalu.m_bitstream);
-        m_seiWriter.writeSEImessage(nalu.m_bitstream, sei, &m_sps);
+        m_seiWriter.writeSEImessage(nalu.m_bitstream, *sei, &m_sps);
         writeRBSPTrailingBits(nalu.m_bitstream);
         m_nalList[m_nalCount] = X265_MALLOC(NALUnitEBSP, 1);
         if (m_nalList[m_nalCount])
diff -r d78f38b707ba -r f9f553c8bd6a source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/encoder/ratecontrol.cpp	Tue Jun 03 18:03:15 2014 +0530
@@ -27,6 +27,10 @@
 #include "encoder.h"
 #include "slicetype.h"
 #include "ratecontrol.h"
+#include "TLibCommon/SEI.h"
+
+#define BR_SHIFT  6
+#define CPB_SHIFT 4
 
 using namespace x265;
 
@@ -34,6 +38,50 @@
 const double RateControl::amortizeFraction = 0.85;
 const int RateControl::amortizeFrames = 75;
 
+namespace {
+
+inline int calcScale(uint32_t x)
+{
+    static uint8_t lut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
+    int y, z = (((x & 0xffff) - 1) >> 27) & 16;
+    x >>= z;
+    z += y = (((x & 0xff) - 1) >> 28) & 8;
+    x >>= y;
+    z += y = (((x & 0xf) - 1) >> 29) & 4;
+    x >>= y;
+    return z + lut[x&0xf];
+}
+
+inline int calcLength(uint32_t x)
+{
+    static uint8_t lut[16] = {4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0};
+    int y, z = (((x >> 16) - 1) >> 27) & 16;
+    x >>= z ^ 16;
+    z += y = ((x - 0x100) >> 28) & 8;
+    x >>= y ^ 8;
+    z += y = ((x - 0x10) >> 29) & 4;
+    x >>= y ^ 4;
+    return z + lut[x];
+}
+
+inline void reduceFraction(int* n, int* d)
+{
+    int a = *n;
+    int b = *d;
+    int c;
+    if (!a || !b)
+        return;
+    c = a % b;
+    while (c)
+    {
+        a = b;
+        b = c;
+        c = a % b;
+    }
+    *n /= b;
+    *d /= b;
+}
+}  // end anonymous namespace
 /* Compute variance to derive AC energy of each block */
 static inline uint32_t acEnergyVar(TComPic *pic, uint64_t sum_ssd, int shift, int i)
 {
@@ -287,40 +335,11 @@
         param->rc.vbvMaxBitrate = 0;
     }
 
-    isVbv = param->rc.vbvMaxBitrate > 0 && param->rc.vbvBufferSize > 0;
-    double fps = (double)param->fpsNum / param->fpsDenom;
-    if (isVbv)
-    {
-        /* We don't support changing the ABR bitrate right now,
-           so if the stream starts as CBR, keep it CBR. */
-        if (param->rc.vbvBufferSize < (int)(param->rc.vbvMaxBitrate / fps))
-        {
-            param->rc.vbvBufferSize = (int)(param->rc.vbvMaxBitrate / fps);
-            x265_log(param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",
-                     param->rc.vbvBufferSize);
-        }
-        int vbvBufferSize = param->rc.vbvBufferSize * 1000;
-        int vbvMaxBitrate = param->rc.vbvMaxBitrate * 1000;
-
-        bufferRate = vbvMaxBitrate / fps;
-        vbvMaxRate = vbvMaxBitrate;
-        bufferSize = vbvBufferSize;
-        singleFrameVbv = bufferRate * 1.1 > bufferSize;
-
-        if (param->rc.vbvBufferInit > 1.)
-            param->rc.vbvBufferInit = Clip3(0.0, 1.0, param->rc.vbvBufferInit / param->rc.vbvBufferSize);
-        param->rc.vbvBufferInit = Clip3(0.0, 1.0, X265_MAX(param->rc.vbvBufferInit, bufferRate / bufferSize));
-        bufferFillFinal = bufferSize * param->rc.vbvBufferInit;
-        isCbr = param->rc.rateControlMode == X265_RC_ABR
-            && param->rc.vbvMaxBitrate <= param->rc.bitrate;
-    }
-
     bframes = param->bframes;
     bframeBits = 0;
     leadingNoBSatd = 0;
     ipOffset = 6.0 * X265_LOG2(param->rc.ipFactor);
     pbOffset = 6.0 * X265_LOG2(param->rc.pbFactor);
-    init();
 
     /* Adjust the first frame in order to stabilize the quality level compared to the rest */
 #define ABR_INIT_QP_MIN (24)
@@ -347,8 +366,41 @@
     lstep = pow(2, param->rc.qpStep / 6.0);
 }
 
-void RateControl::init()
+void RateControl::init(TComSPS *sps)
 {
+    isVbv = param->rc.vbvMaxBitrate > 0 && param->rc.vbvBufferSize > 0;
+    double fps = (double)param->fpsNum / param->fpsDenom;
+    if (isVbv)
+    {
+        /* We don't support changing the ABR bitrate right now,
+           so if the stream starts as CBR, keep it CBR. */
+        if (param->rc.vbvBufferSize < (int)(param->rc.vbvMaxBitrate / fps))
+        {
+            param->rc.vbvBufferSize = (int)(param->rc.vbvMaxBitrate / fps);
+            x265_log(param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",
+                     param->rc.vbvBufferSize);
+        }
+        int vbvBufferSize = param->rc.vbvBufferSize * 1000;
+        int vbvMaxBitrate = param->rc.vbvMaxBitrate * 1000;
+
+        TComHRD* hrd = sps->getVuiParameters()->getHrdParameters();
+        if (hrd->getNalHrdParametersPresentFlag())
+        {
+            vbvBufferSize = (hrd->getCpbSizeValueMinus1(0, 0, 0) + 1) << (hrd->getCpbSizeScale() + CPB_SHIFT);
+            vbvMaxBitrate = (hrd->getBitRateValueMinus1(0, 0, 0) + 1) << (hrd->getBitRateScale() + BR_SHIFT);
+        }
+        bufferRate = vbvMaxBitrate / fps;
+        vbvMaxRate = vbvMaxBitrate;
+        bufferSize = vbvBufferSize;
+        singleFrameVbv = bufferRate * 1.1 > bufferSize;
+
+        if (param->rc.vbvBufferInit > 1.)
+            param->rc.vbvBufferInit = Clip3(0.0, 1.0, param->rc.vbvBufferInit / param->rc.vbvBufferSize);
+        param->rc.vbvBufferInit = Clip3(0.0, 1.0, X265_MAX(param->rc.vbvBufferInit, bufferRate / bufferSize));
+        bufferFillFinal = bufferSize * param->rc.vbvBufferInit;
+        isCbr = param->rc.rateControlMode == X265_RC_ABR
+            && param->rc.vbvMaxBitrate <= param->rc.bitrate;
+    }
     totalBits = 0;
     framesDone = 0;
     double tuneCplxFactor = 1;
@@ -373,6 +425,45 @@
     predBfromP = pred[0];
 }
 
+void RateControl::initHRD(TComSPS *sps)
+{
+    int vbvBufferSize = param->rc.vbvBufferSize * 1000;
+    int vbvMaxBitrate = param->rc.vbvMaxBitrate * 1000;
+
+    // Init HRD
+    TComHRD* hrd = sps->getVuiParameters()->getHrdParameters();
+    hrd->setCpbCntMinus1(0, 0);
+    hrd->setLowDelayHrdFlag(0, false);
+    hrd->setFixedPicRateFlag(0, 1);
+    hrd->setPicDurationInTcMinus1(0, 0);
+
+    // normalize HRD size and rate to the value / scale notation
+    hrd->setBitRateScale(Clip3(0, 15, calcScale(vbvMaxBitrate) - BR_SHIFT));
+    hrd->setBitRateValueMinus1(0, 0, 0, (vbvMaxBitrate >> (hrd->getBitRateScale() + BR_SHIFT)) - 1);
+
+    hrd->setCpbSizeScale(Clip3(0, 15, calcScale(vbvBufferSize) - CPB_SHIFT));
+    hrd->setCpbSizeValueMinus1(0, 0, 0, (vbvBufferSize >> (hrd->getCpbSizeScale() + CPB_SHIFT)) - 1);
+    int bitRateUnscale = (hrd->getBitRateValueMinus1(0, 0, 0) + 1) << (hrd->getBitRateScale() + BR_SHIFT);
+    int cpbSizeUnscale = (hrd->getCpbSizeValueMinus1(0, 0, 0) + 1) << (hrd->getCpbSizeScale() + CPB_SHIFT);
+
+    // arbitrary
+    #define MAX_DURATION 0.5
+
+    TimingInfo *time = sps->getVuiParameters()->getTimingInfo();
+    int maxCpbOutputDelay = (int)(X265_MIN(param->keyframeMax * MAX_DURATION * time->getTimeScale() / time->getNumUnitsInTick(), INT_MAX));
+    int maxDpbOutputDelay = (int)(sps->getMaxDecPicBuffering(0) * MAX_DURATION * time->getTimeScale() / time->getNumUnitsInTick());
+    int maxDelay = (int)(90000.0 * cpbSizeUnscale / bitRateUnscale + 0.5);
+
+    hrd->setInitialCpbRemovalDelayLengthMinus1(2 + Clip3(4, 22, 32 - calcLength(maxDelay)) - 1);
+    hrd->setCpbRemovalDelayLengthMinus1(Clip3(4, 31, 32 - calcLength(maxCpbOutputDelay)) - 1);
+    hrd->setDpbOutputDelayLengthMinus1(Clip3(4, 31, 32 - calcLength(maxDpbOutputDelay)) - 1);
+
+    #undef MAX_DURATION
+
+    vbvBufferSize = cpbSizeUnscale;
+    vbvMaxBitrate = bitRateUnscale;
+}
+
 void RateControl::rateControlStart(TComPic* pic, Lookahead *l, RateControlEntry* rce, Encoder* enc)
 {
     curSlice = pic->getSlice();
@@ -627,7 +718,7 @@
             double underflow = 1.0 + (totalBits - wantedBitsWindow) / abrBuffer;
             if (underflow < 0.9 && !isFrameDone)
             {
-                init();
+                init(NULL);
                 shortTermCplxSum = rce->lastSatd / (CLIP_DURATION(frameDuration) / BASE_FRAME_DURATION);
                 shortTermCplxCount = 1;
                 isAbrReset = true;
@@ -642,6 +733,26 @@
     }
 }
 
+void RateControl::hrdFullness(SEIBufferingPeriod *seiBP)
+{
+    TComVUI* vui = curSlice->getSPS()->getVuiParameters();
+    TComHRD* hrd = vui->getHrdParameters();
+    int num = 90000;
+    int denom = (hrd->getBitRateValueMinus1(0, 0, 0) + 1) << (hrd->getBitRateScale() + BR_SHIFT);
+    reduceFraction(&num, &denom);
+    int64_t cpbState = (int64_t)bufferFillFinal;
+    int64_t cpbSize = (int64_t)((hrd->getCpbSizeValueMinus1(0, 0, 0) + 1) << (hrd->getCpbSizeScale() + CPB_SHIFT));
+
+    if(cpbState < 0 || cpbState > cpbSize)
+    {
+         x265_log(param, X265_LOG_WARNING, "CPB %s: %.0lf bits in a %.0lf-bit buffer\n",
+                   cpbState < 0 ? "underflow" : "overflow", (float)cpbState/denom, (float)cpbSize/denom);
+    }
+
+    seiBP->m_initialCpbRemovalDelay[0][0] = (uint32_t)(num * cpbState + denom) / denom;
+    seiBP->m_initialCpbRemovalDelayOffset[0][0] = (uint32_t)(num * cpbSize + denom) / denom - seiBP->m_initialCpbRemovalDelay[0][0];
+}
+
 void RateControl::updateVbvPlan(Encoder* enc)
 {
     bufferFill = bufferFillFinal;
@@ -1133,6 +1244,37 @@
             }
         }
         updateVbv(bits, rce);
+
+        if (param->bEmitHRDSEI)
+        {
+            TComVUI *vui = pic->getSlice()->getSPS()->getVuiParameters();
+            TComHRD *hrd = vui->getHrdParameters();
+            TimingInfo *time = vui->getTimingInfo();
+            if (pic->getSlice()->getPOC() == 0)
+            {
+                // access unit initialises the HRD
+                pic->m_hrdTiming.cpbInitialAT = 0;
+                pic->m_hrdTiming.cpbRemovalTime = nominalRemovalTime = (double)sei.m_initialCpbRemovalDelay[0][0] / 90000;
+            }
+            else
+            {
+                pic->m_hrdTiming.cpbRemovalTime = nominalRemovalTime + (double)pic->m_sei.m_auCpbRemovalDelay * time->getNumUnitsInTick() / time->getTimeScale();
+                double cpbEarliestAT = pic->m_hrdTiming.cpbRemovalTime - (double)sei.m_initialCpbRemovalDelay[0][0] / 90000;
+                if (!pic->m_lowres.bKeyframe)
+                {
+                    cpbEarliestAT -= (double)sei.m_initialCpbRemovalDelayOffset[0][0] / 90000;
+                }
+
+                if (hrd->getCbrFlag(0, 0, 0))
+                    pic->m_hrdTiming.cpbInitialAT = prevCpbFinalAT;
+                else
+                    pic->m_hrdTiming.cpbInitialAT = X265_MAX(prevCpbFinalAT, cpbEarliestAT);
+            }
+
+            uint32_t cpbsizeUnscale = (hrd->getCpbSizeValueMinus1(0, 0, 0) + 1) << (hrd->getCpbSizeScale() + CPB_SHIFT);
+            pic->m_hrdTiming.cpbFinalAT = prevCpbFinalAT = pic->m_hrdTiming.cpbInitialAT + bits / cpbsizeUnscale;
+            pic->m_hrdTiming.dpbOutputTime = (double)pic->m_sei.m_picDpbOutputDelay * time->getNumUnitsInTick() / time->getTimeScale() + pic->m_hrdTiming.cpbRemovalTime;
+        }
     }
     rce->isActive = false;
     return 0;
diff -r d78f38b707ba -r f9f553c8bd6a source/encoder/ratecontrol.h
--- a/source/encoder/ratecontrol.h	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/encoder/ratecontrol.h	Tue Jun 03 18:03:15 2014 +0530
@@ -26,14 +26,14 @@
 #ifndef X265_RATECONTROL_H
 #define X265_RATECONTROL_H
 
-#include "TLibCommon/CommonDef.h"
-
 namespace x265 {
 // encoder namespace
 
 struct Lookahead;
 class Encoder;
 class TComPic;
+class TComSPS;
+class SEIBufferingPeriod;
 
 #define BASE_FRAME_DURATION 0.04
 
@@ -122,6 +122,12 @@
     double lastRceq;
     int framesDone;           /* framesDone keeps track of # of frames passed through RateCotrol already */
     double qCompress;
+
+    /* hrd stuff */
+    SEIBufferingPeriod sei;
+    double nominalRemovalTime;
+    double prevCpbFinalAT;
+
     RateControl(Encoder * _cfg);
 
     // to be called for each frame to process RateControl and set QP
@@ -130,6 +136,9 @@
     int rateControlEnd(TComPic* pic, int64_t bits, RateControlEntry* rce);
     int rowDiagonalVbvRateControl(TComPic* pic, uint32_t row, RateControlEntry* rce, double& qpVbv);
 
+    void hrdFullness(SEIBufferingPeriod* sei);
+    void init(TComSPS* sps);
+    void initHRD(TComSPS* sps);
 protected:
 
     static const double amortizeFraction;
@@ -138,7 +147,6 @@
     int residualFrames;
     int residualCost;
 
-    void init();
     double getQScale(RateControlEntry *rce, double rateFactor);
     double rateEstimateQscale(TComPic* pic, RateControlEntry *rce); // main logic for calculating QP based on ABR
     void accumPQpUpdate();
diff -r d78f38b707ba -r f9f553c8bd6a source/x265.cpp
--- a/source/x265.cpp	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/x265.cpp	Tue Jun 03 18:03:15 2014 +0530
@@ -163,6 +163,8 @@
     { "strong-intra-smoothing",    no_argument, NULL, 0 },
     { "no-cutree",                 no_argument, NULL, 0 },
     { "cutree",                    no_argument, NULL, 0 },
+    { "no-hrd",               no_argument, NULL, 0 },
+    { "hrd",                  no_argument, NULL, 0 },
     { "sar",            required_argument, NULL, 0 },
     { "overscan",       required_argument, NULL, 0 },
     { "videoformat",    required_argument, NULL, 0 },
@@ -392,6 +394,7 @@
     H0("   --[no-]cutree                 Enable cutree for Adaptive Quantization. Default %s\n", OPT(param->rc.cuTree));
     H0("   --cbqpoffs <integer>          Chroma Cb QP Offset. Default %d\n", param->cbQpOffset);
     H0("   --crqpoffs <integer>          Chroma Cr QP Offset. Default %d\n", param->crQpOffset);
+    H0("   --[no-]hrd                    Enable HRD parameters signalling. Default %s\n", OPT(param->bEmitHRDSEI));
     H0("   --rd <0..6>                   Level of RD in mode decision 0:least....6:full RDO. Default %d\n", param->rdLevel);
     H0("   --psy-rd <0..2.0>             Strength of psycho-visual optimization. Requires slow preset or below. Default %f\n", param->psyRd);
     H0("   --[no-]signhide               Hide sign bit of one coeff per TU (rdo). Default %s\n", OPT(param->bEnableSignHiding));
diff -r d78f38b707ba -r f9f553c8bd6a source/x265.h
--- a/source/x265.h	Tue Jun 03 16:15:32 2014 +0530
+++ b/source/x265.h	Tue Jun 03 18:03:15 2014 +0530
@@ -410,6 +410,10 @@
      * NAL at the start of every access unit. Default false */
     int       bEnableAccessUnitDelimiters;
 
+    /* Enables the buffering period SEI and picture timing SEI to signal the HRD
+     * parameteres. Default is disabled */
+    int       bEmitHRDSEI;
+
     /*== Coding Unit (CU) definitions ==*/
 
     /* Maxiumum CU width and height in pixels.  The size must be 64, 32, or 16.


More information about the x265-devel mailing list