Coverage Report

Created: 2026-10-28 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/vvenc/source/Lib/EncoderLib/RateCtrl.cpp
Line
Count
Source
1
/* -----------------------------------------------------------------------------
2
The copyright in this software is being made available under the Clear BSD
3
License, included below. No patent rights, trademark rights and/or
4
other Intellectual Property Rights other than the copyrights concerning
5
the Software are granted under this license.
6
7
The Clear BSD License
8
9
Copyright (c) 2019-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC Authors.
10
All rights reserved.
11
12
Redistribution and use in source and binary forms, with or without modification,
13
are permitted (subject to the limitations in the disclaimer below) provided that
14
the following conditions are met:
15
16
     * Redistributions of source code must retain the above copyright notice,
17
     this list of conditions and the following disclaimer.
18
19
     * Redistributions in binary form must reproduce the above copyright
20
     notice, this list of conditions and the following disclaimer in the
21
     documentation and/or other materials provided with the distribution.
22
23
     * Neither the name of the copyright holder nor the names of its
24
     contributors may be used to endorse or promote products derived from this
25
     software without specific prior written permission.
26
27
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
28
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
29
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
31
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
32
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
36
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38
POSSIBILITY OF SUCH DAMAGE.
39
40
41
------------------------------------------------------------------------------------------- */
42
43
44
/** \file     RateCtrl.cpp
45
    \brief    Rate control manager class
46
*/
47
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
48
#include <nlohmann/json.hpp>
49
#endif
50
51
#include "vvenc/version.h"
52
#include "RateCtrl.h"
53
#include "CommonLib/Picture.h"
54
55
#include <cmath>
56
57
namespace vvenc {
58
59
//sequence level
60
EncRCSeq::EncRCSeq()
61
0
{
62
0
  twoPass             = false;
63
0
  isLookAhead         = false;
64
0
  isIntraGOP          = false;
65
0
  isRateSavingMode    = false;
66
0
  frameRate           = 0.0;
67
0
  targetRate          = 0;
68
0
  maxGopRate          = 0;
69
0
  gopSize             = 0;
70
0
  intraPeriod         = 0;
71
0
  bitsUsed            = 0;
72
0
  bitsUsedQPLimDiff   = 0;
73
0
  estimatedBitUsage   = 0;
74
0
  rateBoostFac        = 1.0;
75
0
  std::memset (qpCorrection, 0, sizeof (qpCorrection));
76
0
  std::memset (actualBitCnt, 0, sizeof (actualBitCnt));
77
0
  std::memset (targetBitCnt, 0, sizeof (targetBitCnt));
78
0
  lastAverageQP       = 0;
79
0
  lastIntraQP         = 0;
80
0
  lastIntraSM         = 0.0;
81
0
  scRelax             = false;
82
0
  bitDepth            = 0;
83
0
}
84
85
EncRCSeq::~EncRCSeq()
86
0
{
87
0
  destroy();
88
0
}
89
90
void EncRCSeq::create( bool twoPassRC, bool lookAhead, int targetBitrate, int maxBitrate, double frRate, int intraPer, int GOPSize, int bitDpth, std::list<TRCPassStats> &firstPassStats )
91
0
{
92
0
  destroy();
93
0
  twoPass             = twoPassRC;
94
0
  isLookAhead         = lookAhead;
95
0
  targetRate          = std::min( INT32_MAX / 3, targetBitrate );
96
0
  maxGopRate          = int( 0.5 + std::min( (double) INT32_MAX, (double) std::min( 3 * targetRate, maxBitrate ) * GOPSize / frRate ) ); // 1.5x-3x is the valid range
97
0
  frameRate           = frRate;
98
0
  intraPeriod         = Clip3<unsigned>( GOPSize, 4 * VVENC_MAX_GOP, intraPer );
99
0
  gopSize             = GOPSize;
100
0
  firstPassData       = firstPassStats;
101
0
  bitDepth            = bitDpth;
102
103
0
  int bitdepthLumaScale = 2 * ( bitDepth - 8 - DISTORTION_PRECISION_ADJUSTMENT( bitDepth ) );
104
0
  minEstLambda = 0.1;
105
0
  maxEstLambda = 65535.9375 * pow( 2.0, bitdepthLumaScale );
106
107
0
  bitsUsed            = 0;
108
0
  bitsUsedQPLimDiff   = 0;
109
0
  estimatedBitUsage   = 0;
110
0
  std::memset (qpCorrection, 0, sizeof (qpCorrection));
111
0
  std::memset (actualBitCnt, 0, sizeof (actualBitCnt));
112
0
  std::memset (targetBitCnt, 0, sizeof (targetBitCnt));
113
0
}
114
115
void EncRCSeq::destroy()
116
0
{
117
0
  return;
118
0
}
119
120
void EncRCSeq::updateAfterPic (const int actBits, const int tgtBits)
121
0
{
122
0
  estimatedBitUsage += tgtBits;
123
124
0
  if (isLookAhead)
125
0
  {
126
0
    const uint64_t* const tlBits = actualBitCnt; // recently updated in EncRCPic::updateAfterPicture()
127
128
0
    bitsUsed = tlBits[0] + tlBits[1] + tlBits[2] + tlBits[3] + tlBits[4] + tlBits[5] + tlBits[6] + tlBits[7];
129
0
  }
130
0
  else
131
0
  {
132
0
    bitsUsed += actBits;
133
0
  }
134
0
}
135
136
//picture level
137
EncRCPic::EncRCPic()
138
0
{
139
0
  encRCSeq            = NULL;
140
0
  frameLevel          = 0;
141
0
  targetBits          = 0;
142
0
  tmpTargetBits       = 0;
143
0
  picQP               = 0;
144
0
  picBits             = 0;
145
0
  poc                 = 0;
146
0
  refreshParams       = false;
147
0
  visActSteady        = 0;
148
0
}
149
150
EncRCPic::~EncRCPic()
151
0
{
152
0
  destroy();
153
0
}
154
155
void EncRCPic::addToPictureList( std::list<EncRCPic*>& listPreviousPictures )
156
0
{
157
0
  if ( listPreviousPictures.size() > std::min( VVENC_MAX_GOP, 2 * encRCSeq->gopSize ) )
158
0
  {
159
0
    EncRCPic* p = listPreviousPictures.front();
160
0
    listPreviousPictures.pop_front();
161
0
    p->destroy();
162
0
    delete p;
163
0
  }
164
165
0
  listPreviousPictures.push_back( this );
166
0
}
167
168
void EncRCPic::create( EncRCSeq* encRcSeq, int frameLvl, int framePoc )
169
0
{
170
0
  destroy();
171
0
  encRCSeq   = encRcSeq;
172
0
  poc        = framePoc;
173
0
  frameLevel = frameLvl;
174
0
}
175
176
void EncRCPic::destroy()
177
0
{
178
0
  encRCSeq = NULL;
179
0
}
180
181
void EncRCPic::clipTargetQP (std::list<EncRCPic*>& listPreviousPictures, const int baseQP, const int refrIncrFac, const int maxTL, const double resRatio, int &qp, int* qpAvg)
182
0
{
183
0
  const int rShift = (resRatio < 0.03125 ? 12 : (resRatio < 0.125 ? 13 : (resRatio < 0.5 ? 14 : 15)));
184
0
  const int initQP = qp;
185
0
  int lastCurrTLQP = -1;
186
0
  int lastPrevTLQP = -1;
187
0
  int lastMaxTLBits = 0;
188
0
  int halvedAvgQP  = -1;
189
0
  std::list<EncRCPic*>::iterator it;
190
191
0
  for (it = listPreviousPictures.begin(); it != listPreviousPictures.end(); it++)
192
0
  {
193
0
    if ((*it)->frameLevel == frameLevel && (*it)->picQP >= 0)  // current temporal level
194
0
    {
195
0
      lastCurrTLQP = (*it)->picQP;
196
0
    }
197
0
    if ((*it)->frameLevel == frameLevel - 1 && (*it)->picQP >= 0) // last temporal level
198
0
    {
199
0
      lastPrevTLQP = (frameLevel == 1 ? ((*it)->picQP * 3) >> 2 : std::max<int> (encRCSeq->lastIntraQP, (*it)->picQP));
200
0
    }
201
0
    if ((*it)->frameLevel == 1 && frameLevel == 0 && refreshParams && lastCurrTLQP < 0)
202
0
    {
203
0
      lastCurrTLQP = (*it)->picQP;
204
0
    }
205
0
    if ((*it)->frameLevel > maxTL && (*it)->poc + 32 + encRCSeq->gopSize > poc)
206
0
    {
207
0
      lastMaxTLBits += (*it)->picBits;
208
0
    }
209
0
    halvedAvgQP += (*it)->picQP;
210
0
  }
211
212
0
  if (qpAvg && !listPreviousPictures.empty()) *qpAvg = int ((halvedAvgQP + 1 + (listPreviousPictures.size() >> 1)) / listPreviousPictures.size());
213
214
0
  if (listPreviousPictures.size() >= 1) halvedAvgQP = int ((halvedAvgQP + 1 + listPreviousPictures.size()) / (2 * listPreviousPictures.size()));
215
0
  if (frameLevel <= 1 && lastPrevTLQP < halvedAvgQP) lastPrevTLQP = halvedAvgQP; // TL0I
216
0
  if (frameLevel == 1 && lastCurrTLQP < 0) lastCurrTLQP = encRCSeq->lastIntraQP; // TL0B
217
0
  if (frameLevel == 0 && !(lastMaxTLBits >> rShift) && maxTL) qp++; // frozen-image mode
218
219
0
  qp = Clip3 (frameLevel + std::max (0, baseQP >> 1), MAX_QP, qp);
220
221
0
  if (lastCurrTLQP >= 0) // limit QP changes among prev. frames from same temporal level
222
0
  {
223
0
    const int clipRange = (refreshParams ? 5 + (encRCSeq->intraPeriod + (encRCSeq->gopSize >> 1)) / encRCSeq->gopSize : std::max (3, 6 - (frameLevel >> 1)));
224
225
0
    qp = Clip3 (lastCurrTLQP - clipRange, std::min (MAX_QP, lastCurrTLQP + (refreshParams ? (refrIncrFac * clipRange) >> 1 : clipRange)), qp);
226
0
  }
227
0
  if (lastPrevTLQP >= 0) // prevent QP from being lower than QPs at lower temporal level
228
0
  {
229
0
    qp = Clip3 (std::min (MAX_QP, lastPrevTLQP + 1), MAX_QP, qp);
230
0
  }
231
0
  else if (encRCSeq->lastIntraQP >= -1 && (frameLevel == 1 || frameLevel == 2))
232
0
  {
233
0
    qp = Clip3 ((encRCSeq->lastIntraQP >> 1) + 1, MAX_QP, qp);
234
0
  }
235
236
0
  if (qp != initQP) // adjust target bits according to QP change as in VCIP paper eq.(4)
237
0
  {
238
0
    targetBits = (int) std::max (1.0, 0.5 + targetBits * pow (2.0, (initQP - qp) / ((105.0 / 128.0) * sqrt ((double) std::max (1, initQP)))));
239
0
  }
240
0
}
241
242
void EncRCPic::updateAfterPicture (const int picActualBits, const int averageQP)
243
0
{
244
0
  picQP = averageQP;
245
0
  picBits = (uint16_t) Clip3 (0, 65535, picActualBits);
246
247
0
  if ((frameLevel <= 7) && (picActualBits > 0) && (targetBits > 0)) // update, for initRateControlPic()
248
0
  {
249
0
    const uint16_t vaMin = 1u << (encRCSeq->bitDepth - 6);
250
0
    const double clipVal = (visActSteady > 0 && visActSteady < vaMin + 48 ? 0.25 * (visActSteady - vaMin) : 12.0);
251
252
0
    encRCSeq->actualBitCnt[frameLevel] += (uint64_t) picActualBits;
253
0
    encRCSeq->targetBitCnt[frameLevel] += (uint64_t) targetBits;
254
255
0
    if (refreshParams && frameLevel <= 2) encRCSeq->lastAverageQP = 0;
256
257
0
    encRCSeq->qpCorrection[frameLevel] = (105.0 / 128.0) * sqrt ((double) std::max (1, encRCSeq->lastAverageQP)) * log ((double) encRCSeq->actualBitCnt[frameLevel] / (double) encRCSeq->targetBitCnt[frameLevel]) / log (2.0); // adjust target bits as in VCIP paper eq.(4)
258
0
    encRCSeq->qpCorrection[frameLevel] = Clip3 (-clipVal, clipVal, encRCSeq->qpCorrection[frameLevel]);
259
260
0
    if (frameLevel > std::max (1, int (log ((double) encRCSeq->gopSize) / log (2.0))))
261
0
    {
262
0
      double highTlQpCorr = 0.0;
263
264
0
      for (int l = 2; l <= 7; l++) // stabilization when corrections differ between low and high levels
265
0
      {
266
0
        highTlQpCorr += encRCSeq->qpCorrection[l];
267
0
      }
268
0
      if (highTlQpCorr > 1.0) // attenuate low-level QP correction towards 0 when bits need to be saved
269
0
      {
270
0
        if (encRCSeq->qpCorrection[0] < -1.0e-9) encRCSeq->qpCorrection[0] /= highTlQpCorr;
271
0
        if (encRCSeq->qpCorrection[1] < -1.0e-9) encRCSeq->qpCorrection[1] /= highTlQpCorr;
272
0
      }
273
0
    }
274
0
  }
275
0
}
276
277
RateCtrl::RateCtrl(MsgLog& logger)
278
1.49k
: msg ( logger )
279
1.49k
{
280
1.49k
  m_pcEncCfg           = nullptr;
281
1.49k
  encRCSeq             = NULL;
282
1.49k
  flushPOC             = -1;
283
1.49k
  rcPass               = 0;
284
1.49k
  rcIsFinalPass        = true;
285
1.49k
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
286
1.49k
  m_pqpaStatsWritten   = 0;
287
1.49k
#endif
288
1.49k
  m_numPicStatsTotal   = 0;
289
1.49k
  m_numPicAddedToList  = 0;
290
1.49k
  m_updateNoisePoc     = -1;
291
1.49k
  m_resetNoise         = true;
292
1.49k
  m_gopMEErrorCBufIdx  = 0;
293
1.49k
  m_maxPicMotionError  = 0;
294
1.49k
  std::fill_n( m_gopMEErrorCBuf, QPA_MAX_NOISE_LEVELS, 0 );
295
1.49k
  std::fill_n( m_minNoiseLevels, QPA_MAX_NOISE_LEVELS, 255u );
296
1.49k
  std::fill_n( m_tempDownSamplStats, VVENC_MAX_TLAYER + 1, TRCPassStats() );
297
1.49k
}
298
299
RateCtrl::~RateCtrl()
300
1.49k
{
301
1.49k
  destroy();
302
1.49k
}
303
304
void RateCtrl::destroy()
305
4.47k
{
306
4.47k
  if ( encRCSeq != NULL )
307
0
  {
308
0
    delete encRCSeq;
309
0
    encRCSeq = NULL;
310
0
  }
311
4.47k
  while ( m_listRCPictures.size() > 0 )
312
0
  {
313
0
    EncRCPic* p = m_listRCPictures.front();
314
0
    m_listRCPictures.pop_front();
315
0
    delete p;
316
0
  }
317
318
4.47k
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
319
4.47k
  if ( m_rcStatsFHandle.is_open() )
320
0
  {
321
0
    m_rcStatsFHandle.close();
322
0
  }
323
4.47k
  m_pqpaStatsWritten = 0;
324
4.47k
#endif
325
4.47k
}
326
327
void RateCtrl::init( const VVEncCfg& encCfg )
328
0
{
329
0
  destroy();
330
331
0
  m_pcEncCfg = &encCfg;
332
333
0
  encRCSeq = new EncRCSeq;
334
0
  encRCSeq->create( m_pcEncCfg->m_RCNumPasses == 2, m_pcEncCfg->m_LookAhead == 1, m_pcEncCfg->m_RCTargetBitrate, m_pcEncCfg->m_RCMaxBitrate, (double)m_pcEncCfg->m_FrameRate / m_pcEncCfg->m_FrameScale,
335
0
                    m_pcEncCfg->m_IntraPeriod, m_pcEncCfg->m_GOPSize, m_pcEncCfg->m_internalBitDepth[CH_L], getFirstPassStats() );
336
0
}
337
338
int RateCtrl::getBaseQP()
339
0
{
340
  // estimate near-optimal base QP for PPS in second RC pass
341
0
  double d = (3840.0 * 2160.0) / double (m_pcEncCfg->m_SourceWidth * m_pcEncCfg->m_SourceHeight);
342
0
  const double firstQPOffset = sqrt ((d * m_pcEncCfg->m_RCTargetBitrate) / 500000.0);
343
0
  std::list<TRCPassStats>& firstPassData = m_listRCFirstPassStats;
344
0
  int baseQP = MAX_QP;
345
346
0
  if (firstPassData.size() > 0 && encRCSeq->frameRate > 0.0)
347
0
  {
348
0
    const int firstPassBaseQP = (m_pcEncCfg->m_RCInitialQP > 0 ? std::min (MAX_QP, m_pcEncCfg->m_RCInitialQP) : std::max (0, MAX_QP_INIT_QPA - (m_pcEncCfg->m_FirstPassMode > 2 ? 4 : 2) - int (0.5 + firstQPOffset)));
349
0
    uint64_t sumFrBits = 0;  // sum of first-pass frame bits
350
351
0
    for (auto& stats : firstPassData)
352
0
    {
353
0
      sumFrBits += stats.numBits;
354
0
    }
355
0
    if (m_pcEncCfg->m_usePerceptQPA && m_pcEncCfg->m_LookAhead) // account for very low visual activity
356
0
    {
357
0
      const double hpEnerPic = sqrt (32.0 * double (1 << (2 * encRCSeq->bitDepth - 10)) * sqrt (d));
358
0
      uint32_t hpEner = 0;
359
360
0
      for (auto& stats : firstPassData)
361
0
      {
362
0
        hpEner += stats.visActY;
363
0
      }
364
0
      if (hpEner > 0 && hpEner < hpEnerPic * firstPassData.size()) // similar to applyQPAdaptationSlice
365
0
      {
366
0
        sumFrBits = uint64_t (0.5 + sumFrBits * sqrt (hpEner / (hpEnerPic * firstPassData.size())));
367
0
      }
368
0
    }
369
0
    baseQP = int (24.5 - log (std::max (1.0, d)) / log (2.0)); // QPstart, round(24 + 2*log2(resRatio))
370
0
    d = (double) m_pcEncCfg->m_RCTargetBitrate * (double) firstPassData.size() / (encRCSeq->frameRate * sumFrBits);
371
0
    d = firstPassBaseQP - (105.0 / 128.0) * sqrt ((double) std::max (1, firstPassBaseQP)) * log (d) / log (2.0);
372
0
    baseQP = int (0.5 + d + 0.5 * std::max (0.0, baseQP - d));
373
0
  }
374
0
  else if (m_pcEncCfg->m_LookAhead)
375
0
  {
376
0
    baseQP = int (24.5 - log (std::max (1.0, d)) / log (2.0)); // QPstart, round(24 + 2*log2(resRatio))
377
0
    d = MAX_QP_INIT_QPA - 2.0 - 1.5 * firstQPOffset - 0.5 * log ((double) encRCSeq->intraPeriod / encRCSeq->gopSize) / log (2.0);
378
0
    baseQP = int (0.5 + d + 0.5 * std::max (0.0, baseQP - d));
379
0
  }
380
381
0
  return Clip3 (0, MAX_QP, baseQP);
382
0
}
383
384
void RateCtrl::setRCPass(const VVEncCfg& encCfg, const int pass, const char* statsFName)
385
1.49k
{
386
1.49k
  m_pcEncCfg    = &encCfg;
387
1.49k
  rcPass        = pass;
388
1.49k
  rcIsFinalPass = (pass >= m_pcEncCfg->m_RCNumPasses - 1);
389
390
1.49k
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
391
1.49k
  if( m_rcStatsFHandle.is_open() ) m_rcStatsFHandle.close();
392
393
1.49k
  const std::string name = statsFName != nullptr ? statsFName : "";
394
1.49k
  if( name.length() )
395
0
  {
396
0
    openStatsFile( name );
397
0
    if( rcIsFinalPass )
398
0
    {
399
0
      readStatsFile();
400
0
    }
401
0
  }
402
#else
403
  CHECK( statsFName != nullptr && strlen( statsFName ) > 0, "reading/writing rate control statistics file not supported, please compile with json enabled" );
404
#endif
405
406
1.49k
  if (rcIsFinalPass && (encCfg.m_FirstPassMode > 2))
407
0
  {
408
0
    adjustStatsDownsample();
409
0
  }
410
1.49k
}
411
412
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
413
void RateCtrl::openStatsFile(const std::string& name)
414
0
{
415
0
  if( rcIsFinalPass )
416
0
  {
417
0
    m_rcStatsFHandle.open( name, std::ios::in );
418
0
    CHECK( m_rcStatsFHandle.fail(), "unable to open rate control statistics file for reading" );
419
0
    readStatsHeader();
420
0
  }
421
0
  else
422
0
  {
423
0
    m_rcStatsFHandle.open( name, std::ios::trunc | std::ios::out );
424
0
    CHECK( m_rcStatsFHandle.fail(), "unable to open rate control statistics file for writing" );
425
0
    writeStatsHeader();
426
0
  }
427
0
}
428
429
void RateCtrl::writeStatsHeader()
430
0
{
431
0
  nlohmann::json header = {
432
0
    { "version",      VVENC_VERSION },
433
0
    { "SourceWidth",  m_pcEncCfg->m_SourceWidth },
434
0
    { "SourceHeight", m_pcEncCfg->m_SourceHeight },
435
0
    { "CTUSize",      m_pcEncCfg->m_CTUSize },
436
0
    { "GOPSize",      m_pcEncCfg->m_GOPSize },
437
0
    { "IntraPeriod",  m_pcEncCfg->m_IntraPeriod },
438
0
    { "PQPA",         m_pcEncCfg->m_usePerceptQPA },
439
0
    { "QP",           m_pcEncCfg->m_QP },
440
0
    { "RCInitialQP",  m_pcEncCfg->m_RCInitialQP }
441
0
  };
442
0
  m_rcStatsFHandle << header << std::endl;
443
0
}
444
445
void RateCtrl::readStatsHeader()
446
0
{
447
0
  std::string line;
448
0
  if( ! std::getline( m_rcStatsFHandle, line ) )
449
0
  {
450
0
    THROW( "unable to read header from rate control statistics file" );
451
0
  }
452
0
  nlohmann::json header = nlohmann::json::parse( line );
453
0
  if( header.find( "version" )         == header.end() || ! header[ "version" ].is_string()
454
0
      || header.find( "SourceWidth" )  == header.end() || ! header[ "SourceWidth" ].is_number()
455
0
      || header.find( "SourceHeight" ) == header.end() || ! header[ "SourceHeight" ].is_number()
456
0
      || header.find( "CTUSize" )      == header.end() || ! header[ "CTUSize" ].is_number()
457
0
      || header.find( "GOPSize" )      == header.end() || ! header[ "GOPSize" ].is_number()
458
0
      || header.find( "IntraPeriod" )  == header.end() || ! header[ "IntraPeriod" ].is_number()
459
0
      || header.find( "PQPA" )         == header.end() || ! header[ "PQPA" ].is_boolean()
460
0
      || header.find( "QP" )           == header.end() || ! header[ "QP" ].is_number()
461
0
      || header.find( "RCInitialQP" )  == header.end() || ! header[ "RCInitialQP" ].is_number()
462
0
    )
463
0
  {
464
0
    THROW( "header line in rate control statistics file not recognized" );
465
0
  }
466
0
  if( header[ "version" ]      != VVENC_VERSION )              msg.log( VVENC_WARNING, "WARNING: wrong version in rate control statistics file\n" );
467
0
  if( header[ "SourceWidth" ]  != m_pcEncCfg->m_SourceWidth )  msg.log( VVENC_WARNING, "WARNING: wrong frame width in rate control statistics file\n" );
468
0
  if( header[ "SourceHeight" ] != m_pcEncCfg->m_SourceHeight ) msg.log( VVENC_WARNING, "WARNING: wrong frame height in rate control statistics file\n" );
469
0
  if( header[ "CTUSize" ]      != m_pcEncCfg->m_CTUSize )      msg.log( VVENC_WARNING, "WARNING: wrong CTU size in rate control statistics file\n" );
470
0
  if( header[ "GOPSize" ]      != m_pcEncCfg->m_GOPSize )      msg.log( VVENC_WARNING, "WARNING: wrong GOP size in rate control statistics file\n" );
471
0
  if( header[ "IntraPeriod" ]  != m_pcEncCfg->m_IntraPeriod )  msg.log( VVENC_WARNING, "WARNING: wrong intra period in rate control statistics file\n" );
472
0
}
473
#endif // VVENC_ENABLE_THIRDPARTY_JSON
474
475
void RateCtrl::storeStatsData( TRCPassStats statsData )
476
0
{
477
0
  if( m_pcEncCfg->m_FirstPassMode == 2 || m_pcEncCfg->m_FirstPassMode == 4)
478
0
  {
479
0
    CHECK( statsData.tempLayer > VVENC_MAX_TLAYER, "array index out of bounds" );
480
0
    if( statsData.numBits )
481
0
    {
482
0
      m_tempDownSamplStats[ statsData.tempLayer ] = statsData;
483
0
    }
484
0
    else
485
0
    {
486
0
      const TRCPassStats& srcData = m_tempDownSamplStats[ statsData.tempLayer ];
487
0
      CHECK( srcData.numBits == 0,                                 "miss stats data from previous frame for temporal down-sampling" );
488
0
      CHECK( statsData.poc - srcData.poc >= m_pcEncCfg->m_GOPSize, "miss stats data from previous frame for temporal down-sampling" );
489
0
      statsData.qp        = srcData.qp;
490
0
      statsData.lambda    = srcData.lambda;
491
0
      if( statsData.visActY == 0 && statsData.spVisAct == 0 )
492
0
        statsData.spVisAct = srcData.spVisAct;
493
0
      if( statsData.visActY == 0 )
494
0
        statsData.visActY = srcData.visActY;
495
0
      statsData.numBits   = srcData.numBits;
496
0
      statsData.psnrY     = srcData.psnrY;
497
0
    }
498
0
  }
499
0
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
500
0
  nlohmann::json data = {
501
0
    { "poc",            statsData.poc },
502
0
    { "qp",             statsData.qp },
503
0
    { "lambda",         statsData.lambda },
504
0
    { "visActY",        statsData.visActY },
505
0
    { "numBits",        statsData.numBits },
506
0
    { "psnrY",          statsData.psnrY },
507
0
    { "isIntra",        statsData.isIntra },
508
0
    { "tempLayer",      statsData.tempLayer },
509
0
    { "isStartOfIntra", statsData.isStartOfIntra },
510
0
    { "isStartOfGop",   statsData.isStartOfGop },
511
0
    { "gopNum",         statsData.gopNum },
512
0
    { "scType",         statsData.scType },
513
0
  };
514
515
0
  if( m_pcEncCfg->m_FirstPassMode > 2 )
516
0
  {
517
0
    data[ "spVisAct" ] = statsData.spVisAct;
518
0
    statsData.spVisAct = data[ "spVisAct" ];
519
0
  }
520
521
0
  if( m_rcStatsFHandle.is_open() )
522
0
  {
523
0
    CHECK( ! m_rcStatsFHandle.good(), "unable to write to rate control statistics file" );
524
0
    if( m_listRCIntraPQPAStats.size() > m_pqpaStatsWritten )
525
0
    {
526
0
      std::vector<uint8_t> pqpaTemp;
527
0
      while( m_pqpaStatsWritten < (int)m_listRCIntraPQPAStats.size() )
528
0
      {
529
0
        pqpaTemp.push_back( m_listRCIntraPQPAStats[ m_pqpaStatsWritten ] );
530
0
        m_pqpaStatsWritten++;
531
0
      }
532
0
      data[ "pqpaStats" ] = pqpaTemp;
533
0
    }
534
0
    m_rcStatsFHandle << data << std::endl;
535
0
  }
536
0
  else
537
0
  {
538
    // ensure same precision for internal and written data by serializing internal data as well
539
0
    std::stringstream iss;
540
0
    iss << data;
541
0
    data = nlohmann::json::parse( iss.str() );
542
0
    std::list<TRCPassStats>& listRCFirstPassStats = m_pcEncCfg->m_LookAhead ? m_firstPassCache : m_listRCFirstPassStats;
543
0
    listRCFirstPassStats.push_back( TRCPassStats( data[ "poc" ],
544
0
                                                    data[ "qp" ],
545
0
                                                    data[ "lambda" ],
546
0
                                                    data[ "visActY" ],
547
0
                                                    data[ "numBits" ],
548
0
                                                    data[ "psnrY" ],
549
0
                                                    data[ "isIntra" ],
550
0
                                                    data[ "tempLayer" ],
551
0
                                                    data[ "isStartOfIntra" ],
552
0
                                                    data[ "isStartOfGop" ],
553
0
                                                    data[ "gopNum" ],
554
0
                                                    data[ "scType" ],
555
0
                                                    statsData.spVisAct,
556
0
                                                    statsData.motionEstError,
557
0
                                                    statsData.minNoiseLevels
558
0
                                                    ) );
559
0
  }
560
0
  m_numPicStatsTotal++;
561
#else
562
  m_listRCFirstPassStats.push_back( statsData );
563
564
  if( m_pcEncCfg->m_LookAhead && (int) m_listRCFirstPassStats.size() > encRCSeq->intraPeriod + encRCSeq->gopSize + 1 )
565
  {
566
    m_listRCFirstPassStats.pop_front();
567
  }
568
#endif
569
0
}
570
571
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
572
void RateCtrl::readStatsFile()
573
0
{
574
0
  CHECK( ! m_rcStatsFHandle.good(), "unable to read from rate control statistics file" );
575
576
0
  uint8_t minNoiseLevels[ QPA_MAX_NOISE_LEVELS ];
577
0
  std::fill_n( minNoiseLevels, QPA_MAX_NOISE_LEVELS, 255u );
578
579
0
  int lineNum = 2;
580
0
  std::string line;
581
0
  while( std::getline( m_rcStatsFHandle, line ) )
582
0
  {
583
0
    nlohmann::json data = nlohmann::json::parse( line );
584
0
    if( data.find( "poc" )               == data.end() || ! data[ "poc" ].is_number()
585
0
        || data.find( "qp" )             == data.end() || ! data[ "qp" ].is_number()
586
0
        || data.find( "lambda" )         == data.end() || ! data[ "lambda" ].is_number()
587
0
        || data.find( "visActY" )        == data.end() || ! data[ "visActY" ].is_number()
588
0
        || data.find( "numBits" )        == data.end() || ! data[ "numBits" ].is_number()
589
0
        || data.find( "psnrY" )          == data.end() || ! data[ "psnrY" ].is_number()
590
0
        || data.find( "isIntra" )        == data.end() || ! data[ "isIntra" ].is_boolean()
591
0
        || data.find( "tempLayer" )      == data.end() || ! data[ "tempLayer" ].is_number()
592
0
        || data.find( "isStartOfIntra" ) == data.end() || ! data[ "isStartOfIntra" ].is_boolean()
593
0
        || data.find( "isStartOfGop" )   == data.end() || ! data[ "isStartOfGop" ].is_boolean()
594
0
        || data.find( "gopNum" )         == data.end() || ! data[ "gopNum" ].is_number()
595
0
        || data.find( "scType" )         == data.end() || ! data[ "scType" ].is_number() )
596
0
    {
597
0
      THROW( "syntax of rate control statistics file in line " << lineNum << " not recognized: (" << line << ")" );
598
0
    }
599
0
    int spVisAct = 0;
600
0
    if( data.find( "spVisAct" ) != data.end() )
601
0
    {
602
0
      CHECK( ! data[ "spVisAct" ].is_number(), "spatial visual activity in rate control statistics file must be a number" );
603
0
      spVisAct = data[ "spVisAct" ];
604
0
    }
605
0
    m_listRCFirstPassStats.push_back( TRCPassStats( data[ "poc" ],
606
0
                                                    data[ "qp" ],
607
0
                                                    data[ "lambda" ],
608
0
                                                    data[ "visActY" ],
609
0
                                                    data[ "numBits" ],
610
0
                                                    data[ "psnrY" ],
611
0
                                                    data[ "isIntra" ],
612
0
                                                    data[ "tempLayer" ],
613
0
                                                    data[ "isStartOfIntra" ],
614
0
                                                    data[ "isStartOfGop" ],
615
0
                                                    data[ "gopNum" ],
616
0
                                                    data[ "scType" ],
617
0
                                                    spVisAct,
618
0
                                                    0, // motionEstError
619
0
                                                    minNoiseLevels
620
0
                                                    ) );
621
0
    if( data.find( "pqpaStats" ) != data.end() )
622
0
    {
623
0
      CHECK( ! data[ "pqpaStats" ].is_array(), "pqpa array data in rate control statistics file not recognized" );
624
0
      std::vector<uint8_t> pqpaTemp = data[ "pqpaStats" ];
625
0
      for( auto el : pqpaTemp )
626
0
      {
627
0
        m_listRCIntraPQPAStats.push_back( el );
628
0
      }
629
0
    }
630
0
    lineNum++;
631
0
  }
632
0
}
633
#endif
634
635
void RateCtrl::adjustStatsDownsample()
636
0
{
637
0
  int64_t meanValue = 0; //MCTF or Activity
638
0
  int amount = 0;
639
0
  auto itr = m_listRCFirstPassStats.begin();
640
0
  for (; itr != m_listRCFirstPassStats.end(); itr++)
641
0
  {
642
0
    auto& stat = *itr;
643
0
    int statValue = stat.spVisAct;
644
0
    if (statValue != 0)
645
0
    {
646
0
      meanValue += statValue;
647
0
      amount++;
648
0
    }
649
0
    stat.numBits = stat.numBits << 1;
650
0
  }
651
0
  if (meanValue != 0)
652
0
  {
653
0
    meanValue = meanValue / amount;
654
0
    int64_t sumVar = 0;
655
0
    int numVar = 0;
656
0
    auto itrv = m_listRCFirstPassStats.begin();
657
0
    for (; itrv != m_listRCFirstPassStats.end(); itrv++)
658
0
    {
659
0
      auto& stat = *itrv;
660
0
      if (stat.spVisAct != 0)
661
0
      {
662
0
        sumVar += (std::abs(stat.spVisAct - meanValue) * std::abs(stat.spVisAct - meanValue));
663
0
        numVar++;
664
0
      }
665
0
    }
666
0
    if (numVar)
667
0
    {
668
0
      sumVar = sumVar / std::max(1, numVar - 1);
669
0
      sumVar = int64_t (0.5 + sqrt((double) sumVar));
670
0
    }
671
0
    int value_gopbefore = 0;
672
0
    int value_gopcur = 0;
673
0
    int num_gopcur = 0;
674
0
    int gopcur = 0;
675
0
    bool doChangeBits = false;
676
0
    auto itr = m_listRCFirstPassStats.begin();
677
0
    for (; itr != m_listRCFirstPassStats.end(); itr++)
678
0
    {
679
0
      auto& stat = *itr;
680
0
      int statValue = stat.spVisAct;
681
0
      if (gopcur != stat.gopNum)
682
0
      {
683
0
        gopcur = stat.gopNum;
684
0
        if (num_gopcur)
685
0
        {
686
0
          value_gopbefore = value_gopcur / num_gopcur;
687
0
        }
688
0
        else
689
0
        {
690
0
          value_gopbefore = value_gopcur;
691
0
        }
692
0
        value_gopcur = 0;
693
0
        num_gopcur = 0;
694
0
        doChangeBits = false;
695
0
      }
696
0
      if (statValue != 0)
697
0
      {
698
0
        value_gopcur += statValue;
699
0
        num_gopcur++;
700
0
        doChangeBits = false;
701
0
        if (stat.gopNum != 0)
702
0
        {
703
0
          const int64_t var_cur = std::abs(statValue - meanValue);
704
0
          if (var_cur > (sumVar << 1))
705
0
          {
706
0
            doChangeBits = true;
707
0
          }
708
0
          else
709
0
          {
710
0
            int rate1 = (((value_gopcur / num_gopcur) * 100) / meanValue);
711
0
            int rate2 = (value_gopbefore == 0) ? 100 : (((value_gopcur / num_gopcur) * 100) / value_gopbefore);
712
0
            if ((rate1 > 140) || (rate1 < 60)
713
0
              || (rate2 > 140) || (rate2 < 60))
714
0
            {
715
0
              doChangeBits = true;
716
0
            }
717
0
          }
718
0
        }
719
0
      }
720
0
      if ((stat.gopNum != 0) && doChangeBits && (stat.tempLayer > 1))
721
0
      {
722
0
        stat.numBits = (stat.numBits * 3) >> 1;
723
0
      }
724
0
    }
725
0
  }
726
0
}
727
728
void RateCtrl::setRCRateSavingState( const int maxRate )
729
0
{
730
0
  if ( m_pcEncCfg->m_LookAhead && maxRate < encRCSeq->targetRate ) // end of video: incomplete I-period?
731
0
  {
732
0
    encRCSeq->isIntraGOP = false;
733
0
  }
734
0
  encRCSeq->isRateSavingMode = true;
735
0
}
736
737
void RateCtrl::processFirstPassData( const bool flush, const int poc /*= -1*/ )
738
0
{
739
0
  if( m_pcEncCfg->m_RCNumPasses > 1 )
740
0
  {
741
    // two pass rc
742
0
    CHECK( m_pcEncCfg->m_LookAhead, "two pass rc does not support look-ahead mode" );
743
744
0
    xProcessFirstPassData( flush, poc );
745
0
  }
746
0
  else
747
0
  {
748
    // single pass rc
749
0
    CHECK( !m_pcEncCfg->m_LookAhead,     "single pass rc should be only used in look-ahead mode" );
750
0
    CHECK( m_firstPassCache.size() == 0, "no data available from the first pass" );
751
0
    CHECK( poc < 0,                      "no valid poc given" );
752
753
    // fetch RC data for the next look-ahead chunk
754
    // the next look-ahead chunk starts with a given POC, so find a pic for a given POC in cache
755
    // NOTE!!!: pictures in cache are in coding order
756
757
0
    auto picCacheItr = find_if( m_firstPassCache.begin(), m_firstPassCache.end(), [poc]( auto& picStat ) { return picStat.poc == poc; } );
758
759
0
    for( int count = 0; picCacheItr != m_firstPassCache.end(); ++picCacheItr )
760
0
    {
761
0
      auto& picStat = *picCacheItr;
762
0
      count++;
763
0
      if( !picStat.addedToList )
764
0
      {
765
0
        picStat.addedToList = true;
766
0
        m_numPicAddedToList++;
767
0
        m_listRCFirstPassStats.push_back( picStat );
768
0
        if( m_pcEncCfg->m_LookAhead && (int) m_listRCFirstPassStats.size() > encRCSeq->intraPeriod + encRCSeq->gopSize + 1 )
769
0
        {
770
0
          m_listRCFirstPassStats.pop_front();
771
0
          m_firstPassCache.pop_front();
772
0
        }
773
774
        // the chunk is considered to contain a particular number of pictures up to the picture starting the next GOP (including it)
775
        // in flush-mode, ensure the deterministic definition of last chunk
776
0
        if( ( count >= m_pcEncCfg->m_GOPSize + 1 || ( m_listRCFirstPassStats.size() > 2 && picStat.isStartOfGop ) ) && !( flush && m_numPicAddedToList > m_numPicStatsTotal - m_pcEncCfg->m_GOPSize ) )
777
0
          break;
778
0
      }
779
0
    }
780
    // enable flush only in last chunk (provides correct calculation of flushPOC)
781
0
    xProcessFirstPassData( flush && ( m_numPicAddedToList == m_numPicStatsTotal ), poc );
782
0
  }
783
0
}
784
785
void RateCtrl::xProcessFirstPassData( const bool flush, const int poc )
786
0
{
787
0
  CHECK( m_listRCFirstPassStats.size() == 0, "No data available from the first pass!" );
788
789
0
  m_listRCFirstPassStats.sort( []( const TRCPassStats& a, const TRCPassStats& b ) { return a.poc < b.poc; } );
790
791
0
  if ( flush || !m_pcEncCfg->m_LookAhead )
792
0
  {
793
    // store start POC of last chunk of pictures
794
0
    flushPOC = m_listRCFirstPassStats.back().poc - std::max( 32, m_pcEncCfg->m_GOPSize );
795
0
  }
796
0
  if ( flush && m_pcEncCfg->m_LookAhead )
797
0
  {
798
0
    encRCSeq->isRateSavingMode = true;
799
0
  }
800
801
  // perform a simple scene change detection on first-pass data and update RC parameters when new scenes are detected
802
0
  detectSceneCuts();
803
804
  // process and scale GOP and frame bits using the data from the first pass to account for different target bitrates
805
0
  processGops();
806
807
0
  if( m_pcEncCfg->m_GOPSize > 8
808
0
      && m_pcEncCfg->m_IntraPeriod >= m_pcEncCfg->m_GOPSize
809
0
      && ( m_pcEncCfg->m_usePerceptQPA || m_pcEncCfg->m_vvencMCTF.MCTF > 0 )
810
0
      && m_pcEncCfg->m_RCNumPasses == 1 )
811
0
  {
812
0
    updateMotionErrStatsGop( flush, poc );
813
0
  }
814
815
0
  encRCSeq->firstPassData = m_listRCFirstPassStats;
816
0
}
817
818
double RateCtrl::getAverageBitsFromFirstPass()
819
0
{
820
0
  const uint16_t vaMin = 1u << (encRCSeq->bitDepth - 6);
821
0
  const int16_t factor = (m_pcEncCfg->m_QP > 27 ? 2 : 3);
822
0
  const uint32_t shift = (m_pcEncCfg->m_QP > 37 ? 1 : 4);
823
0
  int vaTm1 = 0, vaTm2 = 0; // temporal memories
824
0
  uint64_t totalBitsFirstPass = 0;
825
0
  std::list<TRCPassStats>::iterator it;
826
827
0
  if (encRCSeq->intraPeriod > 1 && encRCSeq->gopSize > 1 && m_pcEncCfg->m_LookAhead)
828
0
  {
829
0
    const int gopsInIp  = (2 * encRCSeq->intraPeriod + (encRCSeq->gopSize >> 1)) / encRCSeq->gopSize;
830
0
    int l = 2 - (gopsInIp & 1); // fract. tuning
831
0
    uint64_t tlBits [8] = { 0 };
832
0
    unsigned tlCount[8] = { 0 };
833
834
0
    for (it = m_listRCFirstPassStats.begin(); it != m_listRCFirstPassStats.end(); it++) // sum per level
835
0
    {
836
0
      if (tlBits[it->tempLayer] > 0 && it->refreshParameters)
837
0
      {
838
        // take maximum of average temporal level-wise bit counts from last scene and from current scene
839
0
        tlBits[it->tempLayer] = (tlBits[it->tempLayer] + (tlCount[it->tempLayer] >> 1)) / std::max (1u, tlCount[it->tempLayer]);
840
0
        tlBits[it->tempLayer] = std::max (tlBits[it->tempLayer], (uint64_t) it->numBits);
841
0
        tlCount[it->tempLayer] = 1;
842
0
      }
843
0
      else
844
0
      {
845
0
        tlBits[it->tempLayer] += it->numBits;
846
0
        tlCount[it->tempLayer]++;
847
0
      }
848
0
    }
849
0
    if (tlBits[0] == 0)
850
0
    {
851
0
      l = 0; // no I-frame in the analysis range
852
0
    }
853
854
0
    totalBitsFirstPass = (2 * tlBits[0] + (tlCount[0] >> 1)) / std::max (1u, tlCount[0]) +
855
0
            ((gopsInIp - l) * tlBits[1] + (tlCount[1] >> 1)) / std::max (1u, tlCount[1]);
856
0
    for (l = 2; l <= 7; l++)
857
0
    {
858
0
      totalBitsFirstPass += ((gopsInIp << (l - 2)) * tlBits[l] + (tlCount[l] >> 1)) / std::max (1u, tlCount[l]);
859
0
    }
860
861
0
    return totalBitsFirstPass / (2.0 * encRCSeq->intraPeriod);
862
0
  }
863
864
0
  for (it = m_listRCFirstPassStats.begin(); it != m_listRCFirstPassStats.end(); it++) // for two-pass RC
865
0
  {
866
0
    const int vaTmp = std::max (0, (it->visActY << (12 - encRCSeq->bitDepth)) - it->spVisAct);
867
0
    const int vaSum = vaTmp + vaTm2; // improve rate match on temporally downsampled or very dark videos
868
869
0
    if (vaSum > 0 && vaTm1 * 2 * factor < vaSum && vaTm2 * 4 > vaTmp * 3 && vaTm2 * 3 < vaTmp * 4)
870
0
    {
871
0
      totalBitsFirstPass += (it->numBits * uint64_t (vaSum + vaTm1 * 2) * factor + (vaSum * 2)) / (vaSum * (factor + 1));
872
0
    }
873
0
    else
874
0
    {
875
0
      totalBitsFirstPass += (it->visActY >= vaMin && it->visActY < vaMin + (1u << shift) ? (it->numBits * (it->visActY + 1u - vaMin)) >> shift : it->numBits);
876
0
    }
877
878
0
    vaTm2 = vaTm1;
879
0
    vaTm1 = vaTmp;
880
0
  }
881
882
0
  return totalBitsFirstPass / (double) m_listRCFirstPassStats.size();
883
0
}
884
885
void RateCtrl::detectSceneCuts()
886
0
{
887
0
  const int minPocDif = (encRCSeq->gopSize + 1) >> 1;
888
0
  double psnrTL01Prev = 0.0;
889
0
  int sceneCutPocPrev = -minPocDif;
890
0
  uint16_t visActPrev = 0;
891
0
  bool needRefresh[8] = { false };
892
0
  std::list<TRCPassStats>::iterator it = m_listRCFirstPassStats.begin();
893
894
0
  visActPrev = it->visActY;
895
0
  if (it->tempLayer <= 1) psnrTL01Prev = it->psnrY;
896
0
  it->refreshParameters = (it->poc == 0);
897
898
0
  for (it++; it != m_listRCFirstPassStats.end(); it++)
899
0
  {
900
0
    const int tmpLevel = it->tempLayer;
901
0
    const bool  isTL01 = (tmpLevel <= 1);
902
903
0
    it->isNewScene = ((it->visActY * 64 > visActPrev * 181) || (isTL01 && it->visActY <= (1u << (encRCSeq->bitDepth - 6))) || (isTL01 && it->visActY + 3 > visActPrev && psnrTL01Prev > 0.0 && std::abs (it->psnrY - psnrTL01Prev) > 4.5));
904
905
0
    if (it->isNewScene) // filter out scene cuts which happen too closely to the last detected scene cut
906
0
    {
907
0
      if (it->poc >= sceneCutPocPrev + minPocDif)
908
0
      {
909
0
        for (int frameLevel = 0; frameLevel <= m_pcEncCfg->m_maxTLayer + 1; frameLevel++)
910
0
        {
911
0
          needRefresh[frameLevel] = true;
912
0
        }
913
0
        sceneCutPocPrev = it->poc;
914
0
      }
915
0
      else
916
0
      {
917
0
        it->isNewScene = false;
918
0
      }
919
0
    }
920
0
    if (it->scType == SCT_TL0_SCENE_CUT && !needRefresh[0]) // assume scene cuts at all adapted I-frames
921
0
    {
922
0
      it->isNewScene = needRefresh[0] = needRefresh[1] = needRefresh[2] = true;
923
0
    }
924
925
0
    it->refreshParameters = needRefresh[tmpLevel];
926
0
    needRefresh[tmpLevel] = false;
927
928
0
    visActPrev = it->visActY;
929
0
    if (isTL01) psnrTL01Prev = it->psnrY;
930
0
  }
931
0
}
932
933
void RateCtrl::processGops()
934
0
{
935
0
  const double bp1pf = getAverageBitsFromFirstPass();  // first-pass bits/frame
936
0
  const double ratio = (double) encRCSeq->targetRate / (encRCSeq->frameRate * bp1pf); // 2nd-to-1st pass
937
0
  double fac;
938
0
  int vecIdx;
939
0
  int gopNum;
940
0
  std::list<TRCPassStats>::iterator it;
941
0
  std::vector<uint32_t> gopBits (2 + (m_listRCFirstPassStats.back().gopNum - m_listRCFirstPassStats.front().gopNum)); // +2 for the first I frame (GOP) and a potential last incomplete GOP
942
0
  std::vector<float> gopTempVal (2 + (m_listRCFirstPassStats.back().gopNum - m_listRCFirstPassStats.front().gopNum));
943
944
0
  vecIdx = 0;
945
0
  gopNum = m_listRCFirstPassStats.front().gopNum;
946
0
  for (it = m_listRCFirstPassStats.begin(); it != m_listRCFirstPassStats.end(); it++) // scaling, part 1
947
0
  {
948
0
    if (it->gopNum > gopNum)
949
0
    {
950
0
      vecIdx++;
951
0
      gopNum = it->gopNum;
952
0
    }
953
0
    CHECK (vecIdx >= (int) gopBits.size(), "array idx out of bounds");
954
0
    it->targetBits = (int) std::max (1.0, 0.5 + it->numBits * ratio);
955
0
    gopBits[vecIdx] += (uint32_t) it->targetBits; // sum is gf in VCIP'21 paper
956
957
0
    if (it->poc == 0 && it->isIntra) // put the first I-frame into separate GOP
958
0
    {
959
0
      vecIdx++;
960
0
    }
961
0
  }
962
963
0
  vecIdx = 0;
964
0
  fac = 1.0 / gopBits[vecIdx];
965
0
  gopTempVal[vecIdx] = 1.0f;
966
0
  gopNum = m_listRCFirstPassStats.front().gopNum;
967
0
  for (it = m_listRCFirstPassStats.begin(); it != m_listRCFirstPassStats.end(); it++) // scaling, part 2
968
0
  {
969
0
    if (it->gopNum > gopNum)
970
0
    {
971
0
      vecIdx++;
972
0
      fac = 1.0 / gopBits[vecIdx];
973
0
      gopTempVal[vecIdx] = (it->isIntra ? float (it->targetBits * fac) : 0.0f);
974
0
      gopNum = it->gopNum;
975
0
    }
976
0
    it->frameInGopRatio = it->targetBits * fac; // i.e., rf/gf in VCIP'21 paper
977
978
0
    if (it->poc == 0 && it->isIntra) // put the first I-frame into separate GOP
979
0
    {
980
0
      vecIdx++;
981
0
      fac = 1.0 / gopBits[vecIdx];
982
0
      gopTempVal[vecIdx] = 0.0f; // relax rate constraint a bit in first 2 GOPs
983
0
    }
984
0
  }
985
986
0
  if (!encRCSeq->isLookAhead && encRCSeq->maxGopRate < INT32_MAX) // pre-capper
987
0
  {
988
0
    double savedBits = 0.0, scale = 0.0, maxBits;
989
0
    uint32_t savedGOPs = 0;
990
991
0
    for (vecIdx = 0; vecIdx < (int) gopBits.size(); vecIdx++)
992
0
    {
993
0
      if (gopTempVal[vecIdx] > 0.0f)
994
0
      {
995
0
        scale = (double) encRCSeq->maxGopRate * encRCSeq->intraPeriod / (encRCSeq->intraPeriod + gopTempVal[vecIdx] * encRCSeq->gopSize);
996
0
      }
997
0
      maxBits = scale * (1.0 + gopTempVal[vecIdx]);
998
999
0
      if ((double) gopBits[vecIdx] > maxBits)
1000
0
      {
1001
0
        gopTempVal[vecIdx] = float (maxBits / gopBits[vecIdx]); // saving ratio
1002
0
        savedBits += gopBits[vecIdx] - maxBits;
1003
0
        savedGOPs++;
1004
0
      }
1005
0
      else
1006
0
      {
1007
0
        gopTempVal[vecIdx] = 0.f; // signal to next loop that rate is below cap
1008
0
      }
1009
0
    }
1010
1011
0
    if (savedGOPs > 0)  // distribute saved bits equally across not capped GOPs
1012
0
    {
1013
0
      savedBits /= gopBits.size() - savedGOPs;
1014
0
      vecIdx = 0;
1015
0
      scale   = (double) encRCSeq->maxGopRate * encRCSeq->intraPeriod / (encRCSeq->intraPeriod + 1.0 /*frameInGopRatio*/ * encRCSeq->gopSize);
1016
0
      maxBits = scale * (1.0 + 1.0 /*frameInGopRatio*/);
1017
0
      fac = (gopTempVal[vecIdx] > 0.0f ? gopTempVal[vecIdx] : std::min (maxBits, gopBits[vecIdx] + savedBits) / gopBits[vecIdx]);
1018
0
      gopNum = m_listRCFirstPassStats.front().gopNum;
1019
0
      for (it = m_listRCFirstPassStats.begin(); it != m_listRCFirstPassStats.end(); it++) // cap, part 3
1020
0
      {
1021
0
        if (it->gopNum > gopNum)
1022
0
        {
1023
0
          vecIdx++;
1024
0
          if (it->isIntra)
1025
0
          {
1026
0
            scale = (double) encRCSeq->maxGopRate * encRCSeq->intraPeriod / (encRCSeq->intraPeriod + it->frameInGopRatio * encRCSeq->gopSize);
1027
0
          }
1028
0
          maxBits = scale * (it->isIntra ? 1.0 + it->frameInGopRatio : 1.0);
1029
0
          fac = (gopTempVal[vecIdx] > 0.0f ? gopTempVal[vecIdx] : std::min (maxBits, gopBits[vecIdx] + savedBits) / gopBits[vecIdx]);
1030
0
          gopNum = it->gopNum;
1031
0
        }
1032
0
        it->targetBits = (int) std::max (1.0, 0.5 + it->targetBits * fac);
1033
1034
0
        if (it->poc == 0 && it->isIntra) // put first I-frame into separate GOP
1035
0
        {
1036
0
          vecIdx++;
1037
          // maxBits = scale * 1.0; relax rate constraint a bit in first 2 GOPs
1038
0
          fac = (gopTempVal[vecIdx] > 0.0f ? gopTempVal[vecIdx] : std::min (maxBits, gopBits[vecIdx] + savedBits) / gopBits[vecIdx]);
1039
0
        }
1040
0
      }
1041
0
    }
1042
0
  }
1043
0
}
1044
1045
void RateCtrl::updateMotionErrStatsGop( const bool flush, const int poc )
1046
0
{
1047
0
  CHECK( poc <= m_updateNoisePoc, "given TL0 poc before last TL0 poc" );
1048
1049
0
  const bool bIncomplete = ( poc - m_updateNoisePoc ) < m_pcEncCfg->m_GOPSize;
1050
1051
  // reset only if full gop pics available or previous gop ends with intra frame
1052
0
  if( ! bIncomplete || m_resetNoise )
1053
0
  {
1054
0
    m_maxPicMotionError = 0;
1055
0
    std::fill_n( m_minNoiseLevels, QPA_MAX_NOISE_LEVELS, 255u );
1056
0
  }
1057
0
  m_resetNoise = true;
1058
1059
  // currently disabled for last incomplete gop (TODO: check)
1060
0
  if( bIncomplete && flush )
1061
0
  {
1062
0
    m_maxPicMotionError = 0;
1063
0
    std::fill_n( m_minNoiseLevels, QPA_MAX_NOISE_LEVELS, 255u );
1064
0
    return;
1065
0
  }
1066
1067
  // continue with stats after last used poc
1068
0
  const int startPoc = m_updateNoisePoc + 1;
1069
0
  auto itr           = find_if( m_listRCFirstPassStats.begin(), m_listRCFirstPassStats.end(), [ startPoc ]( const auto& stat ) { return stat.poc == startPoc; } );
1070
0
  if( itr == m_listRCFirstPassStats.end() )
1071
0
  {
1072
0
    itr = m_listRCFirstPassStats.begin();
1073
0
  }
1074
1075
0
  for( ; itr != m_listRCFirstPassStats.end(); itr++ )
1076
0
  {
1077
0
    const auto& stat = *itr;
1078
0
    if( stat.poc > poc )
1079
0
    {
1080
0
      m_gopMEErrorCBufIdx = ( m_gopMEErrorCBufIdx + 1u ) & uint8_t( QPA_MAX_NOISE_LEVELS - 1 );
1081
0
      m_gopMEErrorCBuf[ m_gopMEErrorCBufIdx ] = m_maxPicMotionError;
1082
0
      m_resetNoise = stat.isIntra; // in case last update poc is intra, we cannot reuse the old noise levels for the next gop
1083
0
      break;
1084
0
    }
1085
0
    m_maxPicMotionError = std::max( m_maxPicMotionError, stat.motionEstError );
1086
1087
0
    for( int i = 0; i < QPA_MAX_NOISE_LEVELS; i++ )
1088
0
    {
1089
0
      if( stat.minNoiseLevels[ i ] < m_minNoiseLevels[ i ] )
1090
0
      {
1091
0
        m_minNoiseLevels[ i ] = stat.minNoiseLevels[ i ];
1092
0
      }
1093
0
    }
1094
0
  }
1095
1096
  // store highest poc used for current update
1097
0
  m_updateNoisePoc = poc;
1098
0
}
1099
1100
double RateCtrl::getLookAheadBoostFac( const int thresholdDivisor )
1101
0
{
1102
0
  const unsigned thresh = 64 / std::max (1, thresholdDivisor);
1103
0
  unsigned num = 0, sum = 0;
1104
1105
0
  for (int i = 0; i < QPA_MAX_NOISE_LEVELS; i++) // go through non-zero values in circ. buffer
1106
0
  {
1107
0
    if (m_gopMEErrorCBuf[i] > 0)
1108
0
    {
1109
0
      num++;
1110
0
      sum += m_gopMEErrorCBuf[i];
1111
0
    }
1112
0
  }
1113
1114
0
  if (num > 0 && m_gopMEErrorCBuf[m_gopMEErrorCBufIdx] * num > sum + thresh * num) // boosting
1115
0
  {
1116
0
    return double (m_gopMEErrorCBuf[m_gopMEErrorCBufIdx] * num) / (sum + thresh * num);
1117
0
  }
1118
0
  return 1.0;
1119
0
}
1120
1121
double RateCtrl::updateQPstartModelVal()
1122
0
{
1123
0
  unsigned num = 0, sum = 0;
1124
1125
0
  for (int avgIndex = 0; avgIndex < QPA_MAX_NOISE_LEVELS; avgIndex++) // go through all ranges
1126
0
  {
1127
0
    if (m_minNoiseLevels[avgIndex] < 255)
1128
0
    {
1129
0
      num++;
1130
0
      sum += m_minNoiseLevels[avgIndex];
1131
0
    }
1132
0
  }
1133
1134
0
  if (num == 0 || sum == 0) return 24.0; // default if no data, else noise level equivalent QP
1135
1136
0
  return 24.0 + 0.5 * (6.0 * log ((double) sum / (double) num) / log (2.0) - 1.0 - 24.0);
1137
0
}
1138
1139
void RateCtrl::addRCPassStats( const int poc,
1140
                               const int qp,
1141
                               const double lambda,
1142
                               const uint16_t visActY,
1143
                               const uint32_t numBits,
1144
                               const double psnrY,
1145
                               const bool isIntra,
1146
                               const uint32_t tempLayer,
1147
                               const bool isStartOfIntra,
1148
                               const bool isStartOfGop,
1149
                               const int gopNum,
1150
                               const SceneType scType,
1151
                               int spVisAct,
1152
                               const uint16_t motEstError,
1153
                               const uint8_t minNoiseLevels[ QPA_MAX_NOISE_LEVELS ] )
1154
0
{
1155
0
  storeStatsData (TRCPassStats (poc, qp, lambda, visActY, numBits, psnrY, isIntra, tempLayer + int (!isIntra), isStartOfIntra, isStartOfGop, gopNum, scType, spVisAct, motEstError, minNoiseLevels));
1156
0
}
1157
1158
void RateCtrl::updateAfterPicEncRC( const Picture* pic )
1159
0
{
1160
0
  EncRCPic* encRCPic = pic->encRCPic;
1161
0
  const int clipBits = std::max( encRCPic->targetBits, pic->actualTotalBits );
1162
1163
0
  encRCPic->updateAfterPicture( pic->isMeanQPLimited ? clipBits : pic->actualTotalBits, pic->slices[ 0 ]->sliceQp );
1164
0
  encRCPic->addToPictureList( getPicList() );
1165
0
  encRCSeq->updateAfterPic( pic->actualTotalBits, encRCPic->tmpTargetBits );
1166
1167
0
  if ( encRCSeq->isLookAhead )
1168
0
  {
1169
0
    if ( pic->isMeanQPLimited ) encRCSeq->bitsUsedQPLimDiff += pic->actualTotalBits - clipBits;
1170
0
    encRCSeq->bitsUsed += encRCSeq->bitsUsedQPLimDiff;  // sum up actual, not ideal bit counts
1171
0
  }
1172
0
}
1173
1174
void RateCtrl::initRateControlPic( Picture& pic, Slice* slice, int& qp, double& finalLambda )
1175
0
{
1176
0
  const int frameLevel = ( slice->isIntra() ? 0 : slice->TLayer + 1 );
1177
0
  EncRCPic*   encRcPic = new EncRCPic;
1178
0
  double lambda = encRCSeq->maxEstLambda;
1179
0
  int   sliceQP = MAX_QP;
1180
1181
0
  encRcPic->create( encRCSeq, frameLevel, slice->poc );
1182
0
  pic.encRCPic = encRcPic;
1183
1184
0
  if ( frameLevel <= 7 )
1185
0
  {
1186
0
    if ( m_pcEncCfg->m_RCNumPasses == 2 || m_pcEncCfg->m_LookAhead )
1187
0
    {
1188
0
      std::list<TRCPassStats>::iterator it;
1189
1190
0
      for ( it = encRCSeq->firstPassData.begin(); it != encRCSeq->firstPassData.end(); it++ )
1191
0
      {
1192
0
        if ( it->poc == slice->poc && it->numBits > 0 )
1193
0
        {
1194
0
          const double sqrOfResRatio = std::min( 1.0, double( m_pcEncCfg->m_SourceWidth * m_pcEncCfg->m_SourceHeight ) / ( 3840.0 * 2160.0 ) );
1195
0
          const int firstPassSliceQP = it->qp;
1196
0
          const int budgetRelaxScale = ( encRCSeq->maxGopRate + 0.5 < 2.0 * (double)encRCSeq->targetRate * encRCSeq->gopSize / encRCSeq->frameRate ? 2 : 3 ); // quarters
1197
0
          const bool isRateCapperMax = ( encRCSeq->maxGopRate + 0.5 >= 3.0 * (double)encRCSeq->targetRate * encRCSeq->gopSize / encRCSeq->frameRate );
1198
0
          const bool isEndOfSequence = ( it->poc >= flushPOC && flushPOC >= 0 );
1199
0
          const double dLimit = ( isRateCapperMax ? 3.0 : 0.5 * budgetRelaxScale + 0.5 );
1200
0
          double d = (double)it->targetBits, tmpVal;
1201
1202
0
          encRcPic->visActSteady = it->visActY;
1203
1204
0
          if ( it->refreshParameters ) // reset counters for budget usage in subsequent frames
1205
0
          {
1206
0
            encRCSeq->qpCorrection[ frameLevel ] = ( it->poc == 0 && it->isIntra && d < it->numBits ? std::max( -1.0 * it->visActY / double( 1 << ( encRCSeq->bitDepth - 3 ) ), 1.0 - it->numBits / d ) : 0.0 );
1207
0
            if ( !m_pcEncCfg->m_LookAhead )
1208
0
            {
1209
0
              encRCSeq->actualBitCnt[ frameLevel ] = encRCSeq->targetBitCnt[ frameLevel ] = 0;
1210
0
            }
1211
0
            encRcPic->refreshParams = true;
1212
0
          }
1213
0
          if ( it->isIntra ) // update temp stationarity for budget usage in subsequent frames
1214
0
          {
1215
0
            encRCSeq->lastIntraSM = it->frameInGopRatio;
1216
0
          }
1217
0
          if ( it->isStartOfGop )
1218
0
          {
1219
0
            bool allowMoreRatePeaks = false;
1220
0
            encRCSeq->rateBoostFac  = 1.0;
1221
1222
0
            if ( m_pcEncCfg->m_LookAhead && !encRCSeq->isRateSavingMode ) // rate boost factor
1223
0
            {
1224
0
              encRCSeq->rateBoostFac = getLookAheadBoostFac( isRateCapperMax ? 2 : budgetRelaxScale - 1 );
1225
0
              allowMoreRatePeaks = ( encRCSeq->rateBoostFac > 1.0 && isRateCapperMax );
1226
0
              if ( allowMoreRatePeaks && !isEndOfSequence )
1227
0
              {
1228
0
                encRCSeq->lastIntraSM = 1.0;
1229
0
              }
1230
0
              if ( encRCSeq->rateBoostFac > 1.0 && !isEndOfSequence )
1231
0
              {
1232
0
                encRCSeq->estimatedBitUsage = std::max( encRCSeq->estimatedBitUsage, encRCSeq->bitsUsed ); // TL<2: relax constraint
1233
0
              }
1234
0
            }
1235
0
            encRCSeq->isIntraGOP = ( ( it->isStartOfIntra || allowMoreRatePeaks ) && !isEndOfSequence && !encRCSeq->isRateSavingMode );
1236
0
          }
1237
0
          else if ( frameLevel == 2 && it->refreshParameters && encRCSeq->scRelax && !isEndOfSequence && !encRCSeq->isRateSavingMode )
1238
0
          {
1239
0
            encRCSeq->estimatedBitUsage = std::max( encRCSeq->estimatedBitUsage, encRCSeq->bitsUsed ); // cut: relax rate constraint
1240
0
          }
1241
0
          encRCSeq->scRelax = ( frameLevel < 2 && it->refreshParameters && encRCSeq->estimatedBitUsage > encRCSeq->bitsUsed );
1242
1243
0
          CHECK( slice->TLayer >= 7, "analyzed RC frame must have TLayer < 7" );
1244
1245
0
          tmpVal = ( encRCSeq->isIntraGOP ? 0.5 + 0.5 * encRCSeq->lastIntraSM : 0.5 ); // IGOP
1246
0
          if ( !isEndOfSequence && !m_pcEncCfg->m_LookAhead && frameLevel == 2 && encRCSeq->maxGopRate + 0.5 < 3.0 * (double)encRCSeq->targetRate * encRCSeq->gopSize / encRCSeq->frameRate )
1247
0
          {
1248
0
            encRCSeq->estimatedBitUsage = std::max( encRCSeq->estimatedBitUsage, ( budgetRelaxScale * encRCSeq->estimatedBitUsage + ( 4 - budgetRelaxScale ) * encRCSeq->bitsUsed + 2 ) >> 2 );
1249
0
          }
1250
1251
0
          if ( encRCSeq->rateBoostFac > 1.0 )
1252
0
          {
1253
0
            it->targetBits = int( it->targetBits * encRCSeq->rateBoostFac + 0.5 ); // boosting
1254
0
          }
1255
0
          encRcPic->tmpTargetBits = it->targetBits;
1256
1257
          // calculate the difference of under or overspent bits and adjust the current target bits based on the GOP and frame ratio
1258
0
          d = encRcPic->tmpTargetBits + std::min( (int64_t) encRCSeq->maxGopRate, encRCSeq->estimatedBitUsage - encRCSeq->bitsUsed ) * tmpVal * it->frameInGopRatio;
1259
1260
          // try to hit target rate more aggressively in last coded frames, lambda/QP clipping below will ensure smooth value change
1261
0
          if ( isEndOfSequence )
1262
0
          {
1263
0
            d += std::min( (int64_t) encRCSeq->maxGopRate, encRCSeq->estimatedBitUsage - encRCSeq->bitsUsed ) * ( 1.0 - tmpVal ) * it->frameInGopRatio;
1264
0
          }
1265
0
          else if ( d > dLimit * encRcPic->tmpTargetBits )
1266
0
          {
1267
0
            d = encRcPic->tmpTargetBits * dLimit; // prevent large spendings after easy scenes
1268
0
          }
1269
0
          else if ( d * dLimit < encRcPic->tmpTargetBits )
1270
0
          {
1271
0
            d = encRcPic->tmpTargetBits / dLimit; // prevent small spendings after hard scenes
1272
0
          }
1273
1274
0
          if ( !isEndOfSequence && it->isStartOfGop && it->poc > 0 ) // target bitrate capping
1275
0
          {
1276
0
            const double scale = encRCSeq->intraPeriod / ( encRCSeq->intraPeriod + encRCSeq->lastIntraSM * encRCSeq->gopSize );
1277
0
            const double fBits = scale * ( encRCSeq->isIntraGOP ? 1.0 + encRCSeq->lastIntraSM : 1.0 ) * it->frameInGopRatio; // IGOP
1278
1279
0
            if ( d > fBits * encRCSeq->maxGopRate ) d = fBits * encRCSeq->maxGopRate;
1280
0
          }
1281
0
          d = std::max( 1.0, d );
1282
0
          encRcPic->targetBits = int( d + 0.5 );
1283
1284
0
          tmpVal = updateQPstartModelVal() + log (sqrOfResRatio) / log (2.0); // GOP's QPstart
1285
0
          d /= (double)it->numBits;
1286
0
          d = firstPassSliceQP - ( 105.0 / 128.0 ) * sqrt( (double)std::max( 1, firstPassSliceQP ) ) * log( d ) / log( 2.0 );
1287
0
          sliceQP = int( 0.5 + d + ( it->isIntra ? 0.375 : 0.5 ) * std::max( 0.0, tmpVal - d ) + encRCSeq->qpCorrection[ frameLevel ] );
1288
1289
0
          encRcPic->clipTargetQP( getPicList(), ( m_pcEncCfg->m_LookAhead ? getBaseQP() : m_pcEncCfg->m_QP ) + ( it->isIntra ? m_pcEncCfg->m_intraQPOffset : 0 ), 5 - budgetRelaxScale,
1290
0
                                  ( it->poc < encRCSeq->gopSize ? 0 : ( m_pcEncCfg->m_maxTLayer + 1 ) >> 1 ), sqrOfResRatio, sliceQP, &encRCSeq->lastAverageQP );
1291
0
          lambda = it->lambda * pow( 2.0, double( sliceQP - firstPassSliceQP ) / 3.0 );
1292
0
          lambda = Clip3( encRCSeq->minEstLambda, encRCSeq->maxEstLambda, lambda );
1293
1294
0
          if ( m_pcEncCfg->m_LookAhead ) // update frame bit ratios for bit budget calculation
1295
0
          {
1296
0
            if ( it->isStartOfGop )
1297
0
            {
1298
0
              if ( it->poc == 0 && it->isIntra ) // handle first I-frame being in separate GOP
1299
0
              {
1300
0
                uint32_t gopBits = it->numBits;
1301
0
                std::list<TRCPassStats>::iterator itNext = it;
1302
1303
0
                for ( itNext++; itNext != encRCSeq->firstPassData.end() && !itNext->isStartOfGop; itNext++ )
1304
0
                {
1305
0
                  gopBits += itNext->numBits;
1306
0
                }
1307
0
                encRCSeq->lastIntraSM = (double)it->numBits / gopBits;
1308
0
              }
1309
0
              else if ( it->isIntra && !it->isStartOfIntra && !isEndOfSequence && !encRCSeq->isRateSavingMode && encRCSeq->estimatedBitUsage - encRCSeq->bitsUsed < encRcPic->targetBits )
1310
0
              {
1311
0
                encRCSeq->bitsUsedQPLimDiff += ( encRCSeq->estimatedBitUsage - encRCSeq->bitsUsed - encRcPic->targetBits ) >> 1;
1312
0
              }
1313
0
            }
1314
0
          }
1315
0
          if ( it->isIntra ) // update history for parameter clipping in subsequent key frames
1316
0
          {
1317
0
            encRCSeq->lastIntraQP = sliceQP;
1318
0
          }
1319
1320
0
          break;
1321
0
        }
1322
0
      }
1323
0
    }
1324
0
  }
1325
1326
0
  qp = sliceQP;
1327
0
  finalLambda = lambda;
1328
0
}
1329
1330
}