x265-1.8版本-encoder/ratecontrol.cpp注释

本文详细解析了x265编码器中的率控机制,包括两遍编码模式下的统计文件利用、单遍编码模式下的平均比特率控制策略、基于下采样SATD的量化参数调整算法等内容。

注:问号以及未注释部分 会在x265-1.9版本内更新

/*****************************************************************************
 * Copyright (C) 2013 x265 project
 *
 * Authors: Sumalatha Polureddy <sumalatha@multicorewareinc.com>
 *          Aarthi Priya Thirumalai <aarthi@multicorewareinc.com>
 *          Xun Xu, PPLive Corporation <xunxu@pptv.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
 *
 * This program is also available under a commercial proprietary license.
 * For more information, contact us at license @ x265.com.
 *****************************************************************************/

#include "common.h"
#include "param.h"
#include "frame.h"
#include "framedata.h"
#include "picyuv.h"

#include "encoder.h"
#include "slicetype.h"
#include "ratecontrol.h"
#include "sei.h"

#define BR_SHIFT  6
#define CPB_SHIFT 4

using namespace X265_NS;

/* Amortize the partial cost of I frames over the next N frames */

const int RateControl::s_slidingWindowFrames = 20;//滑动窗口大小
const char *RateControl::s_defaultStatFileName = "x265_2pass.log";

namespace {
#define CMP_OPT_FIRST_PASS(opt, param_val)\
{\
    bErr = 0;\
    p = strstr(opts, opt "=");\
    char* q = strstr(opts, "no-"opt);\
    if (p && sscanf(p, opt "=%d" , &i) && param_val != i)\
        bErr = 1;\
    else if (!param_val && !q && !p)\
        bErr = 1;\
    else if (param_val && (q || !strstr(opts, opt)))\
        bErr = 1;\
    if (bErr)\
    {\
        x265_log(m_param, X265_LOG_ERROR, "different " opt " setting than first pass (%d vs %d)\n", param_val, i);\
        return false;\
    }\
}

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;
}

inline char *strcatFilename(const char *input, const char *suffix)
{
    char *output = X265_MALLOC(char, strlen(input) + strlen(suffix) + 1);
    if (!output)
    {
        x265_log(NULL, X265_LOG_ERROR, "unable to allocate memory for filename\n");
        return NULL;
    }
    strcpy(output, input);
    strcat(output, suffix);
    return output;
}

inline double qScale2bits(RateControlEntry *rce, double qScale)
{
    if (qScale < 0.1)
        qScale = 0.1;
    return (rce->coeffBits + .1) * pow(rce->qScale / qScale, 1.1)
           + rce->mvBits * pow(X265_MAX(rce->qScale, 1) / X265_MAX(qScale, 1), 0.5)
           + rce->miscBits;
}

inline void copyRceData(RateControlEntry* rce, RateControlEntry* rce2Pass)
{
    rce->coeffBits = rce2Pass->coeffBits;
    rce->mvBits = rce2Pass->mvBits;
    rce->miscBits = rce2Pass->miscBits;
    rce->iCuCount = rce2Pass->iCuCount;
    rce->pCuCount = rce2Pass->pCuCount;
    rce->skipCuCount = rce2Pass->skipCuCount;
    rce->keptAsRef = rce2Pass->keptAsRef;
    rce->qScale = rce2Pass->qScale;
    rce->newQScale = rce2Pass->newQScale;
    rce->expectedBits = rce2Pass->expectedBits;
    rce->expectedVbv = rce2Pass->expectedVbv;
    rce->blurredComplexity = rce2Pass->blurredComplexity;
    rce->sliceType = rce2Pass->sliceType;
}

}  // end anonymous namespace
/* Returns the zone for the current frame */
/** 函数功能             : 获取当前帧所在的zone
/*  调用范围             : 只在rateControlStart(只在CQP模式应用)、getDiffLimitedQScale和RateControl::getQScale函数中被调用
* \返回                  : 返回当前帧所在的zone * */
x265_zone* RateControl::getZone()
{
    for (int i = m_param->rc.zoneCount - 1; i >= 0; i--)
    {
        x265_zone *z = &m_param->rc.zones[i];
        if (m_framesDone + 1 >= z->startFrame && m_framesDone < z->endFrame)//如果当前帧在此范围内
            return z;
    }
    return NULL;
}

RateControl::RateControl(x265_param& p)
{
    m_param = &p; //获取当前配置参数
    int lowresCuWidth = ((m_param->sourceWidth / 2) + X265_LOWRES_CU_SIZE - 1) >> X265_LOWRES_CU_BITS; //1/2下采样帧中一行有多少8x8块
    int lowresCuHeight = ((m_param->sourceHeight / 2) + X265_LOWRES_CU_SIZE - 1) >> X265_LOWRES_CU_BITS;//1/2下采样帧中一列有多少8x8块
    m_ncu = lowresCuWidth * lowresCuHeight;//1/2下采样帧中的8x8块个数

    if (m_param->rc.cuTree) //如果应用cutree 设置强度系数为1 否则从配置参数中获取
        m_qCompress = 1;
    else
        m_qCompress = m_param->rc.qCompress;//获取配置参数中的强度系数

    // validate for param->rc, maybe it is need to add a function like x265_parameters_valiate()
    m_residualFrames = 0;
    m_partialResidualFrames = 0;
    m_residualCost = 0;
    m_partialResidualCost = 0;
    m_rateFactorMaxIncrement = 0;
    m_rateFactorMaxDecrement = 0;
    m_fps = (double)m_param->fpsNum / m_param->fpsDenom;
    m_startEndOrder.set(0); //初始化为0
    m_bTerminated = false;
    m_finalFrameCount = 0;
    m_numEntries = 0;
    m_isSceneTransition = false;
    m_lastPredictorReset = 0;
    if (m_param->rc.rateControlMode == X265_RC_CRF)
    {
        m_param->rc.qp = (int)m_param->rc.rfConstant;
        m_param->rc.bitrate = 0;

        double baseCplx = m_ncu * (m_param->bframes ? 120 : 80);//120*m_ncu
        double mbtree_offset = m_param->rc.cuTree ? (1.0 - m_param->rc.qCompress) * 13.5 : 0;
        m_rateFactorConstant = pow(baseCplx, 1 - m_qCompress) /
            x265_qp2qScale(m_param->rc.rfConstant + mbtree_offset);//一般分辨率越大 此值越大 一般在0.0x之间
        if (m_param->rc.rfConstantMax)//如果配置CRF 的最大量化值
        {
            m_rateFactorMaxIncrement = m_param->rc.rfConstantMax - m_param->rc.rfConstant;//求最大与配置之间的差值
            if (m_rateFactorMaxIncrement <= 0)
            {
                x265_log(m_param, X265_LOG_WARNING, "CRF max must be greater than CRF\n");
                m_rateFactorMaxIncrement = 0;
            }
        }
        if (m_param->rc.rfConstantMin)//如果当前是CRF模式 并且 有配置最小crf值
            m_rateFactorMaxDecrement = m_param->rc.rfConstant - m_param->rc.rfConstantMin;//求最小与配置之间的差值
    }
    m_isAbr = m_param->rc.rateControlMode != X265_RC_CQP && !m_param->rc.bStatRead;//是否应用ABR 当前不是CQP并且不是多pass 读的时候
    m_2pass = m_param->rc.rateControlMode == X265_RC_ABR && m_param->rc.bStatRead;//判断当前是否是2pass
    m_bitrate = m_param->rc.bitrate * 1000;//获取当前配置的目标码率
    m_frameDuration = (double)m_param->fpsDenom / m_param->fpsNum; //当前播放一帧占用的时间(单位秒)
    m_qp = m_param->rc.qp;//获取配置的固定QP
    m_lastRceq = 1; /* handles the cmplxrsum when the previous frame cost is zero */
    m_shortTermCplxSum = 0;
    m_shortTermCplxCount = 0;
    m_lastNonBPictType = I_SLICE;
    m_isAbrReset = false;
    m_lastAbrResetPoc = -1;
    m_statFileOut = NULL;
    m_cutreeStatFileOut = m_cutreeStatFileIn = NULL;
    m_rce2Pass = NULL;
    m_lastBsliceSatdCost = 0;

    // vbv initialization
    m_param->rc.vbvBufferSize = x265_clip3(0, 2000000, m_param->rc.vbvBufferSize);
    m_param->rc.vbvMaxBitrate = x265_clip3(0, 2000000, m_param->rc.vbvMaxBitrate);
    m_param->rc.vbvBufferInit = x265_clip3(0.0, 2000000.0, m_param->rc.vbvBufferInit);
    m_singleFrameVbv = 0;
    m_rateTolerance = 1.0;

    if (m_param->rc.vbvBufferSize)
    {
        if (m_param->rc.rateControlMode == X265_RC_CQP)
        {
            x265_log(m_param, X265_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n");
            m_param->rc.vbvBufferSize = 0;
            m_param->rc.vbvMaxBitrate = 0;
        }
        else if (m_param->rc.vbvMaxBitrate == 0)
        {
            if (m_param->rc.rateControlMode == X265_RC_ABR)
            {
                x265_log(m_param, X265_LOG_WARNING, "VBV maxrate unspecified, assuming CBR\n");
                m_param->rc.vbvMaxBitrate = m_param->rc.bitrate;
            }
            else
            {
                x265_log(m_param, X265_LOG_WARNING, "VBV bufsize set but maxrate unspecified, ignored\n");
                m_param->rc.vbvBufferSize = 0;
            }
        }
        else if (m_param->rc.vbvMaxBitrate < m_param->rc.bitrate &&
                 m_param->rc.rateControlMode == X265_RC_ABR)
        {
            x265_log(m_param, X265_LOG_WARNING, "max bitrate less than average bitrate, assuming CBR\n");
            m_param->rc.bitrate = m_param->rc.vbvMaxBitrate;
        }
    }
    else if (m_param->rc.vbvMaxBitrate)
    {
        x265_log(m_param, X265_LOG_WARNING, "VBV maxrate specified, but no bufsize, ignored\n");
        m_param->rc.vbvMaxBitrate = 0;
    }
    m_isVbv = m_param->rc.vbvMaxBitrate > 0 && m_param->rc.vbvBufferSize > 0;//判断是否应用VBV
    if (m_param->bEmitHRDSEI && !m_isVbv)
    {
        x265_log(m_param, X265_LOG_WARNING, "NAL HRD parameters require VBV parameters, ignored\n");
        m_param->bEmitHRDSEI = 0;
    }
    m_isCbr = m_param->rc.rateControlMode == X265_RC_ABR && m_isVbv && !m_2pass && m_param->rc.vbvMaxBitrate <= m_param->rc.bitrate;//CBR模式 如果最大码率设定小于目标码率
    if (m_param->rc.bStrictCbr && !m_isCbr)
    {
        x265_log(m_param, X265_LOG_WARNING, "strict CBR set without CBR mode, ignored\n");
        m_param->rc.bStrictCbr = 0;
    }
    if(m_param->rc.bStrictCbr)//如果严格控制目标码率  降低因子
        m_rateTolerance = 0.7;

    m_bframeBits = 0;
    m_leadingNoBSatd = 0;
    m_ipOffset = 6.0 * X265_LOG2(m_param->rc.ipFactor);//用途:I帧与P帧的qp参数关系  P = Iqp + m_ipOffset 默认 2.9125608156077503
    m_pbOffset = 6.0 * X265_LOG2(m_param->rc.pbFactor);//用途:P帧与B帧的qp参数关系  B = Pqp + m_pbOffset 默认 2.2710694220159415

    /* Adjust the first frame in order to stabilize the quality level compared to the rest */
#define ABR_INIT_QP_MIN (24) //ABR默认的最小QP
#define ABR_INIT_QP_MAX (40)
#define ABR_SCENECUT_INIT_QP_MIN (12)//场景切换的最小QP 12
#define CRF_INIT_QP (int)m_param->rc.rfConstant //CRF模式下的默认QP
    for (int i = 0; i < 3; i++)
        m_lastQScaleFor[i] = x265_qp2qScale(m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN);//初始化Qsclae

    if (m_param->rc.rateControlMode == X265_RC_CQP)//固定QP模式
    {
        if (m_qp && !m_param->bLossless)//如果m_qp不为0 并且不是无损压缩模式
        {
            m_qpConstant[P_SLICE] = m_qp;//P帧获取QP
            m_qpConstant[I_SLICE] = x265_clip3(QP_MIN, QP_MAX_MAX, (int)(m_qp - m_ipOffset + 0.5));//I帧获取QP
            m_qpConstant[B_SLICE] = x265_clip3(QP_MIN, QP_MAX_MAX, (int)(m_qp + m_pbOffset + 0.5));//B帧获取QP
        }
        else
        {
            m_qpConstant[P_SLICE] = m_qpConstant[I_SLICE] = m_qpConstant[B_SLICE] = m_qp;//无损模式获取相同的QP值 4
        }
    }

    /* qpstep - value set as encoder specific */
    m_lstep = pow(2, m_param->rc.qpStep / 6.0);//初始化m_lstep

    for (int i = 0; i < 2; i++)
        m_cuTreeStats.qpBuffer[i] = NULL;
}
/** 函数功能             : ???分析加权信息(每个list的第一帧分析加权与否,其它不加权)
/*  调用范围             : 只在Encoder::create()和RateControl::checkAndResetABR函数中被调用
* \参数 rce              : ???当前编码帧
* \参数 isFrameDone      : 当前帧是否编码完毕 rateEstimateQscale为false rateControlEnd为true
* \返回                  : ??null * */
bool RateControl::init(const SPS& sps)
{
    if (m_isVbv)
    {
        /* 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))
        {
            m_param->rc.vbvBufferSize = (int)(m_param->rc.vbvMaxBitrate / m_fps);
            x265_log(m_param, X265_LOG_WARNING, "VBV buffer size cannot be smaller than one frame, using %d kbit\n",
                     m_param->rc.vbvBufferSize);
        }
        int vbvBufferSize = m_param->rc.vbvBufferSize * 1000;
        int vbvMaxBitrate = m_param->rc.vbvMaxBitrate * 1000;//一秒钟占用的最大bits

        if (m_param->bEmitHRDSEI)
        {
            const HRDInfo* hrd = &sps.vuiParameters.hrdParameters;
            vbvBufferSize = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT);
            vbvMaxBitrate = hrd->bitRateValue << (hrd->bitRateScale + BR_SHIFT);
        }
        m_bufferRate = vbvMaxBitrate / m_fps;//平均每帧最大的bits占用数目
        m_vbvMaxRate = vbvMaxBitrate;//一秒钟占用的最大bits
        m_bufferSize = vbvBufferSize;//获取一秒的bits buffer大小
        m_singleFrameVbv = m_bufferRate * 1.1 > m_bufferSize;//如果平均每帧最大的bits占用数目*1.1 大于一秒的bits buffer大小

        if (m_param->rc.vbvBufferInit > 1.)
            m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0, m_param->rc.vbvBufferInit / m_param->rc.vbvBufferSize);
        m_param->rc.vbvBufferInit = x265_clip3(0.0, 1.0, X265_MAX(m_param->rc.vbvBufferInit, m_bufferRate / m_bufferSize));
        m_bufferFillFinal = m_bufferSize * m_param->rc.vbvBufferInit;//???
    }

    m_totalBits = 0;
    m_encodedBits = 0;
    m_framesDone = 0;
    m_residualCost = 0;
    m_partialResidualCost = 0;
    m_amortizeFraction = 0.85;
    m_amortizeFrames = 75;
    if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps && m_param->rc.bStrictCbr) /* Strict CBR segment encode *///编码帧数过少并且严格按照目标码率
    {
        m_amortizeFraction = 0.85;
        m_amortizeFrames = m_param->totalFrames / 2;//重置为一半帧数
    }
    for (int i = 0; i < s_slidingWindowFrames; i++)
    {
        m_satdCostWindow[i] = 0;
        m_encodedBitsWindow[i] = 0;
    }
    m_sliderPos = 0;
    m_isPatternPresent = false;
    m_numBframesInPattern = 0;

    /* 720p videos seem to be a good cutoff for cplxrSum */
    double tuneCplxFactor = (m_param->rc.cuTree && m_ncu > 3600) ? 2.5 : 1;//720p以上参数为2.5 以下为1.0

    /* 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;//初始化用于估计第一帧I帧的QP参数
    m_wantedBitsWindow = m_bitrate * m_frameDuration;//初始化当前第一帧需要的bit数目
    m_accumPNorm = .01;
    m_accumPQp = (m_param->rc.rateControlMode == X265_RC_CRF ? CRF_INIT_QP : ABR_INIT_QP_MIN) * m_accumPNorm;//初始化为0.01*24

    /* Frame Predictors used in vbv */
    initFramePredictors();
    if (!m_statFileOut && (m_param->rc.bStatWrite || m_param->rc.bStatRead))
    {
        /* If the user hasn't defined the stat filename, use the default value */
        const char *fileName = m_param->rc.statFileName;
        if (!fileName)
            fileName = s_defaultStatFileName;
        /* Load stat file and init 2pass algo */
        if (m_param->rc.bStatRead)
        {
            m_expectedBitsSum = 0;
            char *p, *statsIn, *statsBuf;
            /* read 1st pass stats */
            statsIn = statsBuf = x265_slurp_file(fileName);
            if (!statsBuf)
                return false;
            if (m_param->rc.cuTree)
            {
                char *tmpFile = strcatFilename(fileName, ".cutree");
                if (!tmpFile)
                    return false;
                m_cutreeStatFileIn = fopen(tmpFile, "rb");
                X265_FREE(tmpFile);
                if (!m_cutreeStatFileIn)
                {
                    x265_log(m_param, X265_LOG_ERROR, "can't open stats file %s\n", tmpFile);
                    return false;
                }
            }

            /* check whether 1st pass options were compatible with current options */
            if (strncmp(statsBuf, "#options:", 9))
            {
                x265_log(m_param, X265_LOG_ERROR,"options list in stats file not valid\n");
                return false;
            }
            {
                int i, j;
                uint32_t k , l;
                bool bErr = false;
                char *opts = statsBuf;
                statsIn = strchr(statsBuf, '\n');
                if (!statsIn)
                {
                    x265_log(m_param, X265_LOG_ERROR, "Malformed stats file\n");
                    return false;
                }
                *statsIn = '\0';
                statsIn++;
                if (sscanf(opts, "#options: %dx%d", &i, &j) != 2)
                {
                    x265_log(m_param, X265_LOG_ERROR, "Resolution specified in stats file not valid\n");
                    return false;
                }
                if ((p = strstr(opts, " fps=")) == 0 || sscanf(p, " fps=%u/%u", &k, &l) != 2)
                {
                    x265_log(m_param, X265_LOG_ERROR, "fps specified in stats file not valid\n");
                    return false;
                }
                if (k != m_param->fpsNum || l != m_param->fpsDenom)
                {
                    x265_log(m_param, X265_LOG_ERROR, "fps mismatch with 1st pass (%u/%u vs %u/%u)\n",
                              m_param->fpsNum, m_param->fpsDenom, k, l);
                    return false;
                }
                CMP_OPT_FIRST_PASS("bitdepth", m_param->internalBitDepth);
                CMP_OPT_FIRST_PASS("weightp", m_param->bEnableWeightedPred);
                CMP_OPT_FIRST_PASS("bframes", m_param->bframes);
                CMP_OPT_FIRST_PASS("b-pyramid", m_param->bBPyramid);
                CMP_OPT_FIRST_PASS("open-gop", m_param->bOpenGOP);
                CMP_OPT_FIRST_PASS("keyint", m_param->keyframeMax);
                CMP_OPT_FIRST_PASS("scenecut", m_param->scenecutThreshold);

                if ((p = strstr(opts, "b-adapt=")) != 0 && sscanf(p, "b-adapt=%d", &i) && i >= X265_B_ADAPT_NONE && i <= X265_B_ADAPT_TRELLIS)
                {
                    m_param->bFrameAdaptive = i;
                }
                else if (m_param->bframes)
                {
                    x265_log(m_param, X265_LOG_ERROR, "b-adapt method specified in stats file not valid\n");
                    return false;
                }

                if ((p = strstr(opts, "rc-lookahead=")) != 0 && sscanf(p, "rc-lookahead=%d", &i))
                    m_param->lookaheadDepth = i;
            }
            /* find number of pics */
            p = statsIn;
            int numEntries;
            for (numEntries = -1; p; numEntries++)
                p = strchr(p + 1, ';');
            if (!numEntries)
            {
                x265_log(m_param, X265_LOG_ERROR, "empty stats file\n");
                return false;
            }
            m_numEntries = numEntries;

            if (m_param->totalFrames < m_numEntries && m_param->totalFrames > 0)
            {
                x265_log(m_param, X265_LOG_WARNING, "2nd pass has fewer frames than 1st pass (%d vs %d)\n",
                         m_param->totalFrames, m_numEntries);
            }
            if (m_param->totalFrames > m_numEntries)
            {
                x265_log(m_param, X265_LOG_ERROR, "2nd pass has more frames than 1st pass (%d vs %d)\n",
                         m_param->totalFrames, m_numEntries);
                return false;
            }

            m_rce2Pass = X265_MALLOC(RateControlEntry, m_numEntries);
            if (!m_rce2Pass)
            {
                 x265_log(m_param, X265_LOG_ERROR, "Rce Entries for 2 pass cannot be allocated\n");
                 return false;
            }
            /* init all to skipped p frames */
            for (int i = 0; i < m_numEntries; i++)
            {
                RateControlEntry *rce = &m_rce2Pass[i];
                rce->sliceType = P_SLICE;
                rce->qScale = rce->newQScale = x265_qp2qScale(20);
                rce->miscBits = m_ncu + 10;
                rce->newQp = 0;
            }
            /* read stats */
            p = statsIn;
            double totalQpAq = 0;
            for (int i = 0; i < m_numEntries; i++)
            {
                RateControlEntry *rce;
                int frameNumber;
                char picType;
                int e;
                char *next;
                double qpRc, qpAq;
                next = strstr(p, ";");
                if (next)
                    *next++ = 0;
                e = sscanf(p, " in:%d ", &frameNumber);
                if (frameNumber < 0 || frameNumber >= m_numEntries)
                {
                    x265_log(m_param, X265_LOG_ERROR, "bad frame number (%d) at stats line %d\n", frameNumber, i);
                    return false;
                }
                rce = &m_rce2Pass[frameNumber];
                e += sscanf(p, " in:%*d out:%*d type:%c q:%lf q-aq:%lf tex:%d mv:%d misc:%d icu:%lf pcu:%lf scu:%lf",
                       &picType, &qpRc, &qpAq, &rce->coeffBits,
                       &rce->mvBits, &rce->miscBits, &rce->iCuCount, &rce->pCuCount,
                       &rce->skipCuCount);
                rce->keptAsRef = true;
                if (picType == 'b' || picType == 'p')
                    rce->keptAsRef = false;
                if (picType == 'I' || picType == 'i')
                    rce->sliceType = I_SLICE;
                else if (picType == 'P' || picType == 'p')
                    rce->sliceType = P_SLICE;
                else if (picType == 'B' || picType == 'b')
                    rce->sliceType = B_SLICE;
                else
                    e = -1;
                if (e < 10)
                {
                    x265_log(m_param, X265_LOG_ERROR, "statistics are damaged at line %d, parser out=%d\n", i, e);
                    return false;
                }
                rce->qScale = x265_qp2qScale(qpRc);
                totalQpAq += qpAq;
                p = next;
            }
            X265_FREE(statsBuf);

            if (m_param->rc.rateControlMode == X265_RC_ABR)
            {
                if (!initPass2())
                    return false;
            } /* else we're using constant quant, so no need to run the bitrate allocation */
        }
        /* Open output file */
        /* If input and output files are the same, output to a temp file
         * and move it to the real name only when it's complete */
        if (m_param->rc.bStatWrite)
        {
            char *p, *statFileTmpname;
            statFileTmpname = strcatFilename(fileName, ".temp");
            if (!statFileTmpname)
                return false;
            m_statFileOut = fopen(statFileTmpname, "wb");
            X265_FREE(statFileTmpname);
            if (!m_statFileOut)
            {
                x265_log(m_param, X265_LOG_ERROR, "can't open stats file %s\n", statFileTmpname);
                return false;
            }
            p = x265_param2string(m_param);
            if (p)
                fprintf(m_statFileOut, "#options: %s\n", p);
            X265_FREE(p);
            if (m_param->rc.cuTree && !m_param->rc.bStatRead)
            {
                statFileTmpname = strcatFilename(fileName, ".cutree.temp");
                if (!statFileTmpname)
                    return false;
                m_cutreeStatFileOut = fopen(statFileTmpname, "wb");
                X265_FREE(statFileTmpname);
                if (!m_cutreeStatFileOut)
                {
                    x265_log(m_param, X265_LOG_ERROR, "can't open mbtree stats file %s\n", statFileTmpname);
                    return false;
                }
            }
        }
        if (m_param->rc.cuTree)
        {
            m_cuTreeStats.qpBuffer[0] = X265_MALLOC(uint16_t, m_ncu * sizeof(uint16_t));
            if (m_param->bBPyramid && m_param->rc.bStatRead)
                m_cuTreeStats.qpBuffer[1] = X265_MALLOC(uint16_t, m_ncu * sizeof(uint16_t));
            m_cuTreeStats.qpBufPos = -1;
        }
    }
    return true;
}

void RateControl::initHRD(SPS& sps)
{
    int vbvBufferSize = m_param->rc.vbvBufferSize * 1000;
    int vbvMaxBitrate = m_param->rc.vbvMaxBitrate * 1000;

    // Init HRD
    HRDInfo* hrd = &sps.vuiParameters.hrdParameters;
    hrd->cbrFlag = m_isCbr;

    // normalize HRD size and rate to the value / scale notation
    hrd->bitRateScale = x265_clip3(0, 15, calcScale(vbvMaxBitrate) - BR_SHIFT);
    hrd->bitRateValue = (vbvMaxBitrate >> (hrd->bitRateScale + BR_SHIFT));

    hrd->cpbSizeScale = x265_clip3(0, 15, calcScale(vbvBufferSize) - CPB_SHIFT);
    hrd->cpbSizeValue = (vbvBufferSize >> (hrd->cpbSizeScale + CPB_SHIFT));
    int bitRateUnscale = hrd->bitRateValue << (hrd->bitRateScale + BR_SHIFT);
    int cpbSizeUnscale = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT);

    // arbitrary
    #define MAX_DURATION 0.5

    TimingInfo *time = &sps.vuiParameters.timingInfo;
    int maxCpbOutputDelay = (int)(X265_MIN(m_param->keyframeMax * MAX_DURATION * time->timeScale / time->numUnitsInTick, INT_MAX));
    int maxDpbOutputDelay = (int)(sps.maxDecPicBuffering * MAX_DURATION * time->timeScale / time->numUnitsInTick);
    int maxDelay = (int)(90000.0 * cpbSizeUnscale / bitRateUnscale + 0.5);

    hrd->initialCpbRemovalDelayLength = 2 + x265_clip3(4, 22, 32 - calcLength(maxDelay));
    hrd->cpbRemovalDelayLength = x265_clip3(4, 31, 32 - calcLength(maxCpbOutputDelay));
    hrd->dpbOutputDelayLength = x265_clip3(4, 31, 32 - calcLength(maxDpbOutputDelay));

    #undef MAX_DURATION
}

bool RateControl::initPass2()
{
    uint64_t allConstBits = 0;
    uint64_t allAvailableBits = uint64_t(m_param->rc.bitrate * 1000. * m_numEntries * m_frameDuration);
    double rateFactor, stepMult;
    double qBlur = m_param->rc.qblur;
    double cplxBlur = m_param->rc.complexityBlur;
    const int filterSize = (int)(qBlur * 4) | 1;
    double expectedBits;
    double *qScale, *blurredQscale;
    double baseCplx = m_ncu * (m_param->bframes ? 120 : 80);
    double clippedDuration = CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION;

    /* find total/average complexity & const_bits */
    for (int i = 0; i < m_numEntries; i++)
        allConstBits += m_rce2Pass[i].miscBits;

    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 * 1000.));
        return false;
    }

    /* 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
     * bits than intended. */
    for (int i = 0; i < m_numEntries; i++)
    {
        double weightSum = 0;
        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++)
        {
            RateControlEntry *rcj = &m_rce2Pass[i + j];
            weight *= 1 - pow(rcj->iCuCount / m_ncu, 2);
            if (weight < 0.0001)
                break;
            gaussianWeight = weight * exp(-j * j / 200.0);
            weightSum += gaussianWeight;
            cplxSum += gaussianWeight * (qScale2bits(rcj, 1) - rcj->miscBits) / clippedDuration;
        }
        /* weighted average of cplx of past frames */
        weight = 1.0;
        for (int j = 0; j <= cplxBlur * 2 && j <= i; j++)
        {
            RateControlEntry *rcj = &m_rce2Pass[i - j];
            gaussianWeight = weight * exp(-j * j / 200.0);
            weightSum += gaussianWeight;
            cplxSum += gaussianWeight * (qScale2bits(rcj, 1) - rcj->miscBits) / clippedDuration;
            weight *= 1 - pow(rcj->iCuCount / m_ncu, 2);
            if (weight < .0001)
                break;
        }
        m_rce2Pass[i].blurredComplexity = cplxSum / weightSum;
    }

    CHECKED_MALLOC(qScale, double, m_numEntries);
    if (filterSize > 1)
    {
        CHECKED_MALLOC(blurredQscale, double, m_numEntries);
    }
    else
        blurredQscale = qScale;

    /* Search for a factor which, when multiplied by the RCEQ values from
     * each frame, adds up to the desired total size.
     * There is no exact closed-form solution because of VBV constraints and
     * because qscale2bits is not invertible, but we can start with the simple
     * approximation of scaling the 1st pass by the ratio of bitrates.
     * The search range is probably overkill, but speed doesn't matter here. */

    expectedBits = 1;
    for (int i = 0; i < m_numEntries; i++)
    {
        RateControlEntry* rce = &m_rce2Pass[i];
        double q = getQScale(rce, 1.0);
        expectedBits += qScale2bits(rce, q);
        m_lastQScaleFor[rce->sliceType] = q;
    }
    stepMult = allAvailableBits / expectedBits;

    rateFactor = 0;
    for (double step = 1E4 * stepMult; step > 1E-7 * stepMult; step *= 0.5)
    {
        expectedBits = 0;
        rateFactor += step;

        m_lastNonBPictType = -1;
        m_lastAccumPNorm = 1;
        m_accumPNorm = 0;

        m_lastQScaleFor[0] = m_lastQScaleFor[1] =
        m_lastQScaleFor[2] = pow(baseCplx, 1 - m_qCompress) / rateFactor;

        /* find qscale */
        for (int i = 0; i < m_numEntries; i++)
        {
            RateControlEntry *rce = &m_rce2Pass[i];
            qScale[i] = getQScale(rce, rateFactor);
            m_lastQScaleFor[rce->sliceType] = qScale[i];
        }

        /* fixed I/B qscale relative to P */
        for (int i = m_numEntries - 1; i >= 0; i--)
        {
            qScale[i] = getDiffLimitedQScale(&m_rce2Pass[i], qScale[i]);
            X265_CHECK(qScale[i] >= 0, "qScale became negative\n");
        }

        /* smooth curve */
        if (filterSize > 1)
        {
            X265_CHECK(filterSize % 2 == 1, "filterSize not an odd number\n");
            for (int i = 0; i < m_numEntries; i++)
            {
                double q = 0.0, sum = 0.0;

                for (int j = 0; j < filterSize; j++)
                {
                    int idx = i + j - filterSize / 2;
                    double d = idx - i;
                    double coeff = qBlur == 0 ? 1.0 : exp(-d * d / (qBlur * qBlur));
                    if (idx < 0 || idx >= m_numEntries)
                        continue;
                    if (m_rce2Pass[i].sliceType != m_rce2Pass[idx].sliceType)
                        continue;
                    q += qScale[idx] * coeff;
                    sum += coeff;
                }
                blurredQscale[i] = q / sum;
            }
        }

        /* find expected bits */
        for (int i = 0; i < m_numEntries; i++)
        {
            RateControlEntry *rce = &m_rce2Pass[i];
            rce->newQScale = clipQscale(NULL, rce, blurredQscale[i]); // check if needed
            X265_CHECK(rce->newQScale >= 0, "new Qscale is negative\n");
            expectedBits += qScale2bits(rce, rce->newQScale);
        }

        if (expectedBits > allAvailableBits)
            rateFactor -= step;
    }

    X265_FREE(qScale);
    if (filterSize > 1)
        X265_FREE(blurredQscale);

    if (m_isVbv)
        if (!vbv2Pass(allAvailableBits))
            return false;
    expectedBits = countExpectedBits();

    if (fabs(expectedBits / allAvailableBits - 1.0) > 0.01)
    {
        double avgq = 0;
        for (int i = 0; i < m_numEntries; i++)
            avgq += m_rce2Pass[i].newQScale;
        avgq = x265_qScale2qp(avgq / m_numEntries);

        if (expectedBits > allAvailableBits || !m_isVbv)
            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.),
                 avgq);
        if (expectedBits < allAvailableBits && avgq < QP_MIN + 2)
        {
            x265_log(m_param, X265_LOG_WARNING, "try reducing target bitrate\n");
        }
        else if (expectedBits > allAvailableBits && avgq > QP_MAX_SPEC - 2)
        {
            x265_log(m_param, X265_LOG_WARNING, "try increasing target bitrate\n");
        }
        else if (!(m_2pass && m_isVbv))
            x265_log(m_param, X265_LOG_WARNING, "internal error\n");
    }

    return true;

fail:
    x265_log(m_param, X265_LOG_WARNING, "two-pass ABR initialization failed\n");
    return false;
}

bool RateControl::vbv2Pass(uint64_t allAvailableBits)
{
    /* for each interval of bufferFull .. underflow, uniformly increase the qp of all
     * frames in the interval until either buffer is full at some intermediate frame or the
     * last frame in the interval no longer underflows.  Recompute intervals and repeat.
     * Then do the converse to put bits back into overflow areas until target size is met */

    double *fills;
    double expectedBits = 0;
    double adjustment;
    double prevBits = 0;
    int t0, t1;
    int iterations = 0 , adjMin, adjMax;
    CHECKED_MALLOC(fills, double, m_numEntries + 1);
    fills++;

    /* adjust overall stream size */
    do
    {
        iterations++;
        prevBits = expectedBits;

        if (expectedBits)
        {   /* not first iteration */
            adjustment = X265_MAX(X265_MIN(expectedBits / allAvailableBits, 0.999), 0.9);
            fills[-1] = m_bufferSize * m_param->rc.vbvBufferInit;
            t0 = 0;
            /* fix overflows */
            adjMin = 1;
            while (adjMin && findUnderflow(fills, &t0, &t1, 1))
            {
                adjMin = fixUnderflow(t0, t1, adjustment, MIN_QPSCALE, MAX_MAX_QPSCALE);
                t0 = t1;
            }
        }

        fills[-1] = m_bufferSize * (1. - m_param->rc.vbvBufferInit);
        t0 = 0;
        /* fix underflows -- should be done after overflow, as we'd better undersize target than underflowing VBV */
        adjMax = 1;
        while (adjMax && findUnderflow(fills, &t0, &t1, 0))
            adjMax = fixUnderflow(t0, t1, 1.001, MIN_QPSCALE, MAX_MAX_QPSCALE );

        expectedBits = countExpectedBits();
    }
    while ((expectedBits < .995 * allAvailableBits) && ((int64_t)(expectedBits+.5) > (int64_t)(prevBits+.5)));

    if (!adjMax)
        x265_log(m_param, X265_LOG_WARNING, "vbv-maxrate issue, qpmax or vbv-maxrate too low\n");

    /* store expected vbv filling values for tracking when encoding */
    for (int i = 0; i < m_numEntries; i++)
        m_rce2Pass[i].expectedVbv = m_bufferSize - fills[i];

    X265_FREE(fills - 1);
    return true;

fail:
    x265_log(m_param, X265_LOG_ERROR, "malloc failure in two-pass VBV init\n");
    return false;
}

/* In 2pass, force the same frame types as in the 1st pass */
int RateControl::rateControlSliceType(int frameNum)
{
    if (m_param->rc.bStatRead)
    {
        if (frameNum >= m_numEntries)
        {
            /* We could try to initialize everything required for ABR and
             * adaptive B-frames, but that would be complicated.
             * So just calculate the average QP used so far. */
            m_param->rc.qp = (m_accumPQp < 1) ? ABR_INIT_QP_MAX : (int)(m_accumPQp + 0.5);
            m_qpConstant[P_SLICE] = x265_clip3(QP_MIN, QP_MAX_MAX, m_param->rc.qp);
            m_qpConstant[I_SLICE] = x265_clip3(QP_MIN, QP_MAX_MAX, (int)(m_param->rc.qp - m_ipOffset + 0.5));
            m_qpConstant[B_SLICE] = x265_clip3(QP_MIN, QP_MAX_MAX, (int)(m_param->rc.qp + m_pbOffset + 0.5));

            x265_log(m_param, X265_LOG_ERROR, "2nd pass has more frames than 1st pass (%d)\n", m_numEntries);
            x265_log(m_param, X265_LOG_ERROR, "continuing anyway, at constant QP=%d\n", m_param->rc.qp);
            if (m_param->bFrameAdaptive)
                x265_log(m_param, X265_LOG_ERROR, "disabling adaptive B-frames\n");

            m_isAbr = 0;
            m_2pass = 0;
            m_param->rc.rateControlMode = X265_RC_CQP;
            m_param->rc.bStatRead = 0;
            m_param->bFrameAdaptive = 0;
            m_param->scenecutThreshold = 0;
            m_param->rc.cuTree = 0;
            if (m_param->bframes > 1)
                m_param->bframes = 1;
            return X265_TYPE_AUTO;
        }
        int frameType = m_rce2Pass[frameNum].sliceType == I_SLICE ? (frameNum > 0 && m_param->bOpenGOP ? X265_TYPE_I : X265_TYPE_IDR)
                            : m_rce2Pass[frameNum].sliceType == P_SLICE ? X265_TYPE_P
                            : (m_rce2Pass[frameNum].sliceType == B_SLICE && m_rce2Pass[frameNum].keptAsRef? X265_TYPE_BREF : X265_TYPE_B);
        return frameType;
    }
    else
        return X265_TYPE_AUTO;
}
/** 函数功能             : 初始化Predictor
/*  调用范围             : 只在RateControl::init和RateControl::rateControlStart函数中被调用
* \返回                  : null * */
void RateControl::initFramePredictors()
{
    //在初始化和场景切换帧处重置
    /* Frame Predictors used in vbv */
    for (int i = 0; i < 4; i++)
    {
        m_pred[i].coeff = 1.0;
        m_pred[i].count = 1.0;
        m_pred[i].decay = 0.5;
        m_pred[i].offset = 0.0;
    }
    m_pred[0].coeff = m_pred[3].coeff = 0.75;
    if (m_param->rc.qCompress >= 0.8) // when tuned for grain 
    {
        m_pred[1].coeff = 0.75;
        m_pred[0].coeff = m_pred[3].coeff = 0.50;
    }
}
/** 函数功能             : 计算估计当前帧应用的量化参数
/*  调用范围             : 只在FrameEncoder::compressFrame()函数中被调用
* \参数 curFrame         : 当前编码帧
* \参数 rce              : 当前帧的RC编码参数类
* \参数 enc              : 上层encodr类
* \返回                  : 返回当前帧估计的量化参数 * */
int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encoder* enc)
{
    //功能:
    //     1.循环等待触发
    //     2.初始化RC帧类型等基本信息
    //     3.如果是2pass:???
    //     4.根据场景切换帧信息选择是否初始化Predictor
    //     5.如果应用VBV:更新bitsbuffer 根据当前level规定设置当前帧最大占用的bits
    //     6.如果应用ABR,CRF或者当前为2pass:估计当前帧的m_qp值
    //       否则 如果是CQP模式   获取当前帧的QP值 (加上相应的offset)
    //     7.更新数据 并返回当前帧估计的量化参数
    int orderValue = m_startEndOrder.get();//获取当前RC线程控制参量
    int startOrdinal = rce->encodeOrder * 2;//触发参量值

    while (orderValue < startOrdinal && !m_bTerminated)//循环等待
        orderValue = m_startEndOrder.waitForChange(orderValue);//等待参量数据改变

    if (!curFrame) //无帧信息情况 一般不进入
    {
        // faked rateControlStart calls when the encoder is flushing
        m_startEndOrder.incr();
        return 0;
    }

    FrameData& curEncData = *curFrame->m_encData;//获取当前帧的编码信息
    m_curSlice = curEncData.m_slice;//获取当前帧的slice
    m_sliceType = m_curSlice->m_sliceType;//获取当前的slice类型
    rce->sliceType = m_sliceType;//获取当前的slice类型
    if (!m_2pass)//如果是1pass
        rce->keptAsRef = IS_REFERENCED(curFrame);//判断当前是否可作参考帧
    m_predType = getPredictorType(curFrame->m_lowres.sliceType, m_sliceType);//获取predictor应用的标号 0:不可参考b帧 1: P帧  2:I帧 3:可参考B帧
    rce->poc = m_curSlice->m_poc;//获取当前poc
    if (m_param->rc.bStatRead)//????
    {
        X265_CHECK(rce->poc >= 0 && rce->poc < m_numEntries, "bad encode ordinal\n");
        copyRceData(rce, &m_rce2Pass[rce->poc]);
    }
    rce->isActive = true;//RC启动
    bool isRefFrameScenecut = m_sliceType!= I_SLICE && m_curSlice->m_refPicList[0][0]->m_lowres.bScenecut == 1; //如果其前向第一个参考帧为场景切换帧
    //设x为场景切换帧
    //IBBBPBBBxBBBPBBBP  对应m_isSceneTransition值
    //00000000111111110
    if (curFrame->m_lowres.bScenecut) //如果当前帧为场景切换帧
    {
        m_isSceneTransition = true;//场景切换帧置为ture
        m_lastPredictorReset = rce->encodeOrder;//记录Predictor重置的编码位置(编码顺序) rce->encodeOrder
        initFramePredictors();//初始化Predictor
    }
    else if (m_sliceType != B_SLICE && !isRefFrameScenecut)//当前是P帧或者不是场景切换帧的I帧   并且 前向参考不是场景切换帧
        m_isSceneTransition = false;//置为false

    if (rce->encodeOrder < m_lastPredictorReset + m_param->frameNumThreads)//如果遇到场景切换帧 或者首帧 将当前的的count置为0  有几个framethread 进入初始化几次
    {
        rce->rowPreds[0][0].count = 0;
    }

    rce->bLastMiniGopBFrame = curFrame->m_lowres.bLastMiniGopBFrame;//标记当前帧是否为当前GOP的最后一个B帧
    rce->bufferRate = m_bufferRate;//平均每帧最大的bits占用数目
    rce->rowCplxrSum = 0.0;//初始化
    rce->rowTotalBits = 0;//初始化
    //功能:更新bitsbuffer 根据当前level规定设置当前帧最大占用的bits
    //      1.根据情况初始化Predictor (场景切换帧后)
    //      2.更新bitsbuffer
    //      3.根据当前level下最小压缩比设置当前帧相应的frameSizeMaximum(当前帧占用的最大bits)
    if (m_isVbv)//如果应用VBV
    {
        //初始化Predictor
        if (rce->rowPreds[0][0].count == 0)
        {
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    rce->rowPreds[i][j].coeff = 0.25;
                    rce->rowPreds[i][j].count = 1.0;
                    rce->rowPreds[i][j].decay = 0.5;
                    rce->rowPreds[i][j].offset = 0.0;
                }
            }
        }
        rce->rowPred[0] = &rce->rowPreds[m_sliceType][0];//指向当前帧类型位置 便于计算
        rce->rowPred[1] = &rce->rowPreds[m_sliceType][1];//指向当前帧类型位置 便于计算
        m_predictedBits = m_totalBits;//获取当前已经编码帧总的bits
        updateVbvPlan(enc);//VBV中更新根据当前帧并行情况更新当前RCbuffer
        rce->bufferFill = m_bufferFill;//获取更新后的值

        int mincr = enc->m_vps.ptl.minCrForLevel;//当前的最小压缩比 CR 表示 CompressionRatio 
        //根据当前level下最小压缩比设置当前帧相应的frameSizeMaximum(当前帧占用的最大bits)
        /* Profiles above Main10 don't require maxAU size check, so just set the maximum to a large value. */
        if (enc->m_vps.ptl.profileIdc > Profile::MAIN10 || enc->m_vps.ptl.levelIdc == Level::NONE)//如果是MAINSTILLPICTURE、MAINREXT档次或者没有等级定位 
            rce->frameSizeMaximum = 1e9; //设为一个大值
        else
        {
            /* The spec has a special case for the first frame. */
            if (rce->encodeOrder == 0)//第一帧
            {
                /* 1.5 * (Max( PicSizeInSamplesY, fR * MaxLumaSr) + MaxLumaSr * (AuCpbRemovalTime[ 0 ] -AuNominalRemovalTime[ 0 ])) ? MinCr */
                double fr = 1. / 300;//压缩因子 用于估计bits
                int picSizeInSamplesY = m_param->sourceWidth * m_param->sourceHeight;//当前帧的像素个数
                rce->frameSizeMaximum = 8 * 1.5 * X265_MAX(picSizeInSamplesY, fr * enc->m_vps.ptl.maxLumaSrForLevel) / mincr;//8表示一般一个像素占用8比特  1.5表示 4:2:0 格式 1.5帧(含有两个色度)
            }
            else
            {
                /* 1.5 * MaxLumaSr * (AuCpbRemovalTime[ n ] - AuCpbRemovalTime[ n - 1 ]) / MinCr */
                rce->frameSizeMaximum = 8 * 1.5 * enc->m_vps.ptl.maxLumaSrForLevel * m_frameDuration / mincr;//当前level下当前帧占用的最大bits 
            }
        }
    }
    //功能:估计当前帧的m_qp值
    //    1.如果应用ABR或者VBV 获取framecost 记录连续b帧cost相同数目,并在后一个非B帧中设置为同一模式(个数大于配置的B帧个数时)
    //    2.预估当前帧的m_qp
    if (m_isAbr || m_2pass) // ABR,CRF
    {
        if (m_isAbr || m_isVbv)
        {
            m_currentSatd = curFrame->m_lowres.satdCost >> (X265_DEPTH - 8); //获取当前的framecost 如果需要参考帧,取参考帧列表中各自第一帧当做参考帧的framecost 
            /* Update rce for use in rate control VBV later */
            rce->lastSatd = m_currentSatd;//更新当前最新的SATD值
            X265_CHECK(rce->lastSatd, "satdcost cannot be zero\n");
            /* Detect a pattern for B frames with same SATDcost to identify a series of static frames
             * and the P frame at the end of the series marks a possible case for ABR reset logic */
            if (m_param->bframes) //如果有B帧 功能:记录连续b帧cost相同数目,并在后一个非B帧中设置为同一模式(个数大于配置的B帧个数时)
            {
                if (m_sliceType != B_SLICE && m_numBframesInPattern > m_param->bframes)//如果当前是(I帧或者P帧) 并且相同cost的个数大于配置的B帧个数
                {
                    m_isPatternPresent = true;//置为同一模式
                }
                else if (m_sliceType == B_SLICE && !IS_REFERENCED(curFrame))//如果当前是B帧并且不是B参考帧
                {
                    if (m_currentSatd != m_lastBsliceSatdCost && !rce->bLastMiniGopBFrame)//如果当前B帧的cost不等于上一个B帧(编码顺序)的cost 并且不是当前帧是否为当前GOP的最后一个B帧
                    {
                        m_isPatternPresent = false;//开始一个新的模式 置为false
                        m_lastBsliceSatdCost = m_currentSatd;//记录最新B帧的framecost
                        m_numBframesInPattern = 0;//相同cost的个数置为0
                    }
                    else if (m_currentSatd == m_lastBsliceSatdCost)//如果当期B帧cost等于最近一个B帧的cost
                        m_numBframesInPattern++;//累加相同cost的个数
                }
            }
        }
        /* For a scenecut that occurs within the mini-gop, enable scene transition
         * switch until the next mini-gop to ensure a min qp for all the frames within 
         * the scene-transition mini-gop */

        double q = x265_qScale2qp(rateEstimateQscale(curFrame, rce));//预估当前帧的qscale并将预估qscale转换成qp参数 ???
        q = x265_clip3((double)QP_MIN, (double)QP_MAX_MAX, q);//clip操作防止越界
        m_qp = int(q + 0.5);//四舍五入转换成整数qp参数
        rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = q;//获取预估qp参数值(未四舍五入)
        /* copy value of lastRceq into thread local rce struct *to be used in RateControlEnd() */
        rce->qRceq = m_lastRceq;//获取数据对应未加权的qscale
        accumPQpUpdate();//累加m_accumPQp*0.95+m_qp和m_accumPNorm*0.95+1
    }
    else // CQP 固定QP模式
    {
        if (m_sliceType == B_SLICE && IS_REFERENCED(curFrame))//如果是B参考帧
            m_qp = (m_qpConstant[B_SLICE] + m_qpConstant[P_SLICE]) / 2; //QP取B帧和P帧的一半
        else
            m_qp = m_qpConstant[m_sliceType];//获取相应帧对应的QP
        curEncData.m_avgQpAq = curEncData.m_avgQpRc = m_qp;//获取当前帧的QP值
        
        x265_zone* zone = getZone();//获取当前帧所在的zone
        if (zone)
        {
            if (zone->bForceQp)//如果是QP模式
                m_qp += zone->qp - m_qpConstant[P_SLICE];
            else
                m_qp -= (int)(6.0 * X265_LOG2(zone->bitrateFactor));//因子模式
        }
    }
    if (m_sliceType != B_SLICE)//当前不是B帧
    {
        m_lastNonBPictType = m_sliceType;//更新最新的非B帧类型
        m_leadingNoBSatd = m_currentSatd;//更新最新的非B帧的SATD值
    }
    rce->leadingNoBSatd = m_leadingNoBSatd;//暂无任何作用 获取当前RC的最新非B帧的SATD值
    if (curFrame->m_forceqp)//如果应用QPfile
    {
        m_qp = (int32_t)(curFrame->m_forceqp + 0.5) - 1;//获取当前QP值
        m_qp = x265_clip3(QP_MIN, QP_MAX_MAX, m_qp);//防止越界
        rce->qpaRc = curEncData.m_avgQpRc = curEncData.m_avgQpAq = m_qp;//获取当前最新QP
        if (m_isAbr || m_2pass)//如果应用ABR或者当前为2pass
        {
            rce->qpNoVbv = rce->qpaRc;//获取没有经过修正的量化参数
            m_lastQScaleFor[m_sliceType] = x265_qp2qScale(rce->qpaRc);//qp转换为qpscale  存储最新的qscale值
            if (rce->poc == 0)
                 m_lastQScaleFor[P_SLICE] = m_lastQScaleFor[m_sliceType] * fabs(m_param->rc.ipFactor);//第一帧加权
            rce->frameSizePlanned = predictSize(&m_pred[m_predType], m_qp, (double)m_currentSatd);//获取预测的bits
        }
    }
    m_framesDone++;//计数

    return m_qp;//返回当前帧估计的量化参数
}
/** 函数功能             : 累加m_accumPQp*0.95+m_qp和m_accumPNorm*0.95+1
/*  调用范围             : 只在rateControlStart函数中被调用
* \返回                  : null * */
void RateControl::accumPQpUpdate()
{
    m_accumPQp   *= .95;
    m_accumPNorm *= .95;
    m_accumPNorm += 1;
    if (m_sliceType == I_SLICE)
        m_accumPQp += m_qp + m_ipOffset;
    else
        m_accumPQp += m_qp;
}
/** 函数功能             : 获取predictor应用的标号 0:不可参考b帧 1: P帧  2:I帧 3:可参考B帧
/*  调用范围             : 只在rateControlStart和RateControl::clipQscale函数中被调用
* \参数 lowresSliceType  : 在rateControlStart中:当前帧的帧类型(如X265_TYPE_IDR等)             在RateControl::clipQscale中:???
* \参数 sliceType        : 在rateControlStart中:当前帧的slice类型(如B_SLICE,P_SLICE,I_SLICE等)在RateControl::clipQscale中:???
* \返回                  : 返回predictor应用的标号 0:不可参考b帧 1: P帧  2:I帧 3:可参考B帧* */
int RateControl::getPredictorType(int lowresSliceType, int sliceType)
{
    /* Use a different predictor for B Ref and B frames for vbv frame size predictions */
    if (lowresSliceType == X265_TYPE_BREF)
        return 3;
    return sliceType;
}

double RateControl::getDiffLimitedQScale(RateControlEntry *rce, double q)
{
    // force I/B quants as a function of P quants
    const double lastPqScale    = m_lastQScaleFor[P_SLICE];
    const double lastNonBqScale = m_lastQScaleFor[m_lastNonBPictType];
    if (rce->sliceType == I_SLICE)
    {
        double iq = q;
        double pq = x265_qp2qScale(m_accumPQp / m_accumPNorm);
        double ipFactor = fabs(m_param->rc.ipFactor);
        /* don't apply ipFactor if the following frame is also I */
        if (m_accumPNorm <= 0)
            q = iq;
        else if (m_param->rc.ipFactor < 0)
            q = iq / ipFactor;
        else if (m_accumPNorm >= 1)
            q = pq / ipFactor;
        else
            q = m_accumPNorm * pq / ipFactor + (1 - m_accumPNorm) * iq;
    }
    else if (rce->sliceType == B_SLICE)
    {
        if (m_param->rc.pbFactor > 0)
            q = lastNonBqScale;
        if (!rce->keptAsRef)
            q *= fabs(m_param->rc.pbFactor);
    }
    else if (rce->sliceType == P_SLICE
             && m_lastNonBPictType == P_SLICE
             && rce->coeffBits == 0)
    {
        q = lastPqScale;
    }

    /* last qscale / qdiff stuff */
    if (m_lastNonBPictType == rce->sliceType &&
        (rce->sliceType != I_SLICE || m_lastAccumPNorm < 1))
    {
        double maxQscale = m_lastQScaleFor[rce->sliceType] * m_lstep;
        double minQscale = m_lastQScaleFor[rce->sliceType] / m_lstep;
        q = x265_clip3(minQscale, maxQscale, q);
    }

    m_lastQScaleFor[rce->sliceType] = q;
    if (rce->sliceType != B_SLICE)
        m_lastNonBPictType = rce->sliceType;
    if (rce->sliceType == I_SLICE)
    {
        m_lastAccumPNorm = m_accumPNorm;
        m_accumPNorm = 0;
        m_accumPQp = 0;
    }
    if (rce->sliceType == P_SLICE)
    {
        double mask = 1 - pow(rce->iCuCount / m_ncu, 2);
        m_accumPQp   = mask * (x265_qScale2qp(q) + m_accumPQp);
        m_accumPNorm = mask * (1 + m_accumPNorm);
    }

    x265_zone* zone = getZone();
    if (zone)
    {
        if (zone->bForceQp)
            q = x265_qp2qScale(zone->qp);
        else
            q /= zone->bitrateFactor;
    }
    return q;
}

double RateControl::countExpectedBits()
{
    double expectedBits = 0;
    for( int i = 0; i < m_numEntries; i++ )
    {
        RateControlEntry *rce = &m_rce2Pass[i];
        rce->expectedBits = (uint64_t)expectedBits;
        expectedBits += qScale2bits(rce, rce->newQScale);
    }
    return expectedBits;
}

bool RateControl::findUnderflow(double *fills, int *t0, int *t1, int over)
{
    /* find an interval ending on an overflow or underflow (depending on whether
     * we're adding or removing bits), and starting on the earliest frame that
     * can influence the buffer fill of that end frame. */
    const double bufferMin = .1 * m_bufferSize;
    const double bufferMax = .9 * m_bufferSize;
    double fill = fills[*t0 - 1];
    double parity = over ? 1. : -1.;
    int start = -1, end = -1;
    for (int i = *t0; i < m_numEntries; i++)
    {
        fill += (m_frameDuration * m_vbvMaxRate -
                 qScale2bits(&m_rce2Pass[i], m_rce2Pass[i].newQScale)) * parity;
        fill = x265_clip3(0.0, m_bufferSize, fill);
        fills[i] = fill;
        if (fill <= bufferMin || i == 0)
        {
            if (end >= 0)
                break;
            start = i;
        }
        else if (fill >= bufferMax && start >= 0)
            end = i;
    }
    *t0 = start;
    *t1 = end;
    return start >= 0 && end >= 0;
}

bool RateControl::fixUnderflow(int t0, int t1, double adjustment, double qscaleMin, double qscaleMax)
{
    double qscaleOrig, qscaleNew;
    bool adjusted = false;
    if (t0 > 0)
        t0++;
    for (int i = t0; i <= t1; i++)
    {
        qscaleOrig = m_rce2Pass[i].newQScale;
        qscaleOrig = x265_clip3(qscaleMin, qscaleMax, qscaleOrig);
        qscaleNew  = qscaleOrig * adjustment;
        qscaleNew  = x265_clip3(qscaleMin, qscaleMax, qscaleNew);
        m_rce2Pass[i].newQScale = qscaleNew;
        adjusted = adjusted || (qscaleNew != qscaleOrig);
    }
    return adjusted;
}

bool RateControl::cuTreeReadFor2Pass(Frame* frame)
{
    uint8_t sliceTypeActual = (uint8_t)m_rce2Pass[frame->m_poc].sliceType;

    if (m_rce2Pass[frame->m_poc].keptAsRef)
    {
        /* TODO: We don't need pre-lookahead to measure AQ offsets, but there is currently
         * no way to signal this */
        uint8_t type;
        if (m_cuTreeStats.qpBufPos < 0)
        {
            do
            {
                m_cuTreeStats.qpBufPos++;

                if (!fread(&type, 1, 1, m_cutreeStatFileIn))
                    goto fail;
                if (fread(m_cuTreeStats.qpBuffer[m_cuTreeStats.qpBufPos], sizeof(uint16_t), m_ncu, m_cutreeStatFileIn) != (size_t)m_ncu)
                    goto fail;

                if (type != sliceTypeActual && m_cuTreeStats.qpBufPos == 1)
                {
                    x265_log(m_param, X265_LOG_ERROR, "CU-tree frametype %d doesn't match actual frametype %d.\n", type, sliceTypeActual);
                    return false;
                }
            }
            while(type != sliceTypeActual);
        }
        for (int i = 0; i < m_ncu; i++)
        {
            int16_t qpFix8 = m_cuTreeStats.qpBuffer[m_cuTreeStats.qpBufPos][i];
            frame->m_lowres.qpCuTreeOffset[i] = (double)(qpFix8) / 256.0;
            frame->m_lowres.invQscaleFactor[i] = x265_exp2fix8(frame->m_lowres.qpCuTreeOffset[i]);
        }
        m_cuTreeStats.qpBufPos--;
    }
    return true;

fail:
    x265_log(m_param, X265_LOG_ERROR, "Incomplete CU-tree stats file.\n");
    return false;
}
/** 函数功能             : 根据当前已编码bits数目修正预估的qscale值并计算overflow值(qScale *= overflow)
/*  调用范围             : 只在rateEstimateQscale函数中被调用
* \参数 qScale           : qscale参数
* \返回                  : 返回修正后的qscale值 * */
double RateControl::tuneAbrQScaleFromFeedback(double qScale)
{
    double abrBuffer = 2 * m_rateTolerance * m_bitrate;//最大使用的ABRbuffer大小
    if (m_currentSatd)//如果当前帧的framecost不等于0
    {
        /* 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;//当前RC里面帧的总时长 减去并行个数加上当前的一个
        double wantedBits = timeDone * m_bitrate;//当前需要的总共bits数目 (RC里面的所有帧)
        int64_t encodedBits = m_totalBits;//获取当前RC已编码占用的bits
        if (m_param->totalFrames && m_param->totalFrames <= 2 * m_fps)//如果当前编码的总帧数不够2秒钟
        {
            abrBuffer = m_param->totalFrames * (m_bitrate / m_fps);//ABRbuffer修正为这些帧总共需要的空间
            encodedBits = m_encodedBits;//获取时间编码bits (without ammortization)
        }

        if (wantedBits > 0 && encodedBits > 0 && (!m_partialResidualFrames || 
            m_param->rc.bStrictCbr))                    //已经有编码数据
        {
            abrBuffer *= X265_MAX(1, sqrt(timeDone));//修正当前buffer
            overflow = x265_clip3(.5, 2.0, 1.0 + (encodedBits - wantedBits) / abrBuffer);//获取上溢值
            qScale *= overflow;//修正当前qscale
        }
    }
    return qScale;//返回修正后的qscale值
}
/** 函数功能             : ???分析加权信息(每个list的第一帧分析加权与否,其它不加权)
/*  调用范围             : 只在rateControlStart函数中被调用
* \参数 curFrame         : 当前编码帧
* \参数 rce              : ???当前编码帧
* \返回                  : ??null * */
double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
{
    //功能:???
    //     1. 如果是2pass环节 无须计算(只需验证是否帧类型一致) 如果是1pass并且应用ABR:计算滑动窗口平均framecost和 
    //     2. 如果当前是B帧:根据向后参考帧获取预估qscale值,获取当前帧预估bits 返回预估当前帧的qscale值
    //     3. 如果当前为I帧或者P帧:获取当前帧预估bits 返回预估当前帧的qscale值
    double q;//用于存储当前帧估计的qscale值

    if (m_2pass)//如果当前处在应用2pass
    {
        if (m_sliceType != rce->sliceType)//2pass中 如果帧类型不匹配报错
        {
            x265_log(m_param, X265_LOG_ERROR, "slice=%c but 2pass stats say %c\n",
                     g_sliceTypeToChar[m_sliceType], g_sliceTypeToChar[rce->sliceType]);
        }
    }
    else
    {
        if (m_isAbr)//应用ABR  功能:计算滑动窗口平均framecost和
        {
            double slidingWindowCplxSum = 0;//用于计算滑动窗口平均framecost和
            //计算方式:假设当前序列为 F0 F1 F2 F3 F4.....  
            //则: F0 = 0  F1 = F0*0.5  F2 = (F0*0.5 + F1)*0.5  F3 = ((F0*0.5 + F1)*0.5 + F2)*0.5  F4 = (((F0*0.5 + F1)*0.5 + F2)*0.5 + F3)*0.5
            int start = m_sliderPos > s_slidingWindowFrames ?  m_sliderPos : 0;//获取当前滑动窗口的下标  循环队列
            for (int cnt = 0; cnt < s_slidingWindowFrames; cnt++, start++)//获取滑动窗口framecost和
            {
                int pos = start % s_slidingWindowFrames;//获取当前下标
                slidingWindowCplxSum *= 0.5;
                if (!m_satdCostWindow[pos])
                    break;
                slidingWindowCplxSum += m_satdCostWindow[pos];//累加当前framecost
            }
            rce->movingAvgSum = slidingWindowCplxSum;//获取滑动窗口平均framecost和
            m_satdCostWindow[m_sliderPos % s_slidingWindowFrames] = rce->lastSatd;//获取当前下标
            m_sliderPos++;//用于标示下一帧的滑动窗口下标
        }
    }

    if (m_sliceType == B_SLICE)//如果当前是B帧:根据向后参考帧获取预估qscale值,获取当前帧预估bits 返回预估当前帧的qscale值
    {
        /* B-frames don't have independent rate control, but rather get the
         * average QP of the two adjacent P-frames + an offset */
        Slice* prevRefSlice = m_curSlice->m_refPicList[0][0]->m_encData->m_slice;//获取前向帧slice
        Slice* nextRefSlice = m_curSlice->m_refPicList[1][0]->m_encData->m_slice;//获取后向帧slice
        double q0 = m_curSlice->m_refPicList[0][0]->m_encData->m_avgQpRc;//获取前向参考帧的平均QP
        double q1 = m_curSlice->m_refPicList[1][0]->m_encData->m_avgQpRc;//获取后向参考帧的平均QP
        bool i0 = prevRefSlice->m_sliceType == I_SLICE;//判断前向参考帧是否为I帧
        bool i1 = nextRefSlice->m_sliceType == I_SLICE;//判断后向参考帧是否为I帧
        int dt0 = abs(m_curSlice->m_poc - prevRefSlice->m_poc);//获取当前编码帧与前向参考帧的距离
        int dt1 = abs(m_curSlice->m_poc - nextRefSlice->m_poc);//获取当前编码帧与后向参考帧的距离

        // Skip taking a reference frame before the Scenecut if ABR has been reset.
        if (m_lastAbrResetPoc >= 0)//如果前面有非B帧 ABR进行重置
        {
            if (prevRefSlice->m_sliceType == P_SLICE && prevRefSlice->m_poc < m_lastAbrResetPoc)//如果前向帧为P帧  并且  前向帧在重置位置之前
            {
                i0 = i1; //获取后向帧是否为I帧
                dt0 = dt1;//获取后向帧距离
                q0 = q1;//获取后向帧QP
            }
        }
        if (prevRefSlice->m_sliceType == B_SLICE && IS_REFERENCED(m_curSlice->m_refPicList[0][0]))//如果前向参考帧为B帧  并且 前向参考帧为可参考B帧
            q0 -= m_pbOffset / 2; //修正前向参考帧的平均QP
        if (nextRefSlice->m_sliceType == B_SLICE && IS_REFERENCED(m_curSlice->m_refPicList[1][0]))//如果后向参考帧为B帧  并且 后向参考帧为可参考B帧
            q1 -= m_pbOffset / 2;//修正前向参考帧的平均QP
        if (i0 && i1)//如果前向和后向参考帧都为I帧
            q = (q0 + q1) / 2 + m_ipOffset;//获取当前预估QP值:其平均值
        else if (i0)//如果前向参考帧为I帧
            q = q1;//获取后向帧的平均QP值
        else if (i1)//如果后向参考帧为I帧
            q = q0;//获取前向参考帧的平均QP值
        else
            q = (q0 * dt1 + q1 * dt0) / (dt0 + dt1);//获取按距离加权的平均QP值

        if (IS_REFERENCED(curFrame))//如果当前帧为可参考帧
            q += m_pbOffset / 2;//QP增加一半PBoffset
        else
            q += m_pbOffset;//QP增加PBoffset

        /* Set a min qp at scenechanges and transitions */
        if (m_isSceneTransition)//如果当前帧是场景切换帧
        {
            q = X265_MAX(ABR_SCENECUT_INIT_QP_MIN, q);//取最大QP
            double minScenecutQscale =x265_qp2qScale(ABR_SCENECUT_INIT_QP_MIN); //获取最小Qscale值
            m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]);//更新P帧最新qscale值
        }
        double qScale = x265_qp2qScale(q);//获取当前预估的qscale值
        rce->qpNoVbv = q;//获取未经VBV修正的qp参数
        double lmin = 0, lmax = 0;//暂存最大 和最小qscale值
        if (m_isVbv)//如果应用VBV
        {
            lmin = m_lastQScaleFor[P_SLICE] / m_lstep;//获取最小qscale
            lmax = m_lastQScaleFor[P_SLICE] * m_lstep;//获取最大qscale
            if (m_isCbr)//如果为CBR
            {
                qScale = tuneAbrQScaleFromFeedback(qScale);//根据当前已编码bits数目修正预估的qscale值并计算overflow值(qScale *= overflow)
                if (!m_isAbrReset)//如果当前没有被重置ABR
                    qScale = x265_clip3(lmin, lmax, qScale);//clip 当前的qscale值
                q = x265_qScale2qp(qScale);//获取qp量化参数
            }
            if (!m_2pass)//如果不是2pass
            {
                qScale = clipQscale(curFrame, rce, qScale);//根据下采样SATD信息修正qscale值
                /* clip qp to permissible range after vbv-lookahead estimation to avoid possible 
                 * mispredictions by initial frame size predictors */
                if (m_pred[m_predType].count == 1)//如果当前帧类型值update过一次predictor
                    qScale = x265_clip3(lmin, lmax, qScale);//clip操作 防止越界
                m_lastQScaleFor[m_sliceType] = qScale;//获取最新的qscale值
                rce->frameSizePlanned = predictSize(&m_pred[m_predType], qScale, (double)m_currentSatd);//预测当前帧需要的bits
            }
            else
                rce->frameSizePlanned = qScale2bits(rce, qScale);//根据1pass 数据 获取2pass当前计划占用的bits

            /* Limit planned size by MinCR */
            rce->frameSizePlanned = X265_MIN(rce->frameSizePlanned, rce->frameSizeMaximum);////预估bits与当前level下当前帧占用的最大bit去最小值
            rce->frameSizeEstimated = rce->frameSizePlanned;//获取预估bits
        }
        rce->newQScale = qScale;//获取当前的预估qscale
        return qScale;//返回预估的qscale
    }
    else //如果当前为I帧或者P帧
    {
        //功能:获取当前帧预估bits 返回预估当前帧的qscale值
        //    1. 如果当前是2pass:???
        //       否则如果当前不是2pass:预估当前帧的qscale值 并 根据下采样SATD信息修正qscale值(仅限VBV模式)
        //    2. 获取当前帧预估bits 返回预估当前帧的qscale值
        double abrBuffer = 2 * m_rateTolerance * m_bitrate;//获取当前可用buffer
        if (m_2pass)//????
        {
            int64_t diff;
            if (!m_isVbv)
            {
                m_predictedBits = m_totalBits;
                if (rce->encodeOrder < m_param->frameNumThreads)
                    m_predictedBits += (int64_t)(rce->encodeOrder * m_bitrate / m_fps);
                else
                    m_predictedBits += (int64_t)(m_param->frameNumThreads * m_bitrate / m_fps);
            }
            /* Adjust ABR buffer based on distance to the end of the video. */
            if (m_numEntries > rce->encodeOrder)
            {
                uint64_t finalBits = m_rce2Pass[m_numEntries - 1].expectedBits;
                double videoPos = (double)rce->expectedBits / finalBits;
                double scaleFactor = sqrt((1 - videoPos) * m_numEntries);
                abrBuffer *= 0.5 * X265_MAX(scaleFactor, 0.5);
            }
            diff = m_predictedBits - (int64_t)rce->expectedBits;
            q = rce->newQScale;
            q /= x265_clip3(0.5, 2.0, (double)(abrBuffer - diff) / abrBuffer);
            if (m_expectedBitsSum > 0)
            {
                /* Adjust quant based on the difference between
                 * achieved and expected bitrate so far */
                double curTime = (double)rce->encodeOrder / m_numEntries;
                double w = x265_clip3(0.0, 1.0, curTime * 100);
                q *= pow((double)m_totalBits / m_expectedBitsSum, w);
            }
            rce->qpNoVbv = x265_qScale2qp(q);
            if (m_isVbv)
            {
                /* Do not overflow vbv */
                double expectedSize = qScale2bits(rce, q);
                double expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
                double expectedFullness = rce->expectedVbv / m_bufferSize;
                double qmax = q * (2 - expectedFullness);
                double sizeConstraint = 1 + expectedFullness;
                qmax = X265_MAX(qmax, rce->newQScale);
                if (expectedFullness < .05)
                    qmax = MAX_MAX_QPSCALE;
                qmax = X265_MIN(qmax, MAX_MAX_QPSCALE);
                while (((expectedVbv < rce->expectedVbv/sizeConstraint) && (q < qmax)) ||
                        ((expectedVbv < 0) && (q < MAX_MAX_QPSCALE)))
                {
                    q *= 1.05;
                    expectedSize = qScale2bits(rce, q);
                    expectedVbv = m_bufferFill + m_bufferRate - expectedSize;
                }
            }
            q = x265_clip3(MIN_QPSCALE, MAX_MAX_QPSCALE, q);
        }
        else //不是2pass
        {
            /* 1pass ABR */

            /* Calculate the quantizer which would have produced the desired
             * average bitrate if it had been applied to all frames so far.
             * Then modulate that quant based on the current frame's complexity
             * relative to the average complexity so far (using the 2pass RCEQ).
             * Then bias the quant up or down if total size so far was far from
             * the target.
             * Result: Depending on the value of rate_tolerance, there is a
             * tradeoff between quality and bitrate precision. But at large
             * tolerances, the bit distribution approaches that of 2pass. */
            //功能:预估当前帧的qscale值 并 根据下采样SATD信息修正qscale值(仅限VBV模式)
            //     1.预估RC计算复杂度 初始化
            //     2.获取预估qscale
            //     3.如果应用VBV:根据下采样SATD信息修正qscale值  否则 只是简单的clip操作
            double overflow = 1;//用于调整当前估计的qscale *= overflow
            double lqmin = MIN_QPSCALE, lqmax = MAX_MAX_QPSCALE; //用于标记最大和最小qsclae 防止越界(后面会对其根据实际情况更新) 分别为 0.21249999999999999 615.46574234477100
            /*m_shortTermCplxSum 当前非B帧及以前所有非B帧 framecost * (fps/25)的平均和  
              计算方式: 设当前序列 I0BBBP1BBBP2BBBP3
              则当前m_shortTermCplxSum = (((I0*fps/25)*0.5 + P1*fps/25)*0.5 + P2*fps/25)*0.5 + P3
              m_shortTermCplxCount 当前帧以前非B帧个数 + 1
              I0 = 1
              P1 = I0*0.5 + 1 = 1.5
              P2 = P1*0.5 + 1 = 1.75
              P3 = P2*0.5 + 1 = 1.875
            **/
            m_shortTermCplxSum *= 0.5;//加权求和
            m_shortTermCplxCount *= 0.5;//加权求和
            m_shortTermCplxSum += m_currentSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);//计算当前的加权framecost fps越大,其权重越大
            m_shortTermCplxCount++; //累加
            /* coeffBits to be used in 2-pass */
            rce->coeffBits = (int)m_currentSatd;//获取当前帧的framecost值
            rce->blurredComplexity = m_shortTermCplxSum / m_shortTermCplxCount; //预估当前的计算复杂度 (当前非B帧及以前所有非B帧 framecost * (fps/25)的平均和) / (当前帧以前非B帧个数 + 1 )
            rce->mvBits = 0;//初始化
            rce->sliceType = m_sliceType;//无须重新赋值 冗余代码

            //功能:获取预估qscale
            if (m_param->rc.rateControlMode == X265_RC_CRF)//如果当前为CRF模式
            {
                q = getQScale(rce, m_rateFactorConstant);//获取预测qscale
            }
            else
            {
                if (!m_param->rc.bStatRead)//如果不是2pass读
                    checkAndResetABR(rce, false);//检测是否下溢(前面占用bits过少 防止当前非B帧占用bits过多)用于下溢检测并重置ABR
                double initialQScale = getQScale(rce, m_wantedBitsWindow / m_cplxrSum);//获取预测的qscale如果有对应zone信息对qcale进行修正 
                q = tuneAbrQScaleFromFeedback(initialQScale);//根据当前已编码bits数目修正预估的qscale值
                overflow = q / initialQScale;//获取tuneAbrQScaleFromFeedback计算的上溢值
            }
            if (m_sliceType == I_SLICE && m_param->keyframeMax > 1
                && m_lastNonBPictType != I_SLICE && !m_isAbrReset)//如果当前为I帧 并且IDR间隔大于1  并且 前一个非B帧不为I帧 并且没有重置 
            {
                if (!m_param->rc.bStrictCbr)//如果不是严格按照目标码率
                    q = x265_qp2qScale(m_accumPQp / m_accumPNorm);//直接获取平均qscale
                q /= fabs(m_param->rc.ipFactor);//获取I帧的qscale
            }
            else if (m_framesDone > 0)//如果RC中帧数大于0
            {
                if (m_param->rc.rateControlMode != X265_RC_CRF)//当前不为CRF模式
                {
                    lqmin = m_lastQScaleFor[m_sliceType] / m_lstep;//更新最小qscale
                    lqmax = m_lastQScaleFor[m_sliceType] * m_lstep;//更新最大qscale
                    if (!m_partialResidualFrames)//如果当前没有均摊帧数
                    {
                        if (overflow > 1.1 && m_framesDone > 3)
                            lqmax *= m_lstep;//更新最大qscale
                        else if (overflow < 0.9)
                            lqmin /= m_lstep;//更新最小qscale
                    }
                    q = x265_clip3(lqmin, lqmax, q);//clip当前qscale 防止越界
                }
            }
            else if (m_qCompress != 1 && m_param->rc.rateControlMode == X265_RC_CRF)//CRF模式
            {
                q = x265_qp2qScale(CRF_INIT_QP) / fabs(m_param->rc.ipFactor);//获取CFR模式下的qscale
            }
            else if (m_framesDone == 0 && !m_isVbv && m_param->rc.rateControlMode == X265_RC_ABR)//ABR 并且非VBV模式下 第一帧更新最大qscale
            {
                /* for ABR alone, clip the first I frame qp */
                lqmax = x265_qp2qScale(ABR_INIT_QP_MAX) * m_lstep;//更新最大qscale (根据初始最大qp)
                q = X265_MIN(lqmax, q);//防止越界 
            }
            q = x265_clip3(MIN_QPSCALE, MAX_MAX_QPSCALE, q);//clip操作防止越界
            /* Set a min qp at scenechanges and transitions */
            if (m_isSceneTransition)//如果当前是场景切换帧
            {
               double minScenecutQscale =x265_qp2qScale(ABR_SCENECUT_INIT_QP_MIN); //场景切换应用的最小QP 12 对应的qscale
               q = X265_MAX(minScenecutQscale, q);//取最大的qscale
               m_lastQScaleFor[P_SLICE] = X265_MAX(minScenecutQscale, m_lastQScaleFor[P_SLICE]);//更新P帧qscale
            }
            rce->qpNoVbv = x265_qScale2qp(q);//获取vbv修正前的量化参数 
            q = clipQscale(curFrame, rce, q);//如果应用VBV:根据下采样SATD信息修正qscale值  否则 只是简单的clip操作
            /*  clip qp to permissible range after vbv-lookahead estimation to avoid possible
             * mispredictions by initial frame size predictors, after each scenecut */
            bool isFrameAfterScenecut = m_sliceType!= I_SLICE && m_curSlice->m_refPicList[0][0]->m_lowres.bScenecut;// 当前帧紧挨帧场景切换帧(编码顺序)如果当前帧不是I帧 并且 其前向参考的第一帧为场景切换帧
            if (!m_2pass && m_isVbv && isFrameAfterScenecut)//如果当前为1pass 并且应用VBV 并且在场景切换帧后面
                q = x265_clip3(lqmin, lqmax, q);//clip操作
        }
        m_lastQScaleFor[m_sliceType] = q;//获取当分B帧的最新qscale
        if ((m_curSlice->m_poc == 0 || m_lastQScaleFor[P_SLICE] < q) && !(m_2pass && !m_isVbv))//(如果是第一帧或者P初始化qscale过小 )
            m_lastQScaleFor[P_SLICE] = q * fabs(m_param->rc.ipFactor);//修正P帧的最新qscale

        if (m_2pass && m_isVbv)//如果当前是2pass 并且应用VBV
            rce->frameSizePlanned = qScale2bits(rce, q);//???
        else
            rce->frameSizePlanned = predictSize(&m_pred[m_predType], q, (double)m_currentSatd);//获取当前帧预估的bits

        /* Always use up the whole VBV in this case. */
        if (m_singleFrameVbv)//如果单帧占用bits过大
            rce->frameSizePlanned = m_bufferRate;//直接将其置为:平均每帧最大的bits占用数目
        /* Limit planned size by MinCR */
        if (m_isVbv)//如果应用VBV
            rce->frameSizePlanned = X265_MIN(rce->frameSizePlanned, rce->frameSizeMaximum);//预估bits与当前level下当前帧占用的最大bit去最小值
        rce->frameSizeEstimated = rce->frameSizePlanned;//获取预估bits
        rce->newQScale = q;//获取当前的预估qscale
        return q;//返回预估的qscale
    }
}
/** 函数功能             : 当前帧编码一半时即时更新数据,便于后续帧快速估计  更新m_startEndOrder计数
/*  调用范围             : 只在processRowEncoder函数中被调用
* \参数 rce              : 当前帧RC相关数据
* \返回                  : null * */
void RateControl::rateControlUpdateStats(RateControlEntry* rce)
{
    if (!m_param->rc.bStatWrite && !m_param->rc.bStatRead)//没有应用多pass结构 既不是读模式 也不是写模式
    {
        if (rce->sliceType == I_SLICE)//如果当前为I帧
        {
            /* previous I still had a residual; roll it into the new loan */
            if (m_partialResidualFrames)//如果已经更新过数据
                rce->rowTotalBits += m_partialResidualCost * m_partialResidualFrames;//当前bits加上剩余bits 
            if ((m_param->totalFrames != 0) && (m_amortizeFrames > (m_param->totalFrames - m_framesDone)))//编码剩余帧数不够分摊帧数
            {
                m_amortizeFrames = 0;//置为0
                m_amortizeFraction = 0;//置为0
            }
            else
            {
                double depreciateRate = 1.1;//因子
                m_amortizeFrames = (int)(m_amortizeFrames / depreciateRate);//更新分摊帧数
                m_amortizeFraction /= depreciateRate;//更新分摊分数
                m_amortizeFrames = X265_MAX(m_amortizeFrames, MIN_AMORTIZE_FRAME);//不能小于默认最小值
                m_amortizeFraction = X265_MAX(m_amortizeFraction, MIN_AMORTIZE_FRACTION);//不能小于默认最小值
            }
            rce->amortizeFrames = m_amortizeFrames;//获取分摊帧数
            rce->amortizeFraction = m_amortizeFraction;//获取分摊因子
            m_partialResidualFrames = X265_MIN((int)rce->amortizeFrames, m_param->keyframeMax);//获取当前分摊帧数
            m_partialResidualCost = (int)((rce->rowTotalBits * rce->amortizeFraction) / m_partialResidualFrames);//获取当前I帧的剩余cost
            rce->rowTotalBits -= m_partialResidualCost * m_partialResidualFrames;//减去更新后的bits
        }
        else if (m_partialResidualFrames)//非I帧
        {
             rce->rowTotalBits += m_partialResidualCost;//修正增加当前I帧的剩余cost
             m_partialResidualFrames--;//减减
        }
    }
    if (rce->sliceType != B_SLICE)//非B帧
        rce->rowCplxrSum = rce->rowTotalBits * x265_qp2qScale(rce->qpaRc) / rce->qRceq;//获取cost值
    else
        rce->rowCplxrSum = rce->rowTotalBits * x265_qp2qScale(rce->qpaRc) / (rce->qRceq * fabs(m_param->rc.pbFactor));//获取cost值

    m_cplxrSum += rce->rowCplxrSum;//获取预测cost
    m_totalBits += rce->rowTotalBits;//累加RC中占有的bits

    /* do not allow the next frame to enter rateControlStart() until this
     * frame has updated its mid-frame statistics */
    //只有在此运行之后,下一个rateControlStart()才继续运行
    if (m_param->rc.rateControlMode == X265_RC_ABR || m_isVbv)//如果应用ABR或者VBV
    {
        m_startEndOrder.incr();//更新计数

        if (rce->encodeOrder < m_param->frameNumThreads - 1)//刚启动时多更新一次
            m_startEndOrder.incr(); // faked rateControlEnd calls for negative frames
    }
}
/** 函数功能             : 检测是否下溢(前面占用bits过少 防止当前非B帧占用bits过多)rateEstimateQscale中用于下溢检测并重置ABR  rateControlEnd用于关闭前面重置的标志位
/*  调用范围             : 只在rateEstimateQscale和RateControl::rateControlEnd函数中被调用
* \参数 rce              : 当前编码帧的RC参数
* \参数 isFrameDone      : 当前帧是否编码完毕 rateEstimateQscale为false rateControlEnd为true
* \返回                  : null * */
void RateControl::checkAndResetABR(RateControlEntry* rce, bool isFrameDone)
{
    //在rateEstimateQscale中只在非B帧中进入
    //在rateControlEnd中:所有帧类型都可以进入
    //一般都符合规则 在此不会改变什么
    double abrBuffer = 2 * m_rateTolerance * m_bitrate;//ARR最多用的buffer大小

    // Check if current Slice is a scene cut that follows low detailed/blank frames
    if (rce->lastSatd > 4 * rce->movingAvgSum)//如果当前帧的framecost 大于4倍的滑动窗口平均framecost和
    {
        if (!m_isAbrReset && rce->movingAvgSum > 0
            && (m_isPatternPresent || !m_param->bframes)) //如果m_isAbrReset为false并且滑动窗口平均cost大于0 并且 序列无B帧或连续大于bframes个数b帧cost相同 ((m_isPatternPresent || !m_param->bframes))这条很难达到一般不进入
        {
            int pos = X265_MAX(m_sliderPos - m_param->frameNumThreads, 0);//排除并行中的frame
            int64_t shrtTermWantedBits = (int64_t) (X265_MIN(pos, s_slidingWindowFrames) * m_bitrate * m_frameDuration);//当前窗口内应该拥有的bits
            int64_t shrtTermTotalBitsSum = 0; //统计当前窗口占用的实际bits
            // Reset ABR if prev frames are blank to prevent further sudden overflows/ high bit rate spikes.
            for (int i = 0; i < s_slidingWindowFrames ; i++)
                shrtTermTotalBitsSum += m_encodedBitsWindow[i];//统计当前窗口占用的实际bits
            double underflow = (shrtTermTotalBitsSum - shrtTermWantedBits) / abrBuffer;//前面由于B帧占用的bits过少,防止下溢,造成当前编码的非B帧占用的bits过大
            const double epsilon = 0.0001f;//阈值
            if (underflow < epsilon && !isFrameDone)//小于阈值并且在rateEstimateQscale函数进入的
            {
                init(*m_curSlice->m_sps);//重新初始化RC
                m_shortTermCplxSum = rce->lastSatd / (CLIP_DURATION(m_frameDuration) / BASE_FRAME_DURATION);//从开始统计非B帧cost 前面的丢弃
                m_shortTermCplxCount = 1;//重新计数
                m_isAbrReset = true;//标记ABR重置
                m_lastAbrResetPoc = rce->poc;//记录最后的重置poc位置
            }
        }
        else if (m_isAbrReset && isFrameDone)//rateControlEnd进入 用于清除已经set的flag
        {
            // Clear flag to reset ABR and continue as usual.
            m_isAbrReset = false;
        }
    }
}

void RateControl::hrdFullness(SEIBufferingPeriod *seiBP)
{
    const VUI* vui = &m_curSlice->m_sps->vuiParameters;
    const HRDInfo* hrd = &vui->hrdParameters;
    int num = 90000;
    int denom = hrd->bitRateValue << (hrd->bitRateScale + BR_SHIFT);
    reduceFraction(&num, &denom);
    int64_t cpbState = (int64_t)m_bufferFillFinal;
    int64_t cpbSize = (int64_t)hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT);

    if (cpbState < 0 || cpbState > cpbSize)
    {
        x265_log(m_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 = (uint32_t)(num * cpbState + denom) / denom;
    seiBP->m_initialCpbRemovalDelayOffset = (uint32_t)(num * cpbSize + denom) / denom - seiBP->m_initialCpbRemovalDelay;
}
/** 函数功能             : VBV中更新根据当前帧并行情况更新当前RCbuffer
/*  调用范围             : 只在RateControl::rateControlStart函数中被调用
* \参数 enc              : 上层encodr类
* \返回                  : null * */
void RateControl::updateVbvPlan(Encoder* enc)
{
    m_bufferFill = m_bufferFillFinal;//获取当前码率控制中一秒钟占用的最大的buffer
    enc->updateVbvPlan(this);//VBV中更新根据当前帧并行情况更新当前RCbuffer
}
/** 函数功能             : 根据当前帧类型的predictor qscale参数 以及对应下采样的SATD值 预估当前块/帧 占用的bits
/*  调用范围             : 只在rateControlStart、rateEstimateQscale、clipQscale、predictRowsSizeSum函数中被调用
* \参数 p                : rateControlStart:m_pred[m_predType]预测器(forceqp时应)、rateEstimateQscale:m_pred[m_predType]、clipQscale:m_pred[m_predType]预测器、predictRowsSizeSum:rowPred[0]、rowPred[1]
* \参数 q                : rateControlStart:当前量化参数(forceqp时应)、rateEstimateQscale:qscale参数、clipQscale:qscale参数、predictRowsSizeSum:当前的qscale值
* \参数 var              : rateControlStart:当前帧的framecost(forceqp时应)、rateEstimateQscale:当前帧的framecost、clipQscale:当前帧的framecost、predictRowsSizeSum:当前行未编码CTU的SATD和
* \返回                  : 返回预测bits * */
double RateControl::predictSize(Predictor *p, double q, double var)
{
    return (p->coeff * var + p->offset) / (q * p->count);//预测当前帧的bits
}
/** 函数功能             : 如果应用VBV:根据下采样SATD信息修正qscale值  否则 只是简单的clip操作
/*  调用范围             : 只在initPass2()、rateEstimateQscale函数中被调用
* \参数 curFrame         : 当前编码帧
* \参数 rce              : 当前编码帧的相关码率控制信息
* \参数 q                : qscale参数
* \返回                  : 返回修正后的qscale * */
double RateControl::clipQscale(Frame* curFrame, RateControlEntry* rce, double q)
{
    // B-frames are not directly subject to VBV,
    // since they are controlled by referenced P-frames' QPs.
    double q0 = q;//存储函数进来时的qscale值
    if (m_isVbv && m_currentSatd > 0 && curFrame)//framecost不为0
    {
        //功能:根据下采样SATD信息修正qscale值
        //    1. 如果应用lookachead 或者 应用cuTree 或者应用scencecut 或者 应用自适应B帧决策
        //           : 根据下采样SATD值预测未来的bits占用数目 从而修正当前的qscale
        //       否则:根据当前bits 修正当前的qscale
        //    2. 根据当前bits以及level定义值 修正qscale
        if (m_param->lookaheadDepth || m_param->rc.cuTree ||
            m_param->scenecutThreshold ||
            (m_param->bFrameAdaptive && m_param->bframes))//应用lookachead 或者 应用cuTree 或者应用scencecut 或则 应用自适应B帧决策
        {
           /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary
            * such that no frames in the lookahead overflow and such that the buffer
            * is in a reasonable state by the end of the lookahead. */
            int loopTerminate = 0;
            /* Avoid an infinite loop. */
            //功能:根据下采样SATD值预测未来的bits占用数目 从而修正当前的qscale
            for (int iterations = 0; iterations < 1000 && loopTerminate != 3; iterations++)
            {
                double frameQ[3];//用于存储IPB帧的加权qscale
                double curBits;//存储当前预测未来某帧占用的bits
                curBits = predictSize(&m_pred[m_predType], q, (double)m_currentSatd);//预测当前占用的bits
                double bufferFillCur = m_bufferFill - curBits;//当前剩余的bitsbuffer
                double targetFill;//存储目标填充
                double totalDuration = m_frameDuration; //当前播放一帧占用的时间(单位秒) 
                frameQ[P_SLICE] = m_sliceType == I_SLICE ? q * m_param->rc.ipFactor : (m_sliceType == B_SLICE ? q / m_param->rc.pbFactor : q);//如果当前是I帧 P帧qscale直接获取(q * m_param->rc.ipFactor)P帧直接获取q B帧 q / m_param->rc.pbFactor
                frameQ[B_SLICE] = frameQ[P_SLICE] * m_param->rc.pbFactor;//B帧 = P帧* m_param->rc.pbFactor
                frameQ[I_SLICE] = frameQ[P_SLICE] / m_param->rc.ipFactor;//I帧 = P帧 / m_param->rc.ipFactor
                /* Loop over the planned future frames. */
                for (int j = 0; bufferFillCur >= 0; j++)//循环遍历未来帧占用的bits 直到遍历一秒钟 或者 buffer剩余为0
                {
                    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;//占用的最大bits
                    if (bufferFillCur + wantedFrameSize <= m_bufferSize)//如果没有溢出
                        bufferFillCur += wantedFrameSize;//累加bits
                    int64_t satd = curFrame->m_lowres.plannedSatd[j] >> (X265_DEPTH - 8);//获取下采样cost值
                    type = IS_X265_TYPE_I(type) ? I_SLICE : IS_X265_TYPE_B(type) ? B_SLICE : P_SLICE;//获取slice类型
                    int predType = getPredictorType(curFrame->m_lowres.plannedType[j], type);//获取predictor标号
                    curBits = predictSize(&m_pred[predType], frameQ[type], (double)satd);//预测当前bits
                    bufferFillCur -= curBits;//剩余bitsbuffer 减去当前预测bits
                }

                /* Try to get the buffer at least 50% filled, but don't set an impossible goal. */
                double finalDur = 1;//存储遍历未来帧的秒数(一般是0.x秒)
                if (m_param->rc.bStrictCbr)//如果严格按照目标码率控制
                {
                    finalDur = x265_clip3(0.4, 1.0, totalDuration);//获取遍历未来帧的秒数(一般是0.x秒)
                }
                targetFill = X265_MIN(m_bufferFill + totalDuration * m_vbvMaxRate * 0.5 , m_bufferSize * (1 - 0.5 * finalDur));//获取目标填充bitsbuffer 不能超过一半buffer
                if (bufferFillCur < targetFill)//遍历剩余buffer 小于目标填充buffer  占用bits过大
                {
                    q *= 1.01;//提高qscale
                    loopTerminate |= 1;//或一
                    continue;
                }
                /* Try to get the buffer not more than 80% filled, but don't set an impossible goal. */
                targetFill = x265_clip3(m_bufferSize * (1 - 0.2 * finalDur), m_bufferSize, m_bufferFill - totalDuration * m_vbvMaxRate * 0.5);
                if (m_isCbr && bufferFillCur > targetFill && !m_isSceneTransition)//CBR模式 并且剩余buffer过大
                {
                    q /= 1.01;//降低qscale
                    loopTerminate |= 2;//或2
                    continue;
                }
                break;
            }
            q = X265_MAX(q0 / 2, q);//不能修正超过一半
        }
        else//一般不进入: 
        {
            /* Fallback to old purely-reactive algorithm: no lookahead. */
            if ((m_sliceType == P_SLICE || m_sliceType == B_SLICE ||
                    (m_sliceType == I_SLICE && m_lastNonBPictType == I_SLICE)) &&
                m_bufferFill / m_bufferSize < 0.5)//占用bits过少
            {
                q /= x265_clip3(0.5, 1.0, 2.0 * m_bufferFill / m_bufferSize);//调大qscale 因为除以的是一个小数
            }
            // Now a hard threshold to make sure the frame fits in VBV.
            // This one is mostly for I-frames.
            double bits = predictSize(&m_pred[m_predType], q, (double)m_currentSatd);//预测当前占用的bits

            // For small VBVs, allow the frame to use up the entire VBV.
            double maxFillFactor;
            maxFillFactor = m_bufferSize >= 5 * m_bufferRate ? 2 : 1;//最大因子
            // For single-frame VBVs, request that the frame use up the entire VBV.
            double minFillFactor = m_singleFrameVbv ? 1 : 2;//最小一字

            for (int iterations = 0; iterations < 10; iterations++)
            {
                double qf = 1.0;
                if (bits > m_bufferFill / maxFillFactor)
                    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;
                bits = predictSize(&m_pred[m_predType], q, (double)m_currentSatd);
            }

            q = X265_MAX(q0, q);//不能超过原来的qscale
        }

        /* Apply MinCR restrictions */
        double pbits = predictSize(&m_pred[m_predType], q, (double)m_currentSatd);//预测当前帧占用的bist
        if (pbits > rce->frameSizeMaximum)//如果bits大于当前level定义值
            q *= pbits / rce->frameSizeMaximum;//调大qscale
        /* To detect frames that are more complex in SATD costs compared to prev window, yet 
         * lookahead vbv reduces its qscale by half its value. Be on safer side and avoid drastic 
         * qscale reductions for frames high in complexity */
        bool mispredCheck = rce->movingAvgSum && m_currentSatd >= rce->movingAvgSum && q <= q0 / 2;
        if (!m_isCbr || (m_isAbr && mispredCheck))
            q = X265_MAX(q0, q);

        if (m_rateFactorMaxIncrement)//最大与配置之间的差值:m_param->rc.rfConstantMax - m_param->rc.rfConstant
        {
            double qpNoVbv = x265_qScale2qp(q0);//未经vbv修正的mv
            double qmax = X265_MIN(MAX_MAX_QPSCALE,x265_qp2qScale(qpNoVbv + m_rateFactorMaxIncrement));//最大qscale
            return x265_clip3(MIN_QPSCALE, qmax, q);//clip 防止越界
        }
    }
    if (m_2pass)//如果当前为2pass 根据最大qscale值和最小qscale 修正当前qscale
    {
        double min = log(MIN_QPSCALE);
        double max = log(MAX_MAX_QPSCALE);
        q = (log(q) - min) / (max - min) - 0.5;
        q = 1.0 / (1.0 + exp(-4 * q));
        q = q*(max - min) + min;
        return exp(q);
    }
    return x265_clip3(MIN_QPSCALE, MAX_MAX_QPSCALE, q);//返回修正后的qscale
}
/** 函数功能             : 计算当前帧已经编码bits加上未编码预测bits
/*  调用范围             : 只在rowDiagonalVbvRateControl函数中被调用
* \参数 curFrame         : 当前编码帧
* \参数 rce              : 当前帧的码率控制数据
* \参数 qpVbv            : 当前更新后的QP参数
* \参数 encodedBitsSoFar : 用于回存当前帧已经编码的bits
* \返回                  : 返回当前帧已经编码bits加上未编码预测bits * */
double RateControl::predictRowsSizeSum(Frame* curFrame, RateControlEntry* rce, double qpVbv, int32_t& encodedBitsSoFar)
{
    uint32_t rowSatdCostSoFar = 0, totalSatdBits = 0;//分别用于存储:当前已经编码完毕的CTU对应的SATD和、未编码预测bits
    encodedBitsSoFar = 0;//初始为0 累加当前已经编码完毕的bits

    double qScale = x265_qp2qScale(qpVbv);//量化参数转换为qscale
    FrameData& curEncData = *curFrame->m_encData;//获取当前帧的编码数据
    int picType = curEncData.m_slice->m_sliceType;//获取slice类型:B_SLICE,P_SLICE,I_SLICE
    Frame* refFrame = curEncData.m_slice->m_refPicList[0][0];//获取前向参考帧 

    uint32_t maxRows = curEncData.m_slice->m_sps->numCuInHeight;//CTU列数
    uint32_t maxCols = curEncData.m_slice->m_sps->numCuInWidth;//CTU行数

    for (uint32_t row = 0; row < maxRows; row++)//遍历所有CTU行
    {
        encodedBitsSoFar += curEncData.m_rowStat[row].encodedBits;//累加当前已经编码完毕的bits
        rowSatdCostSoFar = curEncData.m_rowStat[row].diagSatd;//累加当前已经编码完毕的CTU对应的SATD
        uint32_t satdCostForPendingCus = curEncData.m_rowStat[row].satdForVbv - rowSatdCostSoFar;//当前CTU行还未编码的SATD和
        satdCostForPendingCus >>= X265_DEPTH - 8;//根据像素位宽修正
        if (satdCostForPendingCus  > 0)//还有未编码的CTU
        {
            double pred_s = predictSize(rce->rowPred[0], qScale, satdCostForPendingCus);//预估当前行未编码CTU占用的bits
            uint32_t refRowSatdCost = 0, refRowBits = 0, intraCostForPendingCus = 0;//refRowSatdCost  用于累加当前行未编码CTU参考帧对应位置的下采样cost, refRowBits 累加当前行未编码CTU参考帧对应位置的bits, intraCostForPendingCus 当前行intraSATD值 减去 当前CTU行已编码CTU的对应下采样计算的intraSATD值 
            double refQScale = 0;//用于存储参考帧的refscale

            if (picType != I_SLICE)//如果当前不是I帧
            {
                FrameData& refEncData = *refFrame->m_encData;//获取参考帧的编码数据
                uint32_t endCuAddr = maxCols * (row + 1);//当前行最后一个CTU
                uint32_t startCuAddr = curEncData.m_rowStat[row].numEncodedCUs;//当前CTU行最后一个编码完毕的CTU位置
                if (startCuAddr)//如果当前行编码完毕一个以上CTU
                {
                    for (uint32_t cuAddr = startCuAddr + 1 ; cuAddr < endCuAddr; cuAddr++)//遍历当前帧所有未编码的CTU
                    {
                        refRowSatdCost += refEncData.m_cuStat[cuAddr].vbvCost;//累加参考帧对应位置的CTU cost 8x8下采样
                        refRowBits += refEncData.m_cuStat[cuAddr].totalBits;  //累加参考帧对应位置的CTU占用的bits
                    }
                }
                else//如果当前行刚编码完毕第一个CTU
                {
                    refRowBits = refEncData.m_rowStat[row].encodedBits;//直接获取参考帧对应行的bits
                    refRowSatdCost = refEncData.m_rowStat[row].satdForVbv;//直接获取参考帧对应行的下采样SATD值
                }

                refRowSatdCost >>= X265_DEPTH - 8;//根据位宽修正
                refQScale = refEncData.m_rowStat[row].diagQpScale;//获取参考帧对应的diagQP scale值
            }

            if (picType == I_SLICE || qScale >= refQScale)//如果当前为I帧或者 当前scale大于参考帧的scale
            {
                if (picType == P_SLICE 
                    && refFrame 
                    && refFrame->m_encData->m_slice->m_sliceType == picType
                    && refQScale > 0
                    && refRowSatdCost > 0)//如果当前帧为P帧 并且 参考帧的帧类型也是P帧 并且 参考帧qscale大于0 
                {
                    if (abs((int32_t)(refRowSatdCost - satdCostForPendingCus)) < (int32_t)satdCostForPendingCus / 2)//参考帧未编码cost 与当前帧未编码cost 差距比较小
                    {
                        double predTotal = refRowBits * satdCostForPendingCus / refRowSatdCost * refQScale / qScale;//根据参考帧的bits 预测当前未编码块的bits
                        totalSatdBits += (int32_t)((pred_s + predTotal) * 0.5);//累加当前行未编码CTU占用的bits 并加上预测bits
                        continue;
                    }
                }
                totalSatdBits += (int32_t)pred_s;//累加当前行未编码CTU占用的bits
            }
            else if (picType == P_SLICE)//如果是P帧
            {
                intraCostForPendingCus = curEncData.m_rowStat[row].intraSatdForVbv - curEncData.m_rowStat[row].diagIntraSatd;//当前行intraSATD值 减去 当前CTU行已编码CTU的对应下采样计算的intraSATD值 
                intraCostForPendingCus >>= X265_DEPTH - 8;//根据像素位宽修正
                /* Our QP is lower than the reference! */
                double pred_intra = predictSize(rce->rowPred[1], qScale, intraCostForPendingCus);//预测当前块的bits
                /* Sum: better to overestimate than underestimate by using only one of the two predictors. */
                totalSatdBits += (int32_t)(pred_intra + pred_s);//累加当前行未编码CTU占用的bit 以及intra估计的bits
            }
            else//如果是B帧
                totalSatdBits += (int32_t)pred_s;//累加当前行未编码CTU占用的bits
        }
    }

    return totalSatdBits + encodedBitsSoFar;//返回当前帧已经编码bits加上未编码预测bits
}
/** 函数功能             : 更新predictor、根据当前编码情况估计当前帧占用的bits、计算最优qp参数值、判断是否需要重新编码
/*  调用范围             : 只在processRowEncoder函数中被调用(只在对角线CTU进入)
* \参数 curFrame         : 当前编码帧
* \参数 row              : 当前CTU行号
* \参数 rce              : 当前帧的码率控制数据
* \参数 qpVbv            : 当前CTU对应的baseQp量化参数(更新qp存入此)
* \返回                  : 0:正常编码 -1:qp跳到过大 需要重新编码 * */
int RateControl::rowDiagonalVbvRateControl(Frame* curFrame, uint32_t row, RateControlEntry* rce, double& qpVbv)
{
    //功能:更新predictor、根据当前编码情况估计当前帧占用的bits、计算最优qp参数值、判断是否需要重新编码
    //     1.根据当前bits 更新predictor
    //     2.设置最大最小QP 以及剩余bits
    //     3.如果当前CTU行不是最后一个CTU行:根据当前编码情况估计当前帧占用的bits  计算最优qp参数值
    //       否则,是最后一个CTU行         :根据当前编码情况估计当前帧占用的bits  
    FrameData& curEncData = *curFrame->m_encData;//获取当前帧数据
    double qScaleVbv = x265_qp2qScale(qpVbv);//将量化参数转换为qscale值
    uint64_t rowSatdCost = curEncData.m_rowStat[row].diagSatd;//获取当前CTU行已编码CTU的对应下采样计算的SATD值
    double encodedBits = curEncData.m_rowStat[row].encodedBits;//获取当前CTU行已经编码CTU占用的bits累加值

    if (row == 1)//如果当前是第二个CTU行(从0计数所有当前是第二个CTU行)
    {
        rowSatdCost += curEncData.m_rowStat[0].diagSatd;//累加 第一行已编码的CTU对应下采样的SATD和
        encodedBits += curEncData.m_rowStat[0].encodedBits;//累加 第一CTU行已经编码CTU占用的bits累加值
    }
    rowSatdCost >>= X265_DEPTH - 8;//修正当前row的SATDcost
    updatePredictor(rce->rowPred[0], qScaleVbv, (double)rowSatdCost, encodedBits);//求predictor中系数的CplxSum和
    if (curEncData.m_slice->m_sliceType == P_SLICE)//如果当前为P帧
    {
        Frame* refFrame = curEncData.m_slice->m_refPicList[0][0];//获取前向参考帧
        if (qpVbv < refFrame->m_encData->m_rowStat[row].diagQp)//如果当前QP参数比参考帧QP参数小
        {
            uint64_t intraRowSatdCost = curEncData.m_rowStat[row].diagIntraSatd;//获取当前CTU行已编码CTU的对应下采样计算的intraSATD值
            if (row == 1)//如果是第二个CTU行
                intraRowSatdCost += curEncData.m_rowStat[0].diagIntraSatd;//累加第一行的intraSATD值
            intraRowSatdCost >>= X265_DEPTH - 8;//修正
            updatePredictor(rce->rowPred[1], qScaleVbv, (double)intraRowSatdCost, encodedBits);//求predictor中系数的CplxSum和
        }
    }

    int canReencodeRow = 1;//表示是否可以重新编码当前行 (默认是)
    /* tweak quality based on difference from predicted size */
    double prevRowQp = qpVbv;//当前CTU对应的baseQp量化参数
    double qpAbsoluteMax = QP_MAX_MAX;//RC控制下的最大QP 69
    double qpAbsoluteMin = QP_MIN;//最小QP 0
    if (m_rateFactorMaxIncrement)//如果当前是CRF模式 并且 有配置最大crf值
        qpAbsoluteMax = X265_MIN(qpAbsoluteMax, rce->qpNoVbv + m_rateFactorMaxIncrement);//更新最大QP

    if (m_rateFactorMaxDecrement)//如果当前是CRF模式 并且 有配置最大crf值
        qpAbsoluteMin = X265_MAX(qpAbsoluteMin, rce->qpNoVbv - m_rateFactorMaxDecrement);//更新最小QP

    double qpMax = X265_MIN(prevRowQp + m_param->rc.qpStep, qpAbsoluteMax);//当前最大浮动到的QP最大值
    double qpMin = X265_MAX(prevRowQp - m_param->rc.qpStep, qpAbsoluteMin);//当前最大浮动到的QP最小值
    double stepSize = 0.5;//QP参数缩减的步长
    double bufferLeftPlanned = rce->bufferFill - rce->frameSizePlanned;//当前buffer除去当前帧预测bits还剩下的buffer大小

    const SPS& sps = *curEncData.m_slice->m_sps;//获取当前的SPS
    double maxFrameError = X265_MAX(0.05, 1.0 / sps.numCuInHeight);//错误因子 CTU行总数的倒数

    if (row < sps.numCuInHeight - 1)//不是最后一个CTU行
    {
        /* More threads means we have to be more cautious in letting ratecontrol use up extra bits. */
        double rcTol = bufferLeftPlanned / m_param->frameNumThreads * m_rateTolerance;//在此存储buffer剩余的空间 考虑到并行操作:除以当前的frame并行个数
        int32_t encodedBitsSoFar = 0;//存储目前当前帧已经编码完毕的CTU占用bits
        double accFrameBits = predictRowsSizeSum(curFrame, rce, qpVbv, encodedBitsSoFar);//计算当前帧已经编码bits加上未编码预测bits (理解为一帧预估bits)

        /* * Don't increase the row QPs until a sufficent amount of the bits of
         * the frame have been processed, in case a flat area at the top of the
         * frame was measured inaccurately. */
        if (encodedBitsSoFar < 0.05f * rce->frameSizePlanned)//如果当前已编码bits占用不够计划bits的5%
            qpMax = qpAbsoluteMax = prevRowQp;//由于bits占用过少,后面可以适当增加质量 在此降低最大qP设定

        if (rce->sliceType != I_SLICE || (m_param->rc.bStrictCbr && rce->poc > 0))//如果当前不是I帧 或者采用严格目标码率执行并且不是第一帧
            rcTol *= 0.5;//降低剩余buffer 使当前帧占用更少的bits

        if (!m_isCbr)//如果当前不是CBR模式
            qpMin = X265_MAX(qpMin, rce->qpNoVbv);//最小QP 取当前QP

        double totalBitsNeeded = m_wantedBitsWindow;//获取当前应该需要的bits
        if (m_param->totalFrames)
            totalBitsNeeded = (m_param->totalFrames * m_bitrate) / m_fps;//获取视频需要的总的bits数目
        double abrOvershoot = (accFrameBits + m_totalBits - m_wantedBitsWindow) / totalBitsNeeded;//表示:当前帧超出的bits占总序列bits的比值 (预估当前帧bits + 当前已用bits - 当前应该需要的bits)/(整个视频序列需要的bits)

        //当前bits占用过多 循环增加QP步长
        while (qpVbv < qpMax                                                   //保证小于最大QP设定                                             
               && (((accFrameBits > rce->frameSizePlanned + rcTol) ||          // 保证当前帧的预估bits 小于 计算bits加上剩余buffer大小         
                   (rce->bufferFill - accFrameBits < bufferLeftPlanned * 0.5) ||  //保证只占用剩余buffer 的一半  
                   (accFrameBits > rce->frameSizePlanned && qpVbv < rce->qpNoVbv))//当前估计bits大于实际占用bits
                   && (!m_param->rc.bStrictCbr ? 1 : abrOvershoot > 0.1)))        //占用bits过大
        {
            qpVbv += stepSize;//增加QP
            accFrameBits = predictRowsSizeSum(curFrame, rce, qpVbv, encodedBitsSoFar);//计算当前帧已经编码bits加上未编码预测bits (理解为一帧预估bits)
            abrOvershoot = (accFrameBits + m_totalBits - m_wantedBitsWindow) / totalBitsNeeded;//表示:当前帧超出的bits占总序列bits的比值 (预估当前帧bits + 当前已用bits - 当前应该需要的bits)/(整个视频序列需要的bits)
        }

        //当前bits占用过小 减小QP
        while (qpVbv > qpMin                                                  //保证大于最小QP设定
               && (qpVbv > curEncData.m_rowStat[0].diagQp || m_singleFrameVbv) //当前QP过大
               && (((accFrameBits < rce->frameSizePlanned * 0.8f && qpVbv <= prevRowQp)  //当前bits占用过小
                   || accFrameBits < (rce->bufferFill - m_bufferSize + m_bufferRate) * 1.1)
                   && (!m_param->rc.bStrictCbr ? 1 : abrOvershoot < 0)))
        {
            qpVbv -= stepSize;//减小QP
            accFrameBits = predictRowsSizeSum(curFrame, rce, qpVbv, encodedBitsSoFar);//计算当前帧已经编码bits加上未编码预测bits (理解为一帧预估bits)
            abrOvershoot = (accFrameBits + m_totalBits - m_wantedBitsWindow) / totalBitsNeeded;//表示:当前帧超出的bits占总序列bits的比值 (预估当前帧bits + 当前已用bits - 当前应该需要的bits)/(整个视频序列需要的bits)
        }

        if (m_param->rc.bStrictCbr && m_param->totalFrames)//如果使其更严格的按照目标码率进行编码 
        {
            double timeDone = (double)(m_framesDone) / m_param->totalFrames;//当前已经进入过RC的帧数的比重
            while (qpVbv < qpMax && (qpVbv < rce->qpNoVbv + (m_param->rc.qpStep * timeDone)) &&
                   (timeDone > 0.75 && abrOvershoot > 0))
            {
                qpVbv += stepSize;//增加QP
                accFrameBits = predictRowsSizeSum(curFrame, rce, qpVbv, encodedBitsSoFar);//计算当前帧已经编码bits加上未编码预测bits (理解为一帧预估bits)
                abrOvershoot = (accFrameBits + m_totalBits - m_wantedBitsWindow) / totalBitsNeeded;//表示:当前帧超出的bits占总序列bits的比值 (预估当前帧bits + 当前已用bits - 当前应该需要的bits)/(整个视频序列需要的bits)
            }
            if (qpVbv > curEncData.m_rowStat[0].diagQp &&
                abrOvershoot < -0.1 && timeDone > 0.5 && accFrameBits < rce->frameSizePlanned - rcTol)
            {
                qpVbv -= stepSize;//减小QP
                accFrameBits = predictRowsSizeSum(curFrame, rce, qpVbv, encodedBitsSoFar);//计算当前帧已经编码bits加上未编码预测bits (理解为一帧预估bits)
            }
        }

        /* avoid VBV underflow or MinCr violation */
        while ((qpVbv < qpAbsoluteMax)
               && ((rce->bufferFill - accFrameBits < m_bufferRate * maxFrameError) ||
                   (rce->frameSizeMaximum - accFrameBits < rce->frameSizeMaximum * maxFrameError)))//占用bits依然过大
        {
            qpVbv += stepSize;//增加QP
            accFrameBits = predictRowsSizeSum(curFrame, rce, qpVbv, encodedBitsSoFar);//计算当前帧已经编码bits加上未编码预测bits (理解为一帧预估bits)
        }

        rce->frameSizeEstimated = accFrameBits;//获取当前帧估计的bits

        /* If the current row was large enough to cause a large QP jump, try re-encoding it. */
        if (qpVbv > qpMax && prevRowQp < qpMax && canReencodeRow)//如果当前QP超过设定QP值  QP波动过大
        {
            /* Bump QP to halfway in between... close enough. */
            qpVbv = x265_clip3(prevRowQp + 1.0f, qpMax, (prevRowQp + qpVbv) * 0.5);//重设QP
            return -1;//跳出 重新编码
        }

        if (m_param->rc.rfConstantMin)//CRF模式 有配置最小CRF值
        {
            if (qpVbv < qpMin && prevRowQp > qpMin && canReencodeRow)
            {
                qpVbv = x265_clip3(qpMin, prevRowQp, (prevRowQp + qpVbv) * 0.5);//重设QP
                return -1;//跳出 重新编码
            }
        }
    }
    else
    {
        int32_t encodedBitsSoFar = 0;//存储目前当前帧已经编码完毕的CTU占用bits
        rce->frameSizeEstimated = predictRowsSizeSum(curFrame, rce, qpVbv, encodedBitsSoFar);//计算当前帧已经编码bits加上未编码预测bits (理解为一帧预估bits)

        /* Last-ditch attempt: if the last row of the frame underflowed the VBV,
         * try again. */
        if ((rce->frameSizeEstimated > (rce->bufferFill - m_bufferRate * maxFrameError) &&
             qpVbv < qpMax && canReencodeRow)) //占用bits过大
        {
            qpVbv = qpMax;//重设qp
            return -1;//跳出 重新编码
        }
    }
    return 0;//正常
}

/* modify the bitrate curve from pass1 for one frame */
/** 函数功能             : 在1pass中只在非B帧应用获取预测的qscale如果有对应zone信息对qcale进行修正、在2pass中对所有帧应用获取预测的qscale如果有对应zone信息对qcale进行修正
/*  调用范围             : 只在initPass2()和rateEstimateQscale(只在非B帧应用)函数中被调用 注意:1pass中只在rateEstimateQscale函数中调用 2pass中只在initPass2()调用
* \参数 rce              : 码流控制数据 在initPass2():m_rce2Pass 在rateEstimateQscale:当前编码帧的RC参数 
* \参数 rateFactor       : 参数因子在initPass2():1.0 rateFactor  在rateEstimateQscale:在非B帧中进入 CRF:m_rateFactorConstant ABR:m_wantedBitsWindow / m_cplxrSum (当前需要的总bits/(已编码bits*qscale/m_lastRceq))
* \返回                  : 返回qscale值 * */
double RateControl::getQScale(RateControlEntry *rce, double rateFactor)
{
    double q;//用于存储qscale

    if (m_param->rc.cuTree)//如果应用cutree
    {
        // 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);//值:(fps/25)^(1 - m_param->rc.qCompress)
    }
    else
        q = pow(rce->blurredComplexity, 1 - m_param->rc.qCompress);// rce->blurredComplexity = (当前非B帧及以前所有非B帧 framecost * (fps/25)的平均和) / (当前帧以前非B帧个数 + 1 )
    // avoid NaN's in the Rceq
    if (rce->coeffBits + rce->mvBits == 0)//很少有此情况 一般不进入 在1pass中此处rce->mvBits = 0 前面已经初始化  2pass中会有具体数据
        q = m_lastQScaleFor[rce->sliceType];//选取默认qscale
    else
    {
        m_lastRceq = q;//获取未经过加权的qscale
        q /= rateFactor;//加权qscale
    }
    
    x265_zone* zone = getZone();//获取zone信息
    if (zone)//如果有zone信息
    {
        if (zone->bForceQp)//固定qp模式
            q = x265_qp2qScale(zone->qp);//获取qp对应的scale
        else
            q /= zone->bitrateFactor;//将当前的scale加权
    }
    return q;//返回qscale
}
/** 函数功能             : 求predictor中系数的CplxSum和
/*  调用范围             : 只在rowDiagonalVbvRateControl和RateControl::updateVbv函数中被调用 
* \参数 p                : 在rowDiagonalVbvRateControl:rowPred[0] 和 rowPred[1] 在RateControl::updateVbv:m_pred[predType] 
* \参数 q                : 在rowDiagonalVbvRateControl:qscale值 在RateControl::updateVbv:rce->qpaRc 
* \参数 var              : 在rowDiagonalVbvRateControl:当前row已编码CTU的对应下采样SATD值的累加和  在RateControl::updateVbv:当前帧的SATD值 framecost 
* \参数 bits             : 在rowDiagonalVbvRateControl:当前row已编码CTU占用bits的累加和 在RateControl::updateVbv:当前帧编码占用的实际bits 
* \返回                  : null * */
void RateControl::updatePredictor(Predictor *p, double q, double var, double bits)
{
    if (var < 10) //小采样cost太小 无须更新
        return;
    const double range = 2;//用于clip的返回
    double old_coeff = p->coeff / p->count;//获取先前predictor中coeff的平均值
    double new_coeff = bits * q / var;//用当前编码的实际bits 估计原先占用的系数为多少
    double new_coeff_clipped = x265_clip3(old_coeff / range, old_coeff * range, new_coeff);//clip操作,使当前coeff与 先前coeff不能差距过大
    double new_offset = bits * q - new_coeff_clipped * var;//求最新offset new coeff与clip coeff之间的差距
    if (new_offset >= 0)//new coeff比clip结果大
        new_coeff = new_coeff_clipped;//更新为clip结果
    else
        new_offset = 0;//new coeff比clip结果小,直接取offset为0
    p->count  *= p->decay;//先前count 乘以0.5
    p->coeff  *= p->decay;//先前coeff 乘以0.5
    p->offset *= p->decay;//先前offset 乘以0.5
    p->count++;//更新当前count个数
    p->coeff  += new_coeff;//更新最新coeff和
    p->offset += new_offset;//更新最新offset和
}
/** 函数功能             : 更新帧predictor并且更新当前最新的VBVbuffer
/*  调用范围             : 只在rateControlEnd函数中被调用
* \参数 bits             : 当前帧编码完毕占用的实际bits数目
* \参数 rce              : 当前帧码率控制数据
* \返回                  : null * */
void RateControl::updateVbv(int64_t bits, RateControlEntry* rce)
{
    int predType = rce->sliceType;//获取当前的slice类型 B_SLICE,P_SLICE,I_SLICE 
    predType = rce->sliceType == B_SLICE && rce->keptAsRef ? 3 : predType;//获取标号 用于predictor
    if (rce->lastSatd >= m_ncu && rce->encodeOrder >= m_lastPredictorReset)
        updatePredictor(&m_pred[predType], x265_qp2qScale(rce->qpaRc), (double)rce->lastSatd, (double)bits);//???
    if (!m_isVbv)//如果不应用VBV
        return;

    m_bufferFillFinal -= bits;//当前预设bufferbits 减去当前帧的bits

    if (m_bufferFillFinal < 0)//如果当前预设buffer不够 输出警告信息
        x265_log(m_param, X265_LOG_WARNING, "poc:%d, VBV underflow (%.0f bits)\n", rce->poc, m_bufferFillFinal);

    m_bufferFillFinal = X265_MAX(m_bufferFillFinal, 0);//clip操作 防止为负数
    m_bufferFillFinal += m_bufferRate;//加上新的一帧最大占用的bits
    m_bufferFillFinal = X265_MIN(m_bufferFillFinal, m_bufferSize);//防止溢出 不能超过最大的bits数目
}

/* After encoding one frame, update rate control state */
/** 函数功能             : ???计算估计当前帧应用的量化参数
/*  调用范围             : 只在FrameEncoder::compressFrame()函数中被调用
* \参数 curFrame         : 当前编码帧
* \参数 bits             : 当前帧编码完毕占用的总bits数目
* \参数 rce              : 当前帧的RC编码参数类
* \返回                  : 只会返回0 表示正常结束 * */
int RateControl::rateControlEnd(Frame* curFrame, int64_t bits, RateControlEntry* rce)
{
    //功能:???
    //     1. 等待触发(使RC按顺序进入)
    //     2. 统计QP信息
    //     3. 更新bits相关数据结构
    //     4. ???
    int orderValue = m_startEndOrder.get();//获取RC线程控制数据
    int endOrdinal = (rce->encodeOrder + m_param->frameNumThreads) * 2 - 1;//执行End触发条件
    while (orderValue < endOrdinal && !m_bTerminated)//循环等待
    {
        /* no more frames are being encoded, so fake the start event if we would
         * have blocked on it. Note that this does not enforce rateControlEnd()
         * ordering during flush, but this has no impact on the outputs */
        if (m_finalFrameCount && orderValue >= 2 * m_finalFrameCount)//在读取全部帧时的执行End触发条件
            break;
        orderValue = m_startEndOrder.waitForChange(orderValue);//一直等待数据改变
    }

    FrameData& curEncData = *curFrame->m_encData;//获取编码帧数据
    int64_t actualBits = bits;//获取当前帧实际编码bits
    Slice *slice = curEncData.m_slice;//获取当前slice

    if (m_param->rc.aqMode || m_isVbv)//如若应用自适应量化 或者应用VBV
    {
        if (m_isVbv)//如果应用VBV
        {
            /* determine avg QP decided by VBV rate control */
            for (uint32_t i = 0; i < slice->m_sps->numCuInHeight; i++)
                curEncData.m_avgQpRc += curEncData.m_rowStat[i].sumQpRc; //累加当前帧所有CTU行的sumQpRc和

            curEncData.m_avgQpRc /= slice->m_sps->numCUsInFrame;//获取平均值
            rce->qpaRc = curEncData.m_avgQpRc;//获取平均QP
        }

        if (m_param->rc.aqMode)//如果应用自适应量化
        {
            /* determine actual avg encoded QP, after AQ/cutree adjustments */
            for (uint32_t i = 0; i < slice->m_sps->numCuInHeight; i++)//遍历CTU行
                curEncData.m_avgQpAq += curEncData.m_rowStat[i].sumQpAq;//累加所有CTU行 CU的mqp

            curEncData.m_avgQpAq /= (slice->m_sps->numCUsInFrame * NUM_4x4_PARTITIONS);//求平均值
        }
        else
            curEncData.m_avgQpAq = curEncData.m_avgQpRc;//获取VBVqp值
    }

    if (m_isAbr)//如果应用ABR
    {
        if (m_param->rc.rateControlMode == X265_RC_ABR && !m_param->rc.bStatRead)//如果应用ABR并且当前不是2pass读情况
            checkAndResetABR(rce, true);//用于关闭前面重置的标志位

        if (m_param->rc.rateControlMode == X265_RC_CRF)//如果应用CRF模式
        {
            if (int(curEncData.m_avgQpRc + 0.5) == slice->m_sliceQp)
                curEncData.m_rateFactor = m_rateFactorConstant;//获取相应值
            else
            {
                /* If vbv changed the frame QP recalculate the rate-factor */
                double baseCplx = m_ncu * (m_param->bframes ? 120 : 80);
                double mbtree_offset = m_param->rc.cuTree ? (1.0 - m_param->rc.qCompress) * 13.5 : 0;
                curEncData.m_rateFactor = pow(baseCplx, 1 - m_qCompress) /
                    x265_qp2qScale(int(curEncData.m_avgQpRc + 0.5) + mbtree_offset);//重新计算
            }
        }
    }

    if (m_isAbr && !m_isAbrReset)//如果应用ABR 并且 没有重置过
    {
        /* amortize part of each I slice over the next several frames, up to
         * keyint-max, to avoid over-compensating for the large I slice cost */
        if (!m_param->rc.bStatWrite && !m_param->rc.bStatRead)//不使用多pass结构
        {
            if (rce->sliceType == I_SLICE)//如果当前为I帧
            {
                /* previous I still had a residual; roll it into the new loan */
                if (m_residualFrames)//如果有剩余分摊帧数
                    bits += m_residualCost * m_residualFrames;//当前bits加上剩余均摊cost
                m_residualFrames = X265_MIN((int)rce->amortizeFrames, m_param->keyframeMax);//确定当前分摊帧数
                m_residualCost = (int)((bits * rce->amortizeFraction) / m_residualFrames);//剩余帧分摊bits占用的平均cost
                bits -= m_residualCost * m_residualFrames;
            }
            else if (m_residualFrames)//非I帧 并且有剩余分摊帧数
            {
                bits += m_residualCost;//当前bits加上平均均摊cost
                m_residualFrames--;//均摊帧数减一
            }
        }
        if (rce->sliceType != B_SLICE)//如果是非B帧
        {
            /* The factor 1.5 is to tune up the actual bits, otherwise the cplxrSum is scaled too low
                * to improve short term compensation for next frame. */
            m_cplxrSum += (bits * x265_qp2qScale(rce->qpaRc) / rce->qRceq) - (rce->rowCplxrSum);//获取一帧的bits*qscale值
        }
        else
        {
            /* Depends on the fact that B-frame's QP is an offset from the following P-frame's.
                * 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);//获取一帧的bits*qscale值
        }
        m_wantedBitsWindow += m_frameDuration * m_bitrate;//累加当前需要的bits
        m_totalBits += bits - rce->rowTotalBits;//累加当前RC占用的bits(扣除前面累加的rowcount行占用的bits)
        m_encodedBits += actualBits;//累加实际占用bits
        int pos = m_sliderPos - m_param->frameNumThreads;//获取滑动窗口下标
        if (pos >= 0)
            m_encodedBitsWindow[pos % s_slidingWindowFrames] = actualBits;//滑动窗口获取实际bits
    }

    if (m_2pass)//如果当前处在2pass ????
    {
        m_expectedBitsSum += qScale2bits(rce, x265_qp2qScale(rce->newQp));
        m_totalBits += bits - rce->rowTotalBits;
    }

    if (m_isVbv)//应用VBV
    {
        updateVbv(actualBits, rce);//更新帧predictor并且更新当前最新的VBVbuffer

        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);
            }

            uint32_t cpbsizeUnscale = hrd->cpbSizeValue << (hrd->cpbSizeScale + CPB_SHIFT);
            rce->hrdTiming->cpbFinalAT = m_prevCpbFinalAT = rce->hrdTiming->cpbInitialAT + actualBits / cpbsizeUnscale;
            rce->hrdTiming->dpbOutputTime = (double)rce->picTimingSEI->m_picDpbOutputDelay * time->numUnitsInTick / time->timeScale + rce->hrdTiming->cpbRemovalTime;
        }
    }
    rce->isActive = false;//设置当前帧已经确定好QP值
    // Allow rateControlStart of next frame only when rateControlEnd of previous frame is over
    m_startEndOrder.incr();//控制RC 线程数据 加加
    return 0;//返回0 表示正常结束
}

/* 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;
    char cType = rce->sliceType == I_SLICE ? (rce->poc > 0 && m_param->bOpenGOP ? 'i' : 'I')
        : rce->sliceType == P_SLICE ? 'P'
        : IS_REFERENCED(curFrame) ? 'B' : 'b';
    if (fprintf(m_statFileOut,
                "in:%d out:%d type:%c q:%.2f q-aq:%.2f tex:%d mv:%d misc:%d icu:%.2f pcu:%.2f scu:%.2f ;\n",
                rce->poc, rce->encodeOrder,
                cType, curEncData.m_avgQpRc, curEncData.m_avgQpAq,
                curFrame->m_encData->m_frameStats.coeffBits,
                curFrame->m_encData->m_frameStats.mvBits,
                curFrame->m_encData->m_frameStats.miscBits,
                curFrame->m_encData->m_frameStats.percent8x8Intra * m_ncu,
                curFrame->m_encData->m_frameStats.percent8x8Inter * m_ncu,
                curFrame->m_encData->m_frameStats.percent8x8Skip  * m_ncu) < 0)
        goto writeFailure;
    /* Don't re-write the data in multi-pass mode. */
    if (m_param->rc.cuTree && IS_REFERENCED(curFrame) && !m_param->rc.bStatRead)
    {
        uint8_t sliceType = (uint8_t)rce->sliceType;
        for (int i = 0; i < m_ncu; i++)
                m_cuTreeStats.qpBuffer[0][i] = (uint16_t)(curFrame->m_lowres.qpCuTreeOffset[i] * 256.0);
        if (fwrite(&sliceType, 1, 1, m_cutreeStatFileOut) < 1)
            goto writeFailure;
        if (fwrite(m_cuTreeStats.qpBuffer[0], sizeof(uint16_t), m_ncu, m_cutreeStatFileOut) < (size_t)m_ncu)
            goto writeFailure;
    }
    return 0;

    writeFailure:
    x265_log(m_param, X265_LOG_ERROR, "RatecontrolEnd: stats file write failure\n");
    return 1;
}
#if defined(_MSC_VER)
#pragma warning(disable: 4996) // POSIX function names are just fine, thank you
#endif

/* called when the encoder is flushing, and thus the final frame count is
 * unambiguously known */
void RateControl::setFinalFrameCount(int count)
{
    m_finalFrameCount = count;
    /* unblock waiting threads */
    m_startEndOrder.poke();
}

/* called when the encoder is closing, and no more frames will be output.
 * all blocked functions must finish so the frame encoder threads can be
 * closed */
void RateControl::terminate()
{
    m_bTerminated = true;
    /* unblock waiting threads */
    m_startEndOrder.poke();
}

void RateControl::destroy()
{
    const char *fileName = m_param->rc.statFileName;
    if (!fileName)
        fileName = s_defaultStatFileName;

    if (m_statFileOut)
    {
        fclose(m_statFileOut);
        char *tmpFileName = strcatFilename(fileName, ".temp");
        int bError = 1;
        if (tmpFileName)
        {
           unlink(fileName);
           bError = rename(tmpFileName, fileName);
        }
        if (bError)
        {
            x265_log(m_param, X265_LOG_ERROR, "failed to rename output stats file to \"%s\"\n",
                     fileName);
        }
        X265_FREE(tmpFileName);
    }

    if (m_cutreeStatFileOut)
    {
        fclose(m_cutreeStatFileOut);
        char *tmpFileName = strcatFilename(fileName, ".cutree.temp");
        char *newFileName = strcatFilename(fileName, ".cutree");
        int bError = 1;
        if (tmpFileName && newFileName)
        {
           unlink(newFileName);
           bError = rename(tmpFileName, newFileName);
        }
        if (bError)
        {
            x265_log(m_param, X265_LOG_ERROR, "failed to rename cutree output stats file to \"%s\"\n",
                     newFileName);
        }
        X265_FREE(tmpFileName);
        X265_FREE(newFileName);
    }

    if (m_cutreeStatFileIn)
        fclose(m_cutreeStatFileIn);

    X265_FREE(m_rce2Pass);
    for (int i = 0; i < 2; i++)
        X265_FREE(m_cuTreeStats.qpBuffer[i]);
    
    X265_FREE(m_param->rc.zones);
}


 

#include "odrive_main.h" #include <algorithm> #include <algorithm> Controller::Controller(Config_t& config) : config_(config) { update_filter_gains(); } void Controller::reset() { pos_setpoint_ = 0.0f; vel_setpoint_ = 0.0f; vel_integrator_torque_ = 0.0f; torque_setpoint_ = 0.0f; } void Controller::set_error(Error error) { error_ |= error; axis_->error_ |= Axis::ERROR_CONTROLLER_FAILED; } //-------------------------------- // Command Handling //-------------------------------- bool Controller::select_encoder(size_t encoder_num) { if (encoder_num < AXIS_COUNT) { Axis* ax = axes[encoder_num]; pos_estimate_circular_src_ = &ax->encoder_.pos_circular_; pos_wrap_src_ = &config_.circular_setpoint_range; pos_estimate_linear_src_ = &ax->encoder_.pos_estimate_; pos_estimate_valid_src_ = &ax->encoder_.pos_estimate_valid_; vel_estimate_src_ = &ax->encoder_.vel_estimate_; vel_estimate_valid_src_ = &ax->encoder_.vel_estimate_valid_; return true; } else { return set_error(Controller::ERROR_INVALID_LOAD_ENCODER), false; } } void Controller::move_to_pos(float goal_point) { axis_->trap_traj_.planTrapezoidal(goal_point, pos_setpoint_, vel_setpoint_, axis_->trap_traj_.config_.vel_limit, axis_->trap_traj_.config_.accel_limit, axis_->trap_traj_.config_.decel_limit); axis_->trap_traj_.t_ = 0.0f; trajectory_done_ = false; } void Controller::move_incremental(float displacement, bool from_input_pos = true){ if(from_input_pos){ input_pos_ += displacement; } else{ input_pos_ = pos_setpoint_ + displacement; } input_pos_updated(); } void Controller::start_anticogging_calibration() { // Ensure the cogging map was correctly allocated earlier and that the motor is capable of calibrating if (axis_->error_ == Axis::ERROR_NONE) { config_.anticogging.calib_anticogging = true; } } /* * This anti-cogging implementation iterates through each encoder position, * waits for zero velocity & position error, * then samples the current required to maintain that position. * * This holding current is added as a feedforward term in the control loop. */ bool Controller::anticogging_calibration(float pos_estimate, float vel_estimate) { float pos_err = input_pos_ - pos_estimate; if (std::abs(pos_err) <= config_.anticogging.calib_pos_threshold / (float)axis_->encoder_.config_.cpr && std::abs(vel_estimate) < config_.anticogging.calib_vel_threshold / (float)axis_->encoder_.config_.cpr) { config_.anticogging.cogging_map[std::clamp<uint32_t>(config_.anticogging.index++, 0, 3600)] = vel_integrator_torque_; } if (config_.anticogging.index < 3600) { config_.control_mode = CONTROL_MODE_POSITION_CONTROL; input_pos_ = config_.anticogging.index * axis_->encoder_.getCoggingRatio(); input_vel_ = 0.0f; input_torque_ = 0.0f; input_pos_updated(); return false; } else { config_.anticogging.index = 0; config_.control_mode = CONTROL_MODE_POSITION_CONTROL; input_pos_ = 0.0f; // Send the motor home input_vel_ = 0.0f; input_torque_ = 0.0f; input_pos_updated(); anticogging_valid_ = true; config_.anticogging.calib_anticogging = false; return true; } } void Controller::update_filter_gains() { float bandwidth = std::min(config_.input_filter_bandwidth, 0.25f * current_meas_hz); input_filter_ki_ = 2.0f * bandwidth; // basic conversion to discrete time input_filter_kp_ = 0.25f * (input_filter_ki_ * input_filter_ki_); // Critically damped } static float limitVel(const float vel_limit, const float vel_estimate, const float vel_gain, const float torque) { float Tmax = (vel_limit - vel_estimate) * vel_gain; float Tmin = (-vel_limit - vel_estimate) * vel_gain; return std::clamp(torque, Tmin, Tmax); } bool Controller::update(float* torque_setpoint_output) { float* pos_estimate_linear = (pos_estimate_valid_src_ && *pos_estimate_valid_src_) ? pos_estimate_linear_src_ : nullptr; float* pos_estimate_circular = (pos_estimate_valid_src_ && *pos_estimate_valid_src_) ? pos_estimate_circular_src_ : nullptr; float* vel_estimate_src = (vel_estimate_valid_src_ && *vel_estimate_valid_src_) ? vel_estimate_src_ : nullptr; // Calib_anticogging is only true when calibration is occurring, so we can't block anticogging_pos float anticogging_pos = axis_->encoder_.pos_estimate_ / axis_->encoder_.getCoggingRatio(); if (config_.anticogging.calib_anticogging) { if (!axis_->encoder_.pos_estimate_valid_ || !axis_->encoder_.vel_estimate_valid_) { set_error(ERROR_INVALID_ESTIMATE); return false; } // non-blocking anticogging_calibration(axis_->encoder_.pos_estimate_, axis_->encoder_.vel_estimate_); } // TODO also enable circular deltas for 2nd order filter, etc. if (config_.circular_setpoints) { // Keep pos setpoint from drifting input_pos_ = fmodf_pos(input_pos_, config_.circular_setpoint_range); } // Update inputs switch (config_.input_mode) { case INPUT_MODE_INACTIVE: { // do nothing } break; case INPUT_MODE_PASSTHROUGH: { pos_setpoint_ = input_pos_; vel_setpoint_ = input_vel_; torque_setpoint_ = input_torque_; } break; case INPUT_MODE_VEL_RAMP: { float max_step_size = std::abs(current_meas_period * config_.vel_ramp_rate); float full_step = input_vel_ - vel_setpoint_; float step = std::clamp(full_step, -max_step_size, max_step_size); vel_setpoint_ += step; torque_setpoint_ = (step / current_meas_period) * config_.inertia; } break; case INPUT_MODE_TORQUE_RAMP: { float max_step_size = std::abs(current_meas_period * config_.torque_ramp_rate); float full_step = input_torque_ - torque_setpoint_; float step = std::clamp(full_step, -max_step_size, max_step_size); torque_setpoint_ += step; } break; case INPUT_MODE_POS_FILTER: { // 2nd order pos tracking filter float delta_pos = input_pos_ - pos_setpoint_; // Pos error float delta_vel = input_vel_ - vel_setpoint_; // Vel error float accel = input_filter_kp_*delta_pos + input_filter_ki_*delta_vel; // Feedback torque_setpoint_ = accel * config_.inertia; // Accel vel_setpoint_ += current_meas_period * accel; // delta vel pos_setpoint_ += current_meas_period * vel_setpoint_; // Delta pos } break; case INPUT_MODE_MIRROR: { if (config_.axis_to_mirror < AXIS_COUNT) { pos_setpoint_ = axes[config_.axis_to_mirror]->encoder_.pos_estimate_ * config_.mirror_ratio; vel_setpoint_ = axes[config_.axis_to_mirror]->encoder_.vel_estimate_ * config_.mirror_ratio; } else { set_error(ERROR_INVALID_MIRROR_AXIS); return false; } } break; // case INPUT_MODE_MIX_CHANNELS: { // // NOT YET IMPLEMENTED // } break; case INPUT_MODE_TRAP_TRAJ: { if(input_pos_updated_){ move_to_pos(input_pos_); input_pos_updated_ = false; } // Avoid updating uninitialized trajectory if (trajectory_done_) break; if (axis_->trap_traj_.t_ > axis_->trap_traj_.Tf_) { // Drop into position control mode when done to avoid problems on loop counter delta overflow config_.control_mode = CONTROL_MODE_POSITION_CONTROL; pos_setpoint_ = input_pos_; vel_setpoint_ = 0.0f; torque_setpoint_ = 0.0f; trajectory_done_ = true; } else { TrapezoidalTrajectory::Step_t traj_step = axis_->trap_traj_.eval(axis_->trap_traj_.t_); pos_setpoint_ = traj_step.Y; vel_setpoint_ = traj_step.Yd; torque_setpoint_ = traj_step.Ydd * config_.inertia; axis_->trap_traj_.t_ += current_meas_period; } anticogging_pos = pos_setpoint_; // FF the position setpoint instead of the pos_estimate } break; default: { set_error(ERROR_INVALID_INPUT_MODE); return false; } } // Position control // TODO Decide if we want to use encoder or pll position here float gain_scheduling_multiplier = 1.0f; float vel_des = vel_setpoint_; if (config_.control_mode >= CONTROL_MODE_POSITION_CONTROL) { float pos_err; if (config_.circular_setpoints) { if(!pos_estimate_circular) { set_error(ERROR_INVALID_ESTIMATE); return false; } // Keep pos setpoint from drifting pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap_src_); // Circular delta pos_err = pos_setpoint_ - *pos_estimate_circular; pos_err = wrap_pm(pos_err, 0.5f * *pos_wrap_src_); } else { if(!pos_estimate_linear) { set_error(ERROR_INVALID_ESTIMATE); return false; } pos_err = pos_setpoint_ - *pos_estimate_linear; } vel_des += config_.pos_gain * pos_err; // V-shaped gain shedule based on position error float abs_pos_err = std::abs(pos_err); if (config_.enable_gain_scheduling && abs_pos_err <= config_.gain_scheduling_width) { gain_scheduling_multiplier = abs_pos_err / config_.gain_scheduling_width; } } // Velocity limiting float vel_lim = config_.vel_limit; if (config_.enable_vel_limit) { vel_des = std::clamp(vel_des, -vel_lim, vel_lim); } // Check for overspeed fault (done in this module (controller) for cohesion with vel_lim) if (config_.enable_overspeed_error) { // 0.0f to disable if (!vel_estimate_src) { set_error(ERROR_INVALID_ESTIMATE); return false; } if (std::abs(*vel_estimate_src) > config_.vel_limit_tolerance * vel_lim) { set_error(ERROR_OVERSPEED); return false; } } // TODO: Change to controller working in torque units // Torque per amp gain scheduling (ACIM) float vel_gain = config_.vel_gain; float vel_integrator_gain = config_.vel_integrator_gain; if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { float effective_flux = axis_->motor_.current_control_.acim_rotor_flux; float minflux = axis_->motor_.config_.acim_gain_min_flux; if (fabsf(effective_flux) < minflux) effective_flux = std::copysignf(minflux, effective_flux); vel_gain /= effective_flux; vel_integrator_gain /= effective_flux; // TODO: also scale the integral value which is also changing units. // (or again just do control in torque units) } // Velocity control float torque = torque_setpoint_; // Anti-cogging is enabled after calibration // We get the current position and apply a current feed-forward // ensuring that we handle negative encoder positions properly (-1 == motor->encoder.encoder_cpr - 1) if (anticogging_valid_ && config_.anticogging.anticogging_enabled) { torque += config_.anticogging.cogging_map[std::clamp(mod((int)anticogging_pos, 3600), 0, 3600)]; } float v_err = 0.0f; if (config_.control_mode >= CONTROL_MODE_VELOCITY_CONTROL) { if (!vel_estimate_src) { set_error(ERROR_INVALID_ESTIMATE); return false; } v_err = vel_des - *vel_estimate_src; torque += (vel_gain * gain_scheduling_multiplier) * v_err; // Velocity integral action before limiting torque += vel_integrator_torque_; } // Velocity limiting in current mode if (config_.control_mode < CONTROL_MODE_VELOCITY_CONTROL && config_.enable_current_mode_vel_limit) { if (!vel_estimate_src) { set_error(ERROR_INVALID_ESTIMATE); return false; } torque = limitVel(config_.vel_limit, *vel_estimate_src, vel_gain, torque); } // Torque limiting bool limited = false; float Tlim = axis_->motor_.max_available_torque(); if (torque > Tlim) { limited = true; torque = Tlim; } if (torque < -Tlim) { limited = true; torque = -Tlim; } // Velocity integrator (behaviour dependent on limiting) if (config_.control_mode < CONTROL_MODE_VELOCITY_CONTROL) { // reset integral if not in use vel_integrator_torque_ = 0.0f; } else { if (limited) { // TODO make decayfactor configurable vel_integrator_torque_ *= 0.99f; } else { vel_integrator_torque_ += ((vel_integrator_gain * gain_scheduling_multiplier) * current_meas_period) * v_err; } } if (torque_setpoint_output) *torque_setpoint_output = torque; return true; } 解釋這段程式
11-07
<!-- go/cmark --> <!--* freshness: {owner: 'sprang' reviewed: '2021-04-12'} *--> # Paced Sending The paced sender, often referred to as just the "pacer", is a part of the WebRTC RTP stack used primarily to smooth the flow of packets sent onto the network. ## Background Consider a video stream at 5Mbps and 30fps. This would in an ideal world result in each frame being ~21kB large and packetized into 18 RTP packets. While the average bitrate over say a one second sliding window would be a correct 5Mbps, on a shorter time scale it can be seen as a burst of 167Mbps every 33ms, each followed by a 32ms silent period. Further, it is quite common that video encoders overshoot the target frame size in case of sudden movement especially dealing with screensharing. Frames being 10x or even 100x larger than the ideal size is an all too real scenario. These packet bursts can cause several issues, such as congesting networks and causing buffer bloat or even packet loss. Most sessions have more than one media stream, e.g. a video and an audio track. If you put a frame on the wire in one go, and those packets take 100ms to reach the other side - that means you have now blocked any audio packets from reaching the remote end in time as well. The paced sender solves this by having a buffer in which media is queued, and then using a _leaky bucket_ algorithm to pace them onto the network. The buffer contains separate fifo streams for all media tracks so that e.g. audio can be prioritized over video - and equal prio streams can be sent in a round-robin fashion to avoid any one stream blocking others. Since the pacer is in control of the bitrate sent on the wire, it is also used to generate padding in cases where a minimum send rate is required - and to generate packet trains if bitrate probing is used. ## Life of a Packet The typical path for media packets when using the paced sender looks something like this: 1. `RTPSenderVideo` or `RTPSenderAudio` packetizes media into RTP packets. 2. The packets are sent to the [RTPSender] class for transmission. 3. The pacer is called via [RtpPacketSender] interface to enqueue the packet batch. 4. The packets are put into a queue within the pacer awaiting opportune moments to send them. 5. At a calculated time, the pacer calls the `PacingController::PacketSender()` callback method, normally implemented by the [PacketRouter] class. 6. The router forwards the packet to the correct RTP module based on the packet's SSRC, and in which the `RTPSenderEgress` class makes final time stamping, potentially records it for retransmissions etc. 7. The packet is sent to the low-level `Transport` interface, after which it is now out of scope. Asynchronously to this, the estimated available send bandwidth is determined - and the target send rate is set on the `RtpPacketPacer` via the `void SetPacingRates(DataRate pacing_rate, DataRate padding_rate)` method. ## Packet Prioritization The pacer prioritized packets based on two criteria: * Packet type, with most to least prioritized: 1. Audio 2. Retransmissions 3. Video and FEC 4. Padding * Enqueue order The enqueue order is enforced on a per stream (SSRC) basis. Given equal priority, the [RoundRobinPacketQueue] alternates between media streams to ensure no stream needlessly blocks others. ## Implementations The main class to use is called [TaskQueuePacedSender]. It uses a task queue to manage thread safety and schedule delayed tasks, but delegates most of the actual work to the `PacingController` class. This way, it's possible to develop a custom pacer with different scheduling mechanism - but ratain the same pacing logic. ## The Packet Router An adjacent component called [PacketRouter] is used to route packets coming out of the pacer and into the correct RTP module. It has the following functions: * The `SendPacket` method looks up an RTP module with an SSRC corresponding to the packet for further routing to the network. * If send-side bandwidth estimation is used, it populates the transport-wide sequence number extension. * Generate padding. Modules supporting payload-based padding are prioritized, with the last module to have sent media always being the first choice. * Returns any generated FEC after having sent media. * Forwards REMB and/or TransportFeedback messages to suitable RTP modules. At present the FEC is generated on a per SSRC basis, so is always returned from an RTP module after sending media. Hopefully one day we will support covering multiple streams with a single FlexFEC stream - and the packet router is the likely place for that FEC generator to live. It may even be used for FEC padding as an alternative to RTX. ## The API The section outlines the classes and methods relevant to a few different use cases of the pacer. ### Packet sending For sending packets, use `RtpPacketSender::EnqueuePackets(std::vector<std::unique_ptr<RtpPacketToSend>> packets)` The pacer takes a `PacingController::PacketSender` as constructor argument, this callback is used when it's time to actually send packets. ### Send rates To control the send rate, use `void SetPacingRates(DataRate pacing_rate, DataRate padding_rate)` If the packet queue becomes empty and the send rate drops below `padding_rate`, the pacer will request padding packets from the `PacketRouter`. In order to completely suspend/resume sending data (e.g. due to network availability), use the `Pause()` and `Resume()` methods. The specified pacing rate may be overriden in some cases, e.g. due to extreme encoder overshoot. Use `void SetQueueTimeLimit(TimeDelta limit)` to specify the longest time you want packets to spend waiting in the pacer queue (pausing excluded). The actual send rate may then be increased past the pacing_rate to try to make the _average_ queue time less than that requested limit. The rationale for this is that if the send queue is say longer than three seconds, it's better to risk packet loss and then try to recover using a key-frame rather than cause severe delays. ### Bandwidth estimation If the bandwidth estimator supports bandwidth probing, it may request a cluster of packets to be sent at a specified rate in order to gauge if this causes increased delay/loss on the network. Use the `void CreateProbeCluster(...)` method - packets sent via this `PacketRouter` will be marked with the corresponding cluster_id in the attached `PacedPacketInfo` struct. If congestion window pushback is used, the state can be updated using `SetCongestionWindow()` and `UpdateOutstandingData()`. A few more methods control how we pace: * `SetAccountForAudioPackets()` determines if audio packets count into bandwidth consumed. * `SetIncludeOverhead()` determines if the entire RTP packet size counts into bandwidth used (otherwise just media payload). * `SetTransportOverhead()` sets an additional data size consumed per packet, representing e.g. UDP/IP headers. ### Stats Several methods are used to gather statistics in pacer state: * `OldestPacketWaitTime()` time since the oldest packet in the queue was added. * `QueueSizeData()` total bytes currently in the queue. * `FirstSentPacketTime()` absolute time the first packet was sent. * `ExpectedQueueTime()` total bytes in the queue divided by the send rate. [RTPSender]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/rtp_rtcp/source/rtp_sender.h;drc=77ee8542dd35d5143b5788ddf47fb7cdb96eb08e [RtpPacketSender]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/api/rtp_packet_sender.h;drc=ea55b0872f14faab23a4e5dbcb6956369c8ed5dc [RtpPacketPacer]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/rtp_packet_pacer.h;drc=e7bc3a347760023dd4840cf6ebdd1e6c8592f4d7 [PacketRouter]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/packet_router.h;drc=3d2210876e31d0bb5c7de88b27fd02ceb1f4e03e [TaskQueuePacedSender]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/task_queue_paced_sender.h;drc=5051693ada61bc7b78855c6fb3fa87a0394fa813 [RoundRobinPacketQueue]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/round_robin_packet_queue.h;drc=b571ff48f8fe07678da5a854cd6c3f5dde02855f 翻译
最新发布
12-03
#define CAMERA_MODEL_XIAO_ESP32S3 #include <I2S.h> #include <BLE2902.h> #include <BLEDevice.h> #include <BLEUtils.h> #include <BLEScan.h> #include <BLEAdvertisedDevice.h> #include "esp_camera.h" #include "camera_pins.h" #include "mulaw.h" // Audio // Uncomment to switch the codec // Opus is still under development // Mulaw is used with the web app // PCM is used with the Friend app // To use with the web app, comment CODEC_PCM and // uncomment CODEC_MULAW // #define CODEC_OPUS // #define CODEC_MULAW #define CODEC_PCM #ifdef CODEC_OPUS #include <opus.h> #define OPUS_APPLICATION OPUS_APPLICATION_VOIP #define OPUS_BITRATE 16000 OpusEncoder *opus_encoder = nullptr; #define CHANNELS 1 #define MAX_PACKET_SIZE 1000 #define SAMPLE_RATE 16000 #define SAMPLE_BITS 16 #else #ifdef CODEC_MULAW #define SAMPLE_RATE 8000 #define SAMPLE_BITS 16 #else #define FRAME_SIZE 160 #define SAMPLE_RATE 16000 #define SAMPLE_BITS 16 #endif #endif // // BLE // // Device Information Service #define DEVICE_INFORMATION_SERVICE_UUID (uint16_t)0x180A #define MANUFACTURER_NAME_STRING_CHAR_UUID (uint16_t)0x2A29 #define MODEL_NUMBER_STRING_CHAR_UUID (uint16_t)0x2A24 #define FIRMWARE_REVISION_STRING_CHAR_UUID (uint16_t)0x2A26 #define HARDWARE_REVISION_STRING_CHAR_UUID (uint16_t)0x2A27 // Battery Level Service #define BATTERY_SERVICE_UUID (uint16_t)0x180F #define BATTERY_LEVEL_CHAR_UUID (uint16_t)0x2A19 // Main Friend Service static BLEUUID serviceUUID("19B10000-E8F2-537E-4F6C-D104768A1214"); static BLEUUID audioDataUUID("19B10001-E8F2-537E-4F6C-D104768A1214"); static BLEUUID audioCodecUUID("19B10002-E8F2-537E-4F6C-D104768A1214"); static BLEUUID photoDataUUID("19B10005-E8F2-537E-4F6C-D104768A1214"); static BLEUUID photoControlUUID("19B10006-E8F2-537E-4F6C-D104768A1214"); BLECharacteristic *audioDataCharacteristic; BLECharacteristic *photoDataCharacteristic; BLECharacteristic *photoControlCharacteristic; BLECharacteristic *batteryLevelCharacteristic; // State bool connected = false; uint16_t audio_frame_count = 0; bool isCapturingPhotos = false; int captureInterval = 0; unsigned long lastCaptureTime = 0; size_t sent_photo_bytes = 0; size_t sent_photo_frames = 0; bool photoDataUploading = false; uint8_t batteryLevel = 100; unsigned long lastBatteryUpdate = 0; void handlePhotoControl(int8_t controlValue); class ServerHandler : public BLEServerCallbacks { void onConnect(BLEServer *server) { connected = true; } void onDisconnect(BLEServer *server) { connected = false; BLEDevice::startAdvertising(); } }; class PhotoControlCallback : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *characteristic) { if (characteristic->getLength() == 1) { handlePhotoControl(characteristic->getData()[0]); } } }; void configure_ble() { BLEDevice::init("OpenGlass"); BLEServer *server = BLEDevice::createServer(); // Main service BLEService *service = server->createService(serviceUUID); // Audio characteristics audioDataCharacteristic = service->createCharacteristic( audioDataUUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY ); BLE2902 *ccc = new BLE2902(); ccc->setNotifications(true); audioDataCharacteristic->addDescriptor(ccc); BLECharacteristic *audioCodecCharacteristic = service->createCharacteristic( audioCodecUUID, BLECharacteristic::PROPERTY_READ); #ifdef CODEC_OPUS uint8_t codecId = 20; // Opus 16khz #else #ifdef CODEC_MULAW uint8_t codecId = 11; // MuLaw 8khz #else uint8_t codecId = 1; // PCM 8khz #endif #endif audioCodecCharacteristic->setValue(&codecId, 1); // Photo characteristics photoDataCharacteristic = service->createCharacteristic( photoDataUUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); ccc = new BLE2902(); ccc->setNotifications(true); photoDataCharacteristic->addDescriptor(ccc); BLECharacteristic *photoControlCharacteristic = service->createCharacteristic( photoControlUUID, BLECharacteristic::PROPERTY_WRITE); photoControlCharacteristic->setCallbacks(new PhotoControlCallback()); uint8_t controlValue = 0; photoControlCharacteristic->setValue(&controlValue, 1); // Device Information Service BLEService *deviceInfoService = server->createService(DEVICE_INFORMATION_SERVICE_UUID); BLECharacteristic *manufacturerNameCharacteristic = deviceInfoService->createCharacteristic( MANUFACTURER_NAME_STRING_CHAR_UUID, BLECharacteristic::PROPERTY_READ); BLECharacteristic *modelNumberCharacteristic = deviceInfoService->createCharacteristic( MODEL_NUMBER_STRING_CHAR_UUID, BLECharacteristic::PROPERTY_READ); BLECharacteristic *firmwareRevisionCharacteristic = deviceInfoService->createCharacteristic( FIRMWARE_REVISION_STRING_CHAR_UUID, BLECharacteristic::PROPERTY_READ); BLECharacteristic *hardwareRevisionCharacteristic = deviceInfoService->createCharacteristic( HARDWARE_REVISION_STRING_CHAR_UUID, BLECharacteristic::PROPERTY_READ); manufacturerNameCharacteristic->setValue("Based Hardware"); modelNumberCharacteristic->setValue("OpenGlass"); firmwareRevisionCharacteristic->setValue("1.0.1"); hardwareRevisionCharacteristic->setValue("Seeed Xiao ESP32S3 Sense"); // Battery Service BLEService *batteryService = server->createService(BATTERY_SERVICE_UUID); batteryLevelCharacteristic = batteryService->createCharacteristic( BATTERY_LEVEL_CHAR_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); ccc = new BLE2902(); ccc->setNotifications(true); batteryLevelCharacteristic->addDescriptor(ccc); batteryLevelCharacteristic->setValue(&batteryLevel, 1); // Start the services service->start(); deviceInfoService->start(); batteryService->start(); server->setCallbacks(new ServerHandler()); BLEAdvertising *advertising = BLEDevice::getAdvertising(); advertising->addServiceUUID(BATTERY_SERVICE_UUID); advertising->addServiceUUID(DEVICE_INFORMATION_SERVICE_UUID); advertising->addServiceUUID(service->getUUID()); advertising->setScanResponse(true); advertising->setMinPreferred(0x06); advertising->setMaxPreferred(0x12); BLEDevice::startAdvertising(); } camera_fb_t *fb; bool take_photo() { // Release buffer if (fb) { esp_camera_fb_return(fb); } // Take a photo fb = esp_camera_fb_get(); if (!fb) { Serial.println("Failed to get camera frame buffer"); return false; } return true; } void handlePhotoControl(int8_t controlValue) { if (controlValue == -1) { // Take a single photo isCapturingPhotos = true; captureInterval = 0; } else if (controlValue == 0) { // Stop taking photos isCapturingPhotos = false; captureInterval = 0; } else if (controlValue >= 5 && controlValue <= 300) { // Start taking photos at specified interval captureInterval = (controlValue / 5) * 5000; // Round to nearest 5 seconds and convert to milliseconds isCapturingPhotos = true; lastCaptureTime = millis() - captureInterval; } } // // Microphone // #ifdef CODEC_OPUS static size_t recording_buffer_size = FRAME_SIZE * 2; // 16-bit samples static size_t compressed_buffer_size = MAX_PACKET_SIZE; #define VOLUME_GAIN 2 #else #ifdef CODEC_MULAW static size_t recording_buffer_size = 400; static size_t compressed_buffer_size = 400 + 3; /* header */ #define VOLUME_GAIN 2 #else static size_t recording_buffer_size = FRAME_SIZE * 2; // 16-bit samples static size_t compressed_buffer_size = recording_buffer_size + 3; /* header */ #define VOLUME_GAIN 2 #endif #endif static uint8_t *s_recording_buffer = nullptr; static uint8_t *s_compressed_frame = nullptr; static uint8_t *s_compressed_frame_2 = nullptr; void configure_microphone() { // start I2S at 16 kHz with 16-bits per sample I2S.setAllPins(-1, 42, 41, -1, -1); if (!I2S.begin(PDM_MONO_MODE, SAMPLE_RATE, SAMPLE_BITS)) { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } // Allocate buffers s_recording_buffer = (uint8_t *) ps_calloc(recording_buffer_size, sizeof(uint8_t)); s_compressed_frame = (uint8_t *) ps_calloc(compressed_buffer_size, sizeof(uint8_t)); s_compressed_frame_2 = (uint8_t *) ps_calloc(compressed_buffer_size, sizeof(uint8_t)); } size_t read_microphone() { size_t bytes_recorded = 0; esp_i2s::i2s_read(esp_i2s::I2S_NUM_0, s_recording_buffer, recording_buffer_size, &bytes_recorded, portMAX_DELAY); return bytes_recorded; } // // Camera // void configure_camera() { camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sscb_sda = SIOD_GPIO_NUM; config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.frame_size = FRAMESIZE_UXGA; config.pixel_format = PIXFORMAT_JPEG; // for streaming config.fb_count = 1; // High quality (psram) // config.jpeg_quality = 10; // config.fb_count = 2; // config.grab_mode = CAMERA_GRAB_LATEST; // Low quality (and in local ram) config.jpeg_quality = 10; config.frame_size = FRAMESIZE_SVGA; config.grab_mode = CAMERA_GRAB_LATEST; config.fb_location = CAMERA_FB_IN_PSRAM; // config.fb_location = CAMERA_FB_IN_DRAM; // camera init esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return; } } void updateBatteryLevel() { // TODO: batteryLevelCharacteristic->setValue(&batteryLevel, 1); batteryLevelCharacteristic->notify(); } // // Main // // static uint8_t *s_compressed_frame_2 = nullptr; // static size_t compressed_buffer_size = 400 + 3; void setup() { Serial.begin(921600); // SD.begin(21); configure_ble(); // s_compressed_frame_2 = (uint8_t *) ps_calloc(compressed_buffer_size, sizeof(uint8_t)); #ifdef CODEC_OPUS int opus_err; opus_encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, OPUS_APPLICATION, &opus_err); if (opus_err != OPUS_OK || !opus_encoder) { Serial.println("Failed to create Opus encoder!"); while (1) ; // do nothing } opus_encoder_ctl(opus_encoder, OPUS_SET_BITRATE(OPUS_BITRATE)); #endif configure_microphone(); configure_camera(); } void loop() { // Read from mic size_t bytes_recorded = read_microphone(); // Push audio to BLE if (bytes_recorded > 0 && connected) { #ifdef CODEC_OPUS int16_t samples[FRAME_SIZE]; for (size_t i = 0; i < bytes_recorded; i += 2) { samples[i / 2] = ((s_recording_buffer[i + 1] << 8) | s_recording_buffer[i]) << VOLUME_GAIN; } int encoded_bytes = opus_encode(opus_encoder, samples, FRAME_SIZE, &s_compressed_frame[3], MAX_PACKET_SIZE - 3); if (encoded_bytes > 0) { #else #ifdef CODEC_MULAW for (size_t i = 0; i < bytes_recorded; i += 2) { int16_t sample = ((s_recording_buffer[i + 1] << 8) | s_recording_buffer[i]) << VOLUME_GAIN; s_compressed_frame[i / 2 + 3] = linear2ulaw(sample); } int encoded_bytes = bytes_recorded / 2; #else for (size_t i = 0; i < bytes_recorded / 4; i++) { int16_t sample = ((int16_t *)s_recording_buffer)[i * 2] << VOLUME_GAIN; // Read every other 16-bit sample s_compressed_frame[i * 2 + 3] = sample & 0xFF; // Low byte s_compressed_frame[i * 2 + 4] = (sample >> 8) & 0xFF; // High byte } int encoded_bytes = bytes_recorded / 2; #endif #endif s_compressed_frame[0] = audio_frame_count & 0xFF; s_compressed_frame[1] = (audio_frame_count >> 8) & 0xFF; s_compressed_frame[2] = 0; size_t out_buffer_size = encoded_bytes + 3; audioDataCharacteristic->setValue(s_compressed_frame, out_buffer_size); audioDataCharacteristic->notify(); audio_frame_count++; #ifdef CODEC_OPUS } #endif } // Take a photo unsigned long now = millis(); // Don't take a photo if we are already sending data for previous photo if (isCapturingPhotos && !photoDataUploading && connected) { if ((captureInterval == 0) || ((now - lastCaptureTime) >= captureInterval)) { if (captureInterval == 0) { // Single photo requested isCapturingPhotos = false; } // Take the photo if (take_photo()) { photoDataUploading = true; sent_photo_bytes = 0; sent_photo_frames = 0; lastCaptureTime = now; } } } // Push photo data to BLE if (photoDataUploading) { size_t remaining = fb->len - sent_photo_bytes; if (remaining > 0) { // Populate buffer s_compressed_frame_2[0] = sent_photo_frames & 0xFF; s_compressed_frame_2[1] = (sent_photo_frames >> 8) & 0xFF; size_t bytes_to_copy = remaining; if (bytes_to_copy > 200) { bytes_to_copy = 200; } memcpy(&s_compressed_frame_2[2], &fb->buf[sent_photo_bytes], bytes_to_copy); // Push to BLE photoDataCharacteristic->setValue(s_compressed_frame_2, bytes_to_copy + 2); photoDataCharacteristic->notify(); sent_photo_bytes += bytes_to_copy; sent_photo_frames++; } else { // End flag s_compressed_frame_2[0] = 0xFF; s_compressed_frame_2[1] = 0xFF; photoDataCharacteristic->setValue(s_compressed_frame_2, 2); photoDataCharacteristic->notify(); photoDataUploading = false; } } // Update battery level if (now - lastBatteryUpdate > 60000) { updateBatteryLevel(); lastBatteryUpdate = millis(); } // Delay delay(20); } 请帮我检查一下是否正确
09-30
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf LDF Modes: Finder ~ chain, Compatibility ~ soft Found 37 compatible libraries Scanning dependencies... Dependency Graph |-- ESP32Encoder @ 0.11.8 Building in release mode Compiling .pio/build/esp32-c3-devkitm-1/src/main.cpp.o Linking .pio/build/esp32-c3-devkitm-1/firmware.elf /Users/gengwenhao/.platformio/packages/toolchain-riscv32-esp/bin/../lib/gcc/riscv32-esp-elf/8.4.0/../../../../riscv32-esp-elf/bin/ld: .pio/build/esp32-c3-devkitm-1/src/main.cpp.o: in function `setup()': /Users/gengwenhao/Documents/PlatformIO/Projects/9KEY_ENCODER/src/main.cpp:22: undefined reference to `ESP32Encoder::attachHalfQuad(int, int)' /Users/gengwenhao/.platformio/packages/toolchain-riscv32-esp/bin/../lib/gcc/riscv32-esp-elf/8.4.0/../../../../riscv32-esp-elf/bin/ld: /Users/gengwenhao/Documents/PlatformIO/Projects/9KEY_ENCODER/src/main.cpp:25: undefined reference to `ESP32Encoder::clearCount()' /Users/gengwenhao/.platformio/packages/toolchain-riscv32-esp/bin/../lib/gcc/riscv32-esp-elf/8.4.0/../../../../riscv32-esp-elf/bin/ld: /Users/gengwenhao/Documents/PlatformIO/Projects/9KEY_ENCODER/src/main.cpp:31: undefined reference to `ESP32Encoder::getCount()' /Users/gengwenhao/.platformio/packages/toolchain-riscv32-esp/bin/../lib/gcc/riscv32-esp-elf/8.4.0/../../../../riscv32-esp-elf/bin/ld: .pio/build/esp32-c3-devkitm-1/src/main.cpp.o: in function `_GLOBAL__sub_I_encoder': /Users/gengwenhao/Documents/PlatformIO/Projects/9KEY_ENCODER/src/main.cpp:70: undefined reference to `ESP32Encoder::ESP32Encoder(bool, void (*)(void*), void*)' collect2: error: ld returned 1 exit status *** [.pio/build/esp32-c3-devkitm-1/firmware.elf] Error 1 =================================================== [FAILED] Took 1.36 seconds ===
08-04
<think>我们正在解决ESP32Encoder库在PlatformIO中出现的未定义引用错误。这种错误通常发生在链接阶段,表明编译器找到了函数声明,但没有找到其实现。以下是一些常见原因及解决方法: ### 1. **检查库的安装** - 确保已正确安装ESP32Encoder库。在PlatformIO中,可以通过`platformio.ini`文件添加依赖。 - 在`platformio.ini`中添加: ``` lib_deps = madhephaestus/ESP32Encoder @ ^0.12.0 ``` ### 2. **检查库的兼容性** - 确保所安装的库版本支持ESP32-C3。由于ESP32-C3是RISC-V架构,某些库可能不支持。ESP32Encoder库从0.12.0版本开始支持ESP32-C3[^1]。 ### 3. **检查头文件包含** - 在代码中正确包含头文件: ```cpp #include <ESP32Encoder.h> ``` ### 4. **链接器错误的具体分析** - 错误信息类似`undefined reference to 'ESP32Encoder::ESP32Encoder()'`,这通常意味着链接器找不到库的实现。可能的原因: - 库未正确安装:检查`.pio/libdeps`目录下是否存在`ESP32Encoder`文件夹。 - 构建系统未链接库:确保PlatformIO能够自动链接已安装的库。 ### 5. **清理并重新构建** - 在PlatformIO中,执行清理并重新构建: ``` pio run --target clean pio run ``` ### 6. **检查硬件支持** - 确保在`platformio.ini`中正确设置了板子型号: ``` board = esp32-c3-devkitm-1 platform = espressif32 ``` ### 7. **更新PlatformIO和平台** - 更新PlatformIO核心和Espressif32平台: ``` pio upgrade pio platform update espressif32 ``` ### 8. **使用正确的命名空间** - 根据库的文档,使用正确的类名。例如: ```cpp ESP32Encoder encoder; ``` ### 9. **检查库的依赖** - 某些库可能依赖其他库。ESP32Encoder依赖于`driver`库,但PlatformIO通常会自动处理。 ### 10. **查看库的示例** - 参考官方示例代码,确保使用方式正确。 ### 示例代码 ```cpp #include <Arduino.h> #include <ESP32Encoder.h> ESP32Encoder encoder; void setup() { encoder.attachSingleEdge(0, 1); // 根据实际引脚调整 } void loop() { Serial.println(encoder.getCount()); delay(100); } ``` ### 如果问题仍然存在 - 查看库的源代码,确认函数实现是否存在。 - 在GitHub上搜索类似问题。 §§相关问题§§ 1. 如何为ESP32-C3在PlatformIO中添加第三方库? 2. 如何更新PlatformIO的核心和平台? 3. ESP32Encoder库在ESP32-C3上使用时需要注意什么?</think>### ESP32Encoder库在PlatformIO中的链接错误解决方案 您在ESP32-C3-DevKitM-1开发板上使用ESP32Encoder库时遇到的`undefined reference`错误,通常是由于链接器找不到库函数的实现代码导致的。以下是系统化的解决步骤: #### 1. **检查库安装与依赖** - 在`platformio.ini`中添加库依赖(使用最新兼容版本): ```ini lib_deps = madhephaestus/ESP32Encoder @ ^0.12.0 ``` - 执行清理重建: ```bash pio run --target clean && pio run ``` - **注意**:ESP32-C3需要库版本≥0.12.0才能支持RISC-V架构[^1] #### 2. **验证硬件配置** - 确保`platformio.ini`正确定义开发板: ```ini [env:esp32-c3] platform = espressif32 board = esp32-c3-devkitm-1 framework = arduino ``` #### 3. **检查代码包含关系** - 在`.cpp`文件中正确包含头文件: ```cpp #include <ESP32Encoder.h> // 注意不是"ESP32Encoder.h" ``` - 全局初始化编码器对象(避免局部声明): ```cpp ESP32Encoder encoder; // 全局对象声明 void setup() { encoder.attachSingleEdge(0, 1); // GPIO0和GPIO1 } ``` #### 4. **解决架构兼容性问题** - 如果是旧版库导致的RISC-V兼容问题: 1. 打开`.pio/libdeps/esp32-c3/ESP32Encoder/src/ESP32Encoder.h` 2. 确保存在以下宏定义: ```cpp #if CONFIG_IDF_TARGET_ESP32C3 #define _ENCODER_USE_RMT #endif ``` #### 5. **更新开发环境** - 升级PlatformIO核心: ```bash pio upgrade ``` - 更新ESP32平台: ```bash pio platform update espressif32 ``` #### 6. **验证最小可运行示例** ```cpp #include <Arduino.h> #include <ESP32Encoder.h> ESP32Encoder encoder; void setup() { Serial.begin(115200); ESP32Encoder::useInternalWeakPullResistors = UP; // 启用内部上拉 encoder.attachSingleEdge(0, 1); // 使用单边沿计数模式 } void loop() { Serial.printf("Count: %d\n", encoder.getCount()); delay(100); } ``` > **常见错误模式**: > `undefined reference to ESP32Encoder::attachSingleEdge(...)` > ➜ 通常表示库未正确链接,检查`.pio/libdeps`目录是否存在库文件
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值