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/EncGOP.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     EncGOP.cpp
45
    \brief    GOP encoder class
46
*/
47
48
#include "EncGOP.h"
49
#include "CommonLib/SEI.h"
50
#include "CommonLib/UnitTools.h"
51
#include "CommonLib/dtrace_codingstruct.h"
52
#include "CommonLib/dtrace_buffer.h"
53
#include "CommonLib/TimeProfiler.h"
54
#include "CommonLib/MD5.h"
55
#include "NALwrite.h"
56
#include "BitAllocation.h"
57
#include "EncHRD.h"
58
#include "GOPCfg.h"
59
60
#include <list>
61
62
//! \ingroup EncoderLib
63
//! \{
64
65
namespace vvenc {
66
67
#ifdef TRACE_ENABLE_ITT
68
static __itt_string_handle* itt_handle_start = __itt_string_handle_create( "Start" );
69
static __itt_domain* itt_domain_gopEncoder   = __itt_domain_create( "GOPEncoder" );
70
#endif
71
72
// ====================================================================================================================
73
// fast forward decoder in encoder
74
// ====================================================================================================================
75
76
void initPicAuxQPOffsets( const Slice* slice, const bool isBIM ) // get m_picShared->m_picAuxQpOffset and m_picShared->m_ctuBimQpOffset if unavailable
77
1.49k
{
78
1.49k
  const Picture* slicePic = slice->pic;
79
80
1.49k
  if (isBIM && slicePic && slicePic->m_picShared->m_ctuBimQpOffset.empty())
81
1.49k
  {
82
1.49k
    const Picture* refPicL0 = slice->getRefPic (REF_PIC_LIST_0, 0);
83
1.49k
    const Picture* refPicL1 = slice->getRefPic (REF_PIC_LIST_1, 0);
84
85
1.49k
    if (refPicL0 && !refPicL0->m_picShared->m_ctuBimQpOffset.empty() &&
86
0
        refPicL1 && !refPicL1->m_picShared->m_ctuBimQpOffset.empty() &&
87
0
        refPicL0->m_picShared->m_ctuBimQpOffset.size() == refPicL1->m_picShared->m_ctuBimQpOffset.size())
88
0
    {
89
0
      const PicShared* pic0 = refPicL0->m_picShared;
90
0
      const PicShared* pic1 = refPicL1->m_picShared;
91
0
      PicShared* const picC = slicePic->m_picShared;
92
0
      const int32_t  numCtu = (int32_t) pic0->m_ctuBimQpOffset.size();
93
0
      int i, sumCtuQpOffset = 0;
94
95
0
      picC->m_ctuBimQpOffset.resize (numCtu);
96
97
0
      for (i = 0; i < numCtu; i++) // scale and merge QPs
98
0
      {
99
0
        const int qpOffset0 = pic0->m_ctuBimQpOffset[i] + pic0->m_picAuxQpOffset; // CTU delta-QP #1
100
0
        const int qpOffset1 = pic1->m_ctuBimQpOffset[i] + pic1->m_picAuxQpOffset; // CTU delta-QP #2
101
0
        const int qpOffsetC = (3 * qpOffset0 + 3 * qpOffset1 + (qpOffset0 + qpOffset1 < 0 ? 3 : 4)) >> 3; // 3 instead of 4 for correct rounding to -2
102
103
0
        picC->m_ctuBimQpOffset[i] = qpOffsetC;
104
0
        sumCtuQpOffset += qpOffsetC;
105
0
      }
106
107
0
      picC->m_picAuxQpOffset = (sumCtuQpOffset + (sumCtuQpOffset < 0 ? -(numCtu >> 1) : numCtu >> 1)) / numCtu; // pic average; delta-QP scaling: 0.75
108
0
      for (i = 0; i < numCtu; i++) // excl. average again
109
0
      {
110
0
        picC->m_ctuBimQpOffset[i] -= picC->m_picAuxQpOffset; // delta-QP relative to the aux average
111
0
      }
112
0
    }
113
1.49k
  }
114
1.49k
}
115
116
117
// ====================================================================================================================
118
// Constructor / destructor / initialization / destroy
119
// ====================================================================================================================
120
121
EncGOP::EncGOP( MsgLog& logger )
122
1.49k
  : msg                  ( logger )
123
1.49k
  , m_recYuvBufFunc      ( nullptr )
124
1.49k
  , m_recYuvBufCtx       ( nullptr )
125
1.49k
  , m_threadPool         ( nullptr )
126
1.49k
  , m_pcEncCfg           ( nullptr )
127
1.49k
  , m_gopCfg             ( nullptr )
128
1.49k
  , m_pcRateCtrl         ( nullptr )
129
1.49k
  , m_spsMap             ( MAX_NUM_SPS )
130
1.49k
  , m_ppsMap             ( MAX_NUM_PPS )
131
1.49k
  , m_isPreAnalysis      ( false )
132
1.49k
  , m_bFirstWrite        ( true )
133
1.49k
  , m_bRefreshPending    ( false )
134
1.49k
  , m_lastCodingNum      ( -1 )
135
1.49k
  , m_numPicsCoded       ( 0 )
136
1.49k
  , m_numPicsInMissing   ( 0 )
137
1.49k
  , m_numPicsOutOffset   ( 0 )
138
1.49k
  , m_lastCts            ( 0 )
139
1.49k
  , m_pocRecOut          ( 0 )
140
1.49k
  , m_ticksPerFrameMul4  ( 0 )
141
1.49k
  , m_lastIDR            ( 0 )
142
1.49k
  , m_lastRasPoc         ( MAX_INT )
143
1.49k
  , m_pocCRA             ( 0 )
144
1.49k
  , m_associatedIRAPPOC  ( 0 )
145
1.49k
  , m_associatedIRAPType ( VVENC_NAL_UNIT_CODED_SLICE_IDR_N_LP )
146
1.49k
{
147
1.49k
}
148
149
EncGOP::~EncGOP()
150
1.49k
{
151
1.49k
  xWaitFgaTasks();
152
1.49k
  freePicList();
153
154
1.49k
  for( auto& picEncoder : m_freePicEncoderList )
155
5.97k
  {
156
5.97k
    if( picEncoder )
157
5.97k
    {
158
5.97k
      delete picEncoder;
159
5.97k
    }
160
5.97k
  }
161
1.49k
  m_freePicEncoderList.clear();
162
1.49k
  m_threadPool = nullptr;
163
164
  // cleanup parameter sets
165
1.49k
  m_spsMap.clearMap();
166
1.49k
  m_ppsMap.clearMap();
167
168
1.49k
  for( auto& p : m_globalApsList ) delete p;
169
1.49k
  m_globalApsList.clear();
170
1.49k
}
171
172
void EncGOP::init( const VVEncCfg& encCfg, const GOPCfg* gopCfg, RateCtrl& rateCtrl, NoMallocThreadPool* threadPool, bool isPreAnalysis )
173
1.49k
{
174
1.49k
  m_pcEncCfg      = &encCfg;
175
1.49k
  m_gopCfg        = gopCfg;
176
1.49k
  m_pcRateCtrl    = &rateCtrl;
177
1.49k
  m_threadPool    = threadPool;
178
1.49k
  m_isPreAnalysis = isPreAnalysis;
179
180
  // setup parameter sets
181
1.49k
  const int dciId = m_pcEncCfg->m_decodingParameterSetEnabled ? 1 : 0;
182
1.49k
  SPS& sps0       = *( m_spsMap.allocatePS( 0 ) ); // NOTE: implementations that use more than 1 SPS need to be aware of activation issues.
183
1.49k
  PPS& pps0       = *( m_ppsMap.allocatePS( 0 ) );
184
185
1.49k
  xInitSPS( sps0 );
186
1.49k
  sps0.dciId = m_DCI.dciId;
187
1.49k
  xInitVPS( m_VPS );
188
1.49k
  xInitDCI( m_DCI, sps0, dciId );
189
1.49k
  xInitPPS( pps0, sps0 );
190
1.49k
  xInitRPL( sps0 );
191
1.49k
  xInitHrdParameters( sps0 );
192
193
1.49k
  if ( encCfg.m_fga )
194
0
  {
195
    // --fga 1: VTM's analysis with its bugs fixed; --fga 2: with the improvements beyond VTM; --fga 3: --fga 2 with the
196
    // auto-regressive grain model
197
0
    m_fgAnalyzer.init( m_pcEncCfg->m_PadSourceWidth, m_pcEncCfg->m_PadSourceHeight, m_pcEncCfg->m_internChromaFormat,
198
0
                         m_pcEncCfg->m_internalBitDepth, m_pcEncCfg->m_fg.m_fgcSEICompModelPresent, encCfg.m_fga == 2, encCfg.m_fga == 3 );
199
0
    m_fgcByStartPoc[ std::numeric_limits<int>::min() ] = FilmGrainParam( m_fgAnalyzer, m_pcEncCfg->m_fgaStrength );   // no grain, until the first analysis measures it
200
0
  }
201
202
1.49k
  if( !m_pcEncCfg->m_poc0idr )
203
1.49k
  {
204
1.49k
    m_associatedIRAPType = VVENC_NAL_UNIT_CODED_SLICE_IDR_W_RADL;
205
1.49k
  }
206
1.49k
  m_seiEncoder.init( encCfg, gopCfg, m_EncHRD );
207
208
  // One CU encoder per worker thread is enough: a worker encodes one CTU at a time, whichever
209
  // of the parallel pictures it belongs to.
210
1.49k
  const int numThreadRsrc = ( encCfg.m_numThreads > 0 && threadPool ) ? threadPool->numThreads() : 1;
211
1.49k
  m_threadRsrc.init( encCfg, sps0, pps0, &m_globalCtuQpVector, rateCtrl, numThreadRsrc );
212
213
1.49k
  const int maxPicEncoder = ( encCfg.m_maxParallelFrames ) ? encCfg.m_maxParallelFrames : 1;
214
7.46k
  for ( int i = 0; i < maxPicEncoder; i++ )
215
5.97k
  {
216
5.97k
    EncPicture* picEncoder = new EncPicture;
217
5.97k
    picEncoder->init( encCfg, &m_globalCtuQpVector, sps0, pps0, rateCtrl, threadPool, m_threadRsrc );
218
5.97k
    m_freePicEncoderList.push_back( picEncoder );
219
5.97k
  }
220
221
1.49k
  if (encCfg.m_usePerceptQPA)
222
1.49k
  {
223
1.49k
    m_globalCtuQpVector.resize( pps0.useDQP && (encCfg.m_internalUsePerceptQPATempFiltISlice == 2) && encCfg.m_salienceBasedOpt ? pps0.picWidthInCtu * pps0.picHeightInCtu + 1 : 1 );
224
1.49k
  }
225
226
1.49k
  if( m_pcEncCfg->m_FrameRate && m_pcEncCfg->m_TicksPerSecond > 0 )
227
1.49k
  {
228
1.49k
    m_ticksPerFrameMul4 = (int)((int64_t)4 *(int64_t)m_pcEncCfg->m_TicksPerSecond * (int64_t)m_pcEncCfg->m_FrameScale/(int64_t)m_pcEncCfg->m_FrameRate);
229
1.49k
  }
230
1.49k
  m_forceSCC = false;
231
1.49k
  m_rcap.reset();
232
1.49k
}
233
234
235
// ====================================================================================================================
236
// Class interface
237
// ====================================================================================================================
238
239
240
void EncGOP::setRecYUVBufferCallback( void* ctx, std::function<void( void*, vvencYUVBuffer* )> func )
241
1.49k
{
242
1.49k
  m_recYuvBufCtx  = ctx;
243
1.49k
  m_recYuvBufFunc = func;
244
1.49k
}
245
246
void EncGOP::initPicture( Picture* pic )
247
1.49k
{
248
1.49k
  pic->encTime.startTimer();
249
250
1.49k
  pic->TLayer = pic->gopEntry->m_temporalId;
251
1.49k
  if( pic->ctsValid )
252
1.49k
  {
253
1.49k
    if( m_lastCts )
254
0
    {
255
0
      int64_t ticksPerFrame = m_ticksPerFrameMul4/4;
256
0
      int64_t expectedCtsDiff = (m_pcEncCfg->m_TicksPerSecond > 0 ) ? ticksPerFrame : 1;
257
0
      int64_t ctsDiff = pic->cts - m_lastCts;
258
259
0
      if( ctsDiff >= (expectedCtsDiff<<1) || ctsDiff < 0 )
260
0
      {
261
        // signalize that frames are missing at that particular picture
262
0
        pic->picOutOffset = (m_pcEncCfg->m_TicksPerSecond > 0 ) ? (ctsDiff - ticksPerFrame)/ticksPerFrame : ctsDiff-1;
263
0
        m_numPicsInMissing += pic->picOutOffset;
264
0
      }
265
0
    }
266
1.49k
    m_lastCts = pic->cts;
267
1.49k
  }
268
1.49k
  if( m_numPicsInMissing )
269
0
  {
270
0
    pic->picsInMissing = m_numPicsInMissing;
271
0
  }
272
273
1.49k
  pic->setSccFlags( m_pcEncCfg );
274
275
1.49k
  CHECK( m_ppsMap.getFirstPS() == nullptr || m_spsMap.getPS( m_ppsMap.getFirstPS()->spsId ) == nullptr, "picture set not initialised" );
276
277
1.49k
  const PPS& pps = *( m_ppsMap.getFirstPS() );
278
1.49k
  const SPS& sps = *( m_spsMap.getPS( pps.spsId ) );
279
280
1.49k
  if( pic->cs && pic->cs->picHeader )
281
0
  {
282
0
    delete pic->cs->picHeader;
283
0
    pic->cs->picHeader = nullptr;
284
0
  }
285
286
1.49k
  std::mutex* mutex = ( m_pcEncCfg->m_maxParallelFrames ) ? &m_unitCacheMutex : nullptr;
287
1.49k
  pic->finalInit( m_VPS, sps, pps, nullptr, m_shrdUnitCache, mutex, nullptr );
288
289
1.49k
  pic->vps = &m_VPS;
290
1.49k
  pic->dci = &m_DCI;
291
292
  // filter data initialization
293
1.49k
  const uint32_t numberOfCtusInFrame = pic->cs->pcv->sizeInCtus;
294
295
1.49k
  if( m_pcEncCfg->m_usePerceptQPA )
296
1.49k
  {
297
1.49k
    pic->ctuQpaLambda.resize (numberOfCtusInFrame);
298
1.49k
    pic->ctuAdaptedQP.resize (numberOfCtusInFrame);
299
1.49k
  }
300
301
1.49k
  if( pic->cs->sps->saoEnabled )
302
1.49k
  {
303
1.49k
    pic->resizeSAO( numberOfCtusInFrame, 0 );
304
1.49k
    pic->resizeSAO( numberOfCtusInFrame, 1 );
305
1.49k
  }
306
307
1.49k
  if( pic->cs->sps->alfEnabled )
308
1.49k
  {
309
1.49k
    pic->resizeAlfCtuBuffers( numberOfCtusInFrame );
310
1.49k
  }
311
312
1.49k
  pic->encTime.stopTimer();
313
1.49k
}
314
315
void EncGOP::waitForFreeEncoders()
316
1.49k
{
317
1.49k
  {
318
1.49k
    std::unique_lock<std::mutex> lock( m_gopEncMutex );
319
1.49k
    if( ! xEncodersFinished() )
320
1.49k
    {
321
1.49k
      CHECK( m_pcEncCfg->m_numThreads <= 0, "run into MT code, but no threading enabled" );
322
1.49k
      m_gopEncCond.wait( lock );
323
1.49k
    }
324
1.49k
  }
325
1.49k
}
326
327
void EncGOP::processPictures( const PicList& picList, AccessUnitList& auList, PicList& doneList, PicList& freeList )
328
2.98k
{
329
2.98k
  CHECK( picList.empty(), "empty input picture list given" );
330
331
  // create list of pictures ordered in coding order and ready to be encoded
332
2.98k
  xInitPicsInCodingOrder( picList );
333
334
  // encode pictures
335
2.98k
  xProcessPictures( auList, doneList );
336
337
  // output reconstructed YUV
338
2.98k
  xOutputRecYuv( picList );
339
340
  // release pictures not needed anymore
341
2.98k
  xReleasePictures( picList, freeList );
342
343
  // clear output access unit
344
2.98k
  if( m_isPreAnalysis )
345
0
  {
346
0
    auList.clearAu();
347
0
  }
348
2.98k
}
349
350
void EncGOP::xProcessPictures( AccessUnitList& auList, PicList& doneList )
351
2.98k
{
352
  // in lockstep mode, process all pictures in processing list
353
2.98k
  const bool lockStepMode = (m_pcEncCfg->m_RCTargetBitrate > 0 || (m_pcEncCfg->m_LookAhead > 0 && !m_isPreAnalysis)) && (m_pcEncCfg->m_maxParallelFrames > 0);
354
355
  // get list of pictures to be encoded and used for RC update
356
2.98k
  CHECK( m_pcEncCfg->m_rateCap && lockStepMode, "Rate capping should not be used in lockstep mode" );
357
  // rate cap and MT: finish the previous GOP before processing the next one
358
2.98k
  const bool rateCapPrevGopConstr = m_pcEncCfg->m_rateCap && !m_rcUpdateList.empty();
359
360
2.98k
  if( m_procList.empty() && (!m_gopEncListInput.empty() || !m_rcInputReorderList.empty()) && !rateCapPrevGopConstr )
361
1.49k
  {
362
1.49k
    xGetProcessingLists( m_procList, m_rcUpdateList, lockStepMode );
363
1.49k
  }
364
365
2.98k
  if( ! m_procList.empty() )
366
1.49k
  {
367
    // encode one picture in serial mode / multiple pictures in FPP mode
368
1.49k
    PROFILER_ACCUM_AND_START_NEW_SET( 1, g_timeProfiler, P_IGNORE );
369
2.98k
    while( true )
370
2.98k
    {
371
2.98k
      Picture* pic           = nullptr;
372
2.98k
      EncPicture* picEncoder = nullptr;
373
374
      // fetch next picture to be encoded and next free picture encoder
375
2.98k
      {
376
2.98k
        std::unique_lock<std::mutex> lock( m_gopEncMutex, std::defer_lock );
377
2.98k
        if( m_pcEncCfg->m_numThreads > 0) lock.lock();
378
379
        // leave the loop when nothing to do (when all encoders are finished or in non-blocking mode)
380
2.98k
        if( m_procList.empty() && ( isNonBlocking() || xEncodersFinished() ) )
381
1.49k
        {
382
1.49k
          break;
383
1.49k
        }
384
385
        // get next picture ready to be encoded
386
        // if ALF enabled and ALFTempPred is used, ensure that refAps is initialized
387
        // rate capping and MT frame parallel: in the first GOP after scene cut, ensure that the two first frames of 
388
        //                                     this GOP are finished. Their data will be used to adjust the QP of
389
        //                                     remaining frames of this scene-cut-GOP.
390
1.49k
        const std::list<Picture *>* rcUpdateList = &m_rcUpdateList;
391
1.49k
        const VVEncCfg* encCfg = m_pcEncCfg;
392
1.49k
        auto picItr            = find_if( m_procList.begin(), m_procList.end(), [encCfg, rcUpdateList]( auto pic ) {
393
1.49k
          return ( encCfg->m_ifp || pic->slices[ 0 ]->checkAllRefPicsReconstructed() )
394
1.49k
            && ( !encCfg->m_alf || ( !pic->refApsGlobal || pic->refApsGlobal->initalized ) )
395
1.49k
            && ( !encCfg->m_rateCap || !encCfg->m_maxParallelFrames || !pic->isSceneCutGOP || (!rcUpdateList->front()->isSceneCutCheckAdjQP && !rcUpdateList->front()->gopEntry->m_isStartOfGop ) )
396
1.49k
            ; } );
397
398
1.49k
        const bool nextPicReady = picItr != m_procList.end();
399
400
        // check at least one picture and one pic encoder ready
401
1.49k
        if( m_freePicEncoderList.empty() || ! nextPicReady )
402
0
        {
403
          // non-blocking stage: wait on top level, let other stages do their jobs
404
          // in non-lockstep mode, check if next picture can be output
405
0
          if( isNonBlocking() || ( ! lockStepMode && m_gopEncListOutput.front()->isReconstructed ) )
406
0
          {
407
0
            break;
408
0
          }
409
0
          CHECK( m_pcEncCfg->m_numThreads <= 0, "run into MT code, but no threading enabled" );
410
0
          CHECK( xEncodersFinished(), "wait for picture to be finished, but no pic encoder running" );
411
0
          m_gopEncCond.wait( lock );
412
0
          continue;
413
0
        }
414
415
1.49k
        pic = *picItr;
416
1.49k
        picEncoder = m_freePicEncoderList.front();
417
418
        // rate-control with look-ahead: init next chunk
419
1.49k
        if( m_pcEncCfg->m_RCTargetBitrate > 0 && m_pcEncCfg->m_LookAhead )
420
0
        {
421
0
          CHECK( m_isPreAnalysis, "rate control enabled for pre analysis" );
422
423
0
          if( pic->isFlush )
424
0
          {
425
0
            m_pcRateCtrl->setRCRateSavingState(0); // tell budget estimation that end of video is near
426
0
          }
427
0
          if( pic->gopEntry->m_isStartOfGop )
428
0
          {
429
            // check the RC final pass requirement for availability of preprocessed pictures (GOP + 1)
430
0
            if( m_pcRateCtrl->lastPOCInCache() <= pic->poc && ! pic->isFlush )
431
0
            {
432
0
              break;
433
0
            }
434
0
            m_pcRateCtrl->processFirstPassData( pic->isFlush, pic->poc );
435
0
          }
436
0
        }
437
438
1.49k
        m_freePicEncoderList.pop_front();
439
1.49k
      }
440
441
1.49k
      CHECK( picEncoder == nullptr, "no free picture encoder available" );
442
1.49k
      CHECK( pic        == nullptr, "no picture to be encoded, ready for encoding" );
443
1.49k
      m_procList.remove( pic );
444
445
1.49k
      xEncodePicture( pic, picEncoder );
446
1.49k
    }
447
1.49k
  }
448
449
2.98k
  if( lockStepMode && m_pcEncCfg->m_ifpLines && !m_rcUpdateList.empty() )
450
0
  {
451
0
    xUpdateRcIfp();
452
0
  }
453
454
2.98k
  if( m_pcEncCfg->m_rateCap )
455
0
  {
456
0
    xUpdateRateCap();
457
0
  }
458
459
  // picture/AU output
460
  // 
461
  // in lock-step mode:
462
  // the output of a picture is connected to evaluation of the lock-step-chunk
463
  // if the next picture to output belongs to the current chunk, do output (evaluation) when all pictures of the chunk are finished
464
465
2.98k
  if( m_gopEncListOutput.empty() || !m_gopEncListOutput.front()->isReconstructed ||
466
1.49k
    ( lockStepMode && !m_pcEncCfg->m_ifpLines && !m_rcUpdateList.empty() && m_gopEncListOutput.front() == m_rcUpdateList.front() && !xLockStepPicsFinished() ) )
467
1.49k
  {
468
1.49k
    return;
469
1.49k
  }
470
1.49k
  PROFILER_ACCUM_AND_START_NEW_SET( 1, g_timeProfiler, P_TOP_LEVEL );
471
472
  // AU output
473
1.49k
  Picture* outPic = m_gopEncListOutput.front();
474
1.49k
  m_gopEncListOutput.pop_front();
475
476
1.49k
  xWritePicture( *outPic, auList, false );
477
478
  // update pending RC
479
  // first pic has been written to bitstream
480
  // therefore we have at least for this picture a valid total bit and head bit count
481
1.49k
  if( !m_rcUpdateList.empty() && m_rcUpdateList.front() == outPic && (!lockStepMode || !m_pcEncCfg->m_ifpLines)  )
482
0
  {
483
0
    if( m_pcEncCfg->m_RCTargetBitrate > 0 )
484
0
    {
485
0
      for( auto pic : m_rcUpdateList )
486
0
      {
487
0
        if( pic != outPic )
488
0
        {
489
0
          pic->actualHeadBits  = outPic->actualHeadBits;
490
0
          pic->actualTotalBits = pic->sliceDataStreams[0].getNumberOfWrittenBits();
491
0
        }
492
0
        m_pcRateCtrl->updateAfterPicEncRC( pic );
493
0
      }
494
0
    }
495
496
0
    if( lockStepMode )
497
0
      m_rcUpdateList.clear();
498
0
    else
499
0
      m_rcUpdateList.pop_front();
500
0
  }
501
502
1.49k
  const bool skipFirstPass = ( ! m_pcRateCtrl->rcIsFinalPass || m_isPreAnalysis ) && outPic->gopEntry->m_skipFirstPass;
503
1.49k
  if( m_pcEncCfg->m_useAMaxBT && ! skipFirstPass )
504
0
  {
505
0
    m_BlkStat.updateMaxBT( *outPic->slices[0], outPic->picBlkStat );
506
0
  }
507
508
1.49k
  outPic->slices[ 0 ]->updateRefPicCounter( -1 );
509
1.49k
  outPic->isFinished = true;
510
511
1.49k
  if( ! m_isPreAnalysis )
512
1.49k
  {
513
    // a film grain analysis may still read the filtered picture
514
1.49k
    for( auto& task : m_fgaTasks )
515
0
    {
516
0
      if( task->pic == outPic )
517
0
      {
518
0
        task->counter.wait();
519
0
      }
520
0
    }
521
1.49k
    outPic->getFilteredOrigBuffer().destroy();
522
1.49k
  }
523
524
1.49k
  doneList.push_back( outPic );
525
526
1.49k
  m_numPicsCoded += 1;
527
1.49k
}
528
529
void EncGOP::xSyncAlfAps( Picture& pic )
530
1.49k
{
531
1.49k
  Slice& slice = *pic.cs->slice;
532
1.49k
  const bool mtPicParallel = m_pcEncCfg->m_numThreads > 0;
533
534
1.49k
  if( mtPicParallel && slice.isIntra() )
535
1.49k
  {
536
    // reset APS propagation on Intra-Slice in MT-mode
537
1.49k
    return;
538
1.49k
  }
539
540
0
  const PicApsGlobal* refAps = pic.refApsGlobal;
541
0
  if( !refAps )
542
0
    return;
543
0
  CHECK( !refAps->initalized, "Attempt referencing from an uninitialized APS" );
544
0
  pic.refApsGlobal->refCnt--;
545
0
  CHECK( pic.refApsGlobal->refCnt < 0, "Not expected APS ref. counter\n" );
546
547
  // copy ref APSs to current picture
548
0
  const ParameterSetMap<APS>& src = refAps->apsMap;
549
0
  ParameterSetMap<APS>&       dst = pic.picApsMap;
550
0
  if( mtPicParallel && pic.TLayer == 0 )
551
0
  {
552
    // in pic.parallel case, due to limited number of APS IDs, limit propagation of TID-0 APS
553
0
    CHECK( slice.sps->maxTLayers > ALF_CTB_MAX_NUM_APS, "Not enough space for ALF APSs in MT mode: not supported"  )
554
0
    int numApsTID0 = ALF_CTB_MAX_NUM_APS - (int)slice.sps->maxTLayers;
555
0
    int lastTakenApsPOC = pic.poc;
556
0
    while( numApsTID0 > 0 )
557
0
    {
558
0
      const APS* candAPS = nullptr;
559
0
      int candMapIdx = 0;
560
0
      for( int i = 0; i < ALF_CTB_MAX_NUM_APS; i++ )
561
0
      {
562
0
        const int mapIdx = ( i << NUM_APS_TYPE_LEN ) + ALF_APS;
563
0
        const APS* srcAPS = src.getPS( mapIdx );
564
0
        if( srcAPS && srcAPS->apsId != MAX_UINT && srcAPS->poc < lastTakenApsPOC && ( !candAPS || srcAPS->poc > candAPS->poc ) )
565
0
        {
566
0
          candAPS = srcAPS;
567
0
          candMapIdx = mapIdx;
568
0
        }
569
0
      }
570
0
      if( !candAPS )
571
0
        break;
572
573
0
      APS* dstAPS = dst.allocatePS( candMapIdx );
574
0
      *dstAPS = *candAPS;
575
0
      dst.clearChangedFlag( candMapIdx );
576
0
      lastTakenApsPOC = candAPS->poc;
577
0
      numApsTID0--;
578
0
    }
579
0
  }
580
0
  else
581
0
  {
582
0
    for( int i = 0; i < ALF_CTB_MAX_NUM_APS; i++ )
583
0
    {
584
0
      const int apsMapIdx = ( i << NUM_APS_TYPE_LEN ) + ALF_APS;
585
0
      const APS* srcAPS = src.getPS( apsMapIdx );
586
0
      if( srcAPS )
587
0
      {
588
0
        APS* dstAPS = dst.allocatePS( apsMapIdx );
589
0
        *dstAPS = *srcAPS;
590
0
        dst.clearChangedFlag( apsMapIdx );
591
0
      }
592
0
    }
593
0
  }
594
0
  dst.setApsIdStart( src.getApsIdStart() );
595
0
}
596
597
void EncGOP::xEncodePicture( Picture* pic, EncPicture* picEncoder )
598
1.49k
{
599
  // first pass temporal down-sampling
600
1.49k
  if( ( ! m_pcRateCtrl->rcIsFinalPass || m_isPreAnalysis ) && pic->gopEntry->m_skipFirstPass )
601
0
  {
602
0
    pic->isReconstructed = true;
603
0
    m_freePicEncoderList.push_back( picEncoder );
604
0
    return;
605
0
  }
606
607
  // decoder in encoder
608
1.49k
  DTRACE_UPDATE( g_trace_ctx, std::make_pair( "finalpass", m_pcRateCtrl->rcIsFinalPass ? 1: 0 ) );
609
610
1.49k
  if( m_pcEncCfg->m_alf && m_pcEncCfg->m_alfTempPred )
611
1.49k
  {
612
    // Establish reference APS for current picture
613
1.49k
    xSyncAlfAps( *pic );
614
1.49k
  }
615
616
  // initialize next picture
617
1.49k
  pic->isPreAnalysis = m_isPreAnalysis;
618
619
1.49k
  if( pic->slices[0]->TLayer + 1 < m_pcEncCfg->m_maxTLayer ) // skip for highest two temporal levels
620
1.49k
  {
621
1.49k
    initPicAuxQPOffsets( pic->slices[0], m_pcEncCfg->hasBlockQpOffsets() );
622
1.49k
  }
623
624
1.49k
  if( m_pcEncCfg->m_RCTargetBitrate > 0 )
625
0
  {
626
0
    pic->picInitialQP     = -1;
627
0
    pic->picInitialLambda = -1.0;
628
629
0
    m_pcRateCtrl->initRateControlPic( *pic, pic->slices[0], pic->picInitialQP, pic->picInitialLambda );
630
0
  }
631
632
1.49k
  if( pic->isSceneCutGOP && !pic->isSceneCutCheckAdjQP && !pic->gopEntry->m_isStartOfGop && m_rcap.gopAdaptedQPAdj )
633
0
  {
634
0
    pic->gopAdaptedQP += m_rcap.gopAdaptedQPAdj;
635
0
  }
636
637
  // compress next picture
638
1.49k
  picEncoder->compressPicture( *pic, *this );
639
640
  // finish picture encoding and cleanup
641
1.49k
  if( m_pcEncCfg->m_numThreads > 0 )
642
1.49k
  {
643
1.49k
    static auto finishTask = []( int, void* task_param )
644
1.49k
    {
645
1.49k
      FinishTaskParam* param = static_cast<FinishTaskParam*>( task_param );
646
1.49k
      param->picEncoder->finalizePicture( *param->pic );
647
1.49k
      {
648
1.49k
        std::lock_guard<std::mutex> lock( param->gopEncoder->m_gopEncMutex );
649
1.49k
        param->pic->isReconstructed = true;
650
1.49k
        if( param->pic->picApsGlobal )
651
1.49k
          param->pic->picApsGlobal->initalized = true;
652
1.49k
        param->gopEncoder->m_freePicEncoderList.push_back( param->picEncoder );
653
1.49k
        param->gopEncoder->m_gopEncCond.notify_one();
654
1.49k
      }
655
1.49k
      delete param;
656
1.49k
      return true;
657
1.49k
    };
658
1.49k
    FinishTaskParam* param = new FinishTaskParam( this, picEncoder, pic );
659
1.49k
    m_threadPool->addBarrierTask( finishTask, param, nullptr, nullptr, { &picEncoder->m_ctuTasksDoneCounter.done } );
660
1.49k
  }
661
0
  else
662
0
  {
663
0
    picEncoder->finalizePicture( *pic );
664
0
    pic->isReconstructed = true;
665
0
    if( pic->picApsGlobal ) pic->picApsGlobal->initalized = true;
666
0
    m_freePicEncoderList.push_back( picEncoder );
667
0
  }
668
1.49k
}
669
670
void EncGOP::xOutputRecYuv( const PicList& picList )
671
2.98k
{
672
2.98k
  if( m_pcRateCtrl->rcIsFinalPass && m_recYuvBufFunc )
673
0
  {
674
0
    CHECK( m_isPreAnalysis, "yuv output enabled for pre analysis" );
675
    // ordered YUV output
676
0
    bool bRun = true;
677
0
    while( bRun )
678
0
    {
679
0
      bRun = false;
680
0
      for( auto pic : picList )
681
0
      {
682
0
        if( pic->poc != m_pocRecOut )
683
0
          continue;
684
0
        if( ! pic->isReconstructed )
685
0
          return;
686
687
0
        const PPS& pps = *(pic->cs->pps);
688
0
        vvencYUVBuffer yuvBuffer;
689
0
        vvenc_YUVBuffer_default( &yuvBuffer );
690
0
        setupYuvBuffer( pic->getRecoBuf(), yuvBuffer, &pps.conformanceWindow );
691
0
        yuvBuffer.sequenceNumber = pic->poc;
692
0
        m_recYuvBufFunc( m_recYuvBufCtx, &yuvBuffer );
693
694
0
        m_pocRecOut += 1;
695
0
        pic->isNeededForOutput = false;
696
0
        bRun = true;
697
0
        break;
698
0
      }
699
0
    }
700
0
  }
701
2.98k
  else
702
2.98k
  {
703
    // no output needed, simply unmark pictures
704
2.98k
    for( auto pic : picList )
705
2.98k
    {
706
2.98k
      if( pic->isReconstructed && pic->isNeededForOutput )
707
1.49k
        pic->isNeededForOutput = false;
708
2.98k
    }
709
2.98k
  }
710
2.98k
}
711
712
void EncGOP::xReleasePictures( const PicList& picList, PicList& freeList )
713
2.98k
{
714
2.98k
  const bool allPicsDone = m_numPicsCoded >= m_picCount && ( picList.empty() || picList.back()->isFlush );
715
  // Writing a picture reads its reconstruction only for the decoded picture hash SEI. Without it, a
716
  // picture that no other picture references and that is not waiting for reconstruction output needs
717
  // neither its reconstruction nor its motion field once it is reconstructed.
718
2.98k
  const bool writeReadsReco = m_pcEncCfg->m_decodedPictureHashSEIType != VVENC_HASHTYPE_NONE;
719
2.98k
  for( auto pic : picList )
720
2.98k
  {
721
2.98k
    const bool isUnused = ! pic->isNeededForOutput && ! pic->isReferenced && pic->refCounter <= 0;
722
2.98k
    if( ( pic->isFinished && isUnused ) || allPicsDone )
723
1.49k
    {
724
      // Nothing reads a released picture any more. Its buffers are allocated again when the
725
      // picture object is reused and its next picture starts encoding.
726
1.49k
      pic->releaseEncodingBuffers();
727
1.49k
      freeList.push_back( pic );
728
1.49k
    }
729
1.49k
    else if( pic->isReconstructed && isUnused && ! writeReadsReco )
730
0
    {
731
0
      pic->releaseEncodingBuffers();
732
0
    }
733
2.98k
  }
734
2.98k
}
735
736
void EncGOP::printOutSummary( const bool printMSEBasedSNR, const bool printSequenceMSE, const bool printHexPsnr )
737
0
{
738
  //--CFG_KDY
739
  //const int rateMultiplier = 1;
740
0
  double fps = m_pcEncCfg->m_FrameRate/(double)m_pcEncCfg->m_FrameScale;
741
0
  m_AnalyzeAll.setFrmRate( fps );
742
0
  m_AnalyzeI.setFrmRate( fps );
743
0
  m_AnalyzeP.setFrmRate( fps );
744
0
  m_AnalyzeB.setFrmRate( fps );
745
746
0
  const ChromaFormat chFmt = m_pcEncCfg->m_internChromaFormat;
747
748
0
  const BitDepths& bitDepths = m_spsMap.getFirstPS()->bitDepths;
749
  //-- all
750
0
  std::string summary( "\n" );
751
0
  if( m_pcEncCfg->m_verbosity >= VVENC_DETAILS )
752
0
    summary.append("\nvvenc [info]: SUMMARY --------------------------------------------------------\n");
753
754
0
  summary.append( m_AnalyzeAll.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths));
755
756
0
  if( m_pcEncCfg->m_verbosity < VVENC_DETAILS )
757
0
  {
758
0
    msg.log( VVENC_INFO,summary.c_str() );
759
0
  }
760
0
  else
761
0
  {
762
0
    summary.append( "\n\nvvenc [info]: I Slices--------------------------------------------------------\n" );
763
0
    summary.append( m_AnalyzeI.printOut('i', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths));
764
765
0
    summary.append( "\n\nvvenc [info]: P Slices--------------------------------------------------------\n" );
766
0
    summary.append( m_AnalyzeP.printOut('p', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths));
767
768
0
    summary.append( "\n\nvvenc [info]: B Slices--------------------------------------------------------\n" );
769
0
    summary.append( m_AnalyzeB.printOut('b', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths));
770
0
    msg.log( VVENC_DETAILS,summary.c_str() );
771
0
  }
772
773
0
  if (m_pcEncCfg->m_summaryOutFilename[0] != '\0' )
774
0
  {
775
0
    std::string summaryOutFilename(m_pcEncCfg->m_summaryOutFilename);
776
0
    m_AnalyzeAll.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, summaryOutFilename);
777
0
  }
778
779
0
  if (m_pcEncCfg->m_summaryPicFilenameBase[0] != '\0' )
780
0
  {
781
0
    std::string summaryPicFilenameBase(m_pcEncCfg->m_summaryPicFilenameBase);
782
783
0
    m_AnalyzeI.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, summaryPicFilenameBase+"I.txt");
784
0
    m_AnalyzeP.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, summaryPicFilenameBase+"P.txt");
785
0
    m_AnalyzeB.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, summaryPicFilenameBase+"B.txt");
786
0
  }
787
0
}
788
789
void EncGOP::getParameterSets( AccessUnitList& accessUnit )
790
0
{
791
0
  CHECK( m_ppsMap.getFirstPS() == nullptr || m_spsMap.getPS( m_ppsMap.getFirstPS()->spsId ) == nullptr, "sps/pps not initialised" );
792
793
0
  const PPS& pps = *( m_ppsMap.getFirstPS() );
794
0
  const SPS& sps = *( m_spsMap.getPS( pps.spsId ) );
795
796
0
  if (sps.vpsId != 0)
797
0
  {
798
0
    xWriteVPS( accessUnit, &m_VPS, m_HLSWriter );
799
0
  }
800
0
  xWriteDCI( accessUnit, &m_DCI, m_HLSWriter );
801
0
  xWriteSPS( accessUnit, &sps, m_HLSWriter );
802
0
  xWritePPS( accessUnit, &pps, &sps, m_HLSWriter );
803
0
}
804
805
void EncGOP::xUpdateRasInit( Slice* slice )
806
1.49k
{
807
1.49k
  slice->pendingRasInit = false;
808
1.49k
  if ( slice->poc > m_lastRasPoc )
809
0
  {
810
0
    m_lastRasPoc = MAX_INT;
811
0
    slice->pendingRasInit = true;
812
0
  }
813
1.49k
  if ( slice->isIRAP() )
814
1.49k
  {
815
1.49k
    m_lastRasPoc = slice->poc;
816
1.49k
  }
817
1.49k
}
818
819
void EncGOP::xInitVPS(VPS &vps) const
820
1.49k
{
821
  // The SPS must have already been set up.
822
  // set the VPS profile information.
823
1.49k
  vps.maxLayers                   = 1;
824
1.49k
  vps.maxSubLayers                = 1;
825
1.49k
  vps.vpsId                       = 0;
826
1.49k
  vps.allLayersSameNumSubLayers   = true;
827
1.49k
  vps.allIndependentLayers        = true;
828
1.49k
  vps.eachLayerIsAnOls            = true;
829
1.49k
  vps.olsModeIdc                  = 0;
830
1.49k
  vps.numOutputLayerSets          = 1;
831
1.49k
  vps.numPtls                     = 1;
832
1.49k
  vps.extension                   = false;
833
1.49k
  vps.totalNumOLSs                = 0;
834
1.49k
  vps.numDpbParams                = 0;
835
1.49k
  vps.sublayerDpbParamsPresent    = false;
836
1.49k
  vps.targetOlsIdx                = -1;
837
838
97.0k
  for (int i = 0; i < MAX_VPS_LAYERS; i++)
839
95.5k
  {
840
95.5k
    vps.layerId[i]                = 0;
841
95.5k
    vps.independentLayer[i]       = true;
842
6.21M
    for (int j = 0; j < MAX_VPS_LAYERS; j++)
843
6.11M
    {
844
6.11M
      vps.directRefLayer[i][j]    = 0;
845
6.11M
      vps.directRefLayerIdx[i][j] = MAX_VPS_LAYERS;
846
6.11M
      vps.interLayerRefIdx[i][i]  = NOT_VALID;
847
6.11M
    }
848
95.5k
  }
849
850
383k
  for (int i = 0; i < MAX_NUM_OLSS; i++)
851
382k
  {
852
24.8M
    for (int j = 0; j < MAX_VPS_LAYERS; j++)
853
24.4M
    {
854
24.4M
      vps.olsOutputLayer[i][j]    = 0;
855
24.4M
    }
856
382k
    vps.ptPresent[i]              = (i == 0) ? 1 : 0;
857
382k
    vps.ptlMaxTemporalId[i]       = vps.maxSubLayers - 1;
858
382k
    vps.olsPtlIdx[i]              = 0;
859
382k
  }
860
861
1.49k
  vps.profileTierLevel.resize( 1 );
862
1.49k
}
863
864
void EncGOP::xInitDCI(DCI &dci, const SPS &sps, const int dciId) const
865
1.49k
{
866
  // The SPS must have already been set up.
867
  // set the DPS profile information.
868
1.49k
  dci.dciId                 = dciId;
869
870
1.49k
  dci.profileTierLevel.resize(1);
871
  // copy profile level tier info
872
1.49k
  dci.profileTierLevel[0]   = sps.profileTierLevel;
873
1.49k
}
874
875
void EncGOP::xInitConstraintInfo(ConstraintInfo &ci) const
876
1.49k
{
877
1.49k
  ci.intraOnlyConstraintFlag                      = m_pcEncCfg->m_intraOnlyConstraintFlag;
878
1.49k
  ci.maxBitDepthConstraintIdc                     = m_pcEncCfg->m_bitDepthConstraintValue - 8;
879
1.49k
  ci.maxChromaFormatConstraintIdc                 = m_pcEncCfg->m_internChromaFormat;
880
1.49k
  ci.onePictureOnlyConstraintFlag                 = false;
881
1.49k
  ci.lowerBitRateConstraintFlag                   = false;
882
1.49k
  ci.allLayersIndependentConstraintFlag           = false;
883
1.49k
  ci.noQtbttDualTreeIntraConstraintFlag           = ! m_pcEncCfg->m_dualITree;
884
1.49k
  ci.noPartitionConstraintsOverrideConstraintFlag = false;
885
1.49k
  ci.noSaoConstraintFlag                          = ! m_pcEncCfg->m_bUseSAO;
886
1.49k
  ci.noAlfConstraintFlag                          = ! m_pcEncCfg->m_alf;
887
1.49k
  ci.noCCAlfConstraintFlag                        = ! m_pcEncCfg->m_ccalf;
888
1.49k
  ci.noRefWraparoundConstraintFlag                = false;
889
1.49k
  ci.noTemporalMvpConstraintFlag                  = m_pcEncCfg->m_TMVPModeId == 0;
890
1.49k
  ci.noSbtmvpConstraintFlag                       = !m_pcEncCfg->m_SbTMVP;
891
1.49k
  ci.noAmvrConstraintFlag                         = false;
892
1.49k
  ci.noBdofConstraintFlag                         = ! m_pcEncCfg->m_BDOF;
893
1.49k
  ci.noDmvrConstraintFlag                         = ! m_pcEncCfg->m_DMVR;
894
1.49k
  ci.noCclmConstraintFlag                         = ! m_pcEncCfg->m_LMChroma;
895
1.49k
  ci.noMtsConstraintFlag                          = !(m_pcEncCfg->m_MTSImplicit || m_pcEncCfg->m_MTS);
896
1.49k
  ci.noSbtConstraintFlag                          = m_pcEncCfg->m_SBT == 0;
897
1.49k
  ci.noAffineMotionConstraintFlag                 = ! m_pcEncCfg->m_Affine;
898
1.49k
  ci.noBcwConstraintFlag                          = true;
899
1.49k
  ci.noIbcConstraintFlag                          = m_pcEncCfg->m_IBCMode == 0;
900
1.49k
  ci.noCiipConstraintFlag                         = m_pcEncCfg->m_CIIP == 0;
901
1.49k
  ci.noGeoConstraintFlag                          = m_pcEncCfg->m_Geo == 0;
902
1.49k
  ci.noLadfConstraintFlag                         = true;
903
1.49k
  ci.noTransformSkipConstraintFlag                = m_pcEncCfg->m_TS == 0;
904
1.49k
  ci.noBDPCMConstraintFlag                        = m_pcEncCfg->m_useBDPCM==0;
905
1.49k
  ci.noJointCbCrConstraintFlag                    = ! m_pcEncCfg->m_JointCbCrMode;
906
1.49k
  ci.noMrlConstraintFlag                          = ! m_pcEncCfg->m_MRL;
907
1.49k
  ci.noIspConstraintFlag                          = true;
908
1.49k
  ci.noMipConstraintFlag                          = ! m_pcEncCfg->m_MIP;
909
1.49k
  ci.noQpDeltaConstraintFlag                      = false;
910
1.49k
  ci.noDepQuantConstraintFlag                     = ! m_pcEncCfg->m_DepQuantEnabled;
911
1.49k
  ci.noMixedNaluTypesInPicConstraintFlag          = false;
912
1.49k
  ci.noSignDataHidingConstraintFlag               = ! m_pcEncCfg->m_SignDataHidingEnabled;
913
1.49k
  ci.noLfnstConstraintFlag                        = ! m_pcEncCfg->m_LFNST;
914
1.49k
  ci.noMmvdConstraintFlag                         = ! m_pcEncCfg->m_MMVD;
915
1.49k
  ci.noSmvdConstraintFlag                         = ! m_pcEncCfg->m_SMVD;
916
1.49k
  ci.noProfConstraintFlag                         = ! m_pcEncCfg->m_PROF;
917
1.49k
  ci.noPaletteConstraintFlag                      = true;
918
1.49k
  ci.noActConstraintFlag                          = true;
919
1.49k
  ci.noLmcsConstraintFlag                         = true;
920
1.49k
  ci.noTrailConstraintFlag                        = m_pcEncCfg->m_IntraPeriod == 1;
921
1.49k
  ci.noStsaConstraintFlag                         = m_pcEncCfg->m_IntraPeriod == 1 || ! m_gopCfg->hasNonZeroTemporalId();
922
1.49k
  ci.noRaslConstraintFlag                         = m_pcEncCfg->m_IntraPeriod == 1 || ! m_gopCfg->hasLeadingPictures();
923
1.49k
  ci.noRadlConstraintFlag                         = m_pcEncCfg->m_IntraPeriod == 1 || ! m_gopCfg->hasLeadingPictures();
924
1.49k
  ci.noIdrConstraintFlag                          = false;
925
1.49k
  ci.noCraConstraintFlag                          = (m_pcEncCfg->m_DecodingRefreshType != VVENC_DRT_CRA && m_pcEncCfg->m_DecodingRefreshType != VVENC_DRT_CRA_CRE);
926
1.49k
  ci.noGdrConstraintFlag                          = false;
927
1.49k
  ci.noApsConstraintFlag                          = !m_pcEncCfg->m_alf;
928
1.49k
}
929
930
void EncGOP::xInitSPS(SPS &sps) const
931
1.49k
{
932
1.49k
  ProfileTierLevel* profileTierLevel = &sps.profileTierLevel;
933
934
1.49k
  xInitConstraintInfo( profileTierLevel->constraintInfo );
935
936
1.49k
  profileTierLevel->levelIdc      = m_pcEncCfg->m_level;
937
1.49k
  profileTierLevel->tierFlag      = m_pcEncCfg->m_levelTier;
938
1.49k
  profileTierLevel->profileIdc    = m_pcEncCfg->m_profile;
939
1.49k
  profileTierLevel->subProfileIdc.clear();
940
1.49k
  profileTierLevel->subProfileIdc.push_back( m_pcEncCfg->m_subProfile );
941
942
1.49k
  if( m_pcEncCfg->m_maxPicWidth != 0 && m_pcEncCfg->m_maxPicHeight != 0 )
943
0
  {
944
0
    const int minCuSize = std::max( 1 << ( vvenc::MIN_CU_LOG2 + 1 ), 1 << m_pcEncCfg->m_log2MinCodingBlockSize );
945
0
    int padRight = 0, padBottom = 0;
946
0
    if( m_pcEncCfg->m_maxPicWidth % minCuSize )
947
0
    {
948
0
      padRight = ( ( m_pcEncCfg->m_maxPicWidth / minCuSize) + 1 ) * minCuSize - m_pcEncCfg->m_maxPicWidth;
949
0
    }
950
0
    if( m_pcEncCfg->m_maxPicHeight % minCuSize )
951
0
    {
952
0
      padBottom = ( ( m_pcEncCfg->m_maxPicHeight / minCuSize) + 1 ) * minCuSize - m_pcEncCfg->m_maxPicHeight;
953
0
    }
954
0
    sps.maxPicWidthInLumaSamples      = m_pcEncCfg->m_maxPicWidth + padRight;
955
0
    sps.maxPicHeightInLumaSamples     = m_pcEncCfg->m_maxPicHeight + padBottom;
956
    
957
0
    sps.conformanceWindow.setWindow( 0, padRight, 0, padBottom );
958
0
  }
959
1.49k
  else
960
1.49k
  {
961
1.49k
    sps.maxPicWidthInLumaSamples      = m_pcEncCfg->m_PadSourceWidth;
962
1.49k
    sps.maxPicHeightInLumaSamples     = m_pcEncCfg->m_PadSourceHeight;
963
1.49k
    sps.conformanceWindow.setWindow( m_pcEncCfg->m_confWinLeft, m_pcEncCfg->m_confWinRight, m_pcEncCfg->m_confWinTop, m_pcEncCfg->m_confWinBottom );
964
1.49k
  }
965
1.49k
  sps.chromaFormatIdc               = m_pcEncCfg->m_internChromaFormat;
966
1.49k
  sps.CTUSize                       = m_pcEncCfg->m_CTUSize;
967
1.49k
  sps.maxMTTDepth[0]                = m_pcEncCfg->m_maxMTTDepthI;
968
1.49k
  int maxMTTDepthVal = m_pcEncCfg->m_maxMTTDepth;
969
1.49k
  int minMaxMttD = maxMTTDepthVal % 10;
970
2.98k
  while( maxMTTDepthVal )
971
1.49k
  {
972
1.49k
    minMaxMttD      = std::min( minMaxMttD, maxMTTDepthVal % 10 );
973
1.49k
    maxMTTDepthVal /= 10;
974
1.49k
  }
975
1.49k
  sps.maxMTTDepth[1]                = minMaxMttD;
976
1.49k
  sps.maxMTTDepth[2]                = m_pcEncCfg->m_maxMTTDepthIChroma;
977
5.97k
  for( int i = 0; i < 3; i++)
978
4.47k
  {
979
4.47k
    sps.minQTSize[i]                = m_pcEncCfg->m_MinQT[i];
980
4.47k
    sps.maxBTSize[i]                = m_pcEncCfg->m_maxBT[i];
981
4.47k
    sps.maxTTSize[i]                = m_pcEncCfg->m_maxTT[i];
982
4.47k
  }
983
1.49k
  sps.minQTSize[2]                <<= getChannelTypeScaleX(CH_C, m_pcEncCfg->m_internChromaFormat);
984
985
1.49k
  sps.maxNumMergeCand               = m_pcEncCfg->m_maxNumMergeCand;
986
1.49k
  sps.maxNumAffineMergeCand         = !!m_pcEncCfg->m_Affine ? m_pcEncCfg->m_maxNumAffineMergeCand : 0;
987
1.49k
  sps.maxNumGeoCand                 = !!m_pcEncCfg->m_Geo    ? m_pcEncCfg->m_maxNumGeoCand : 0;
988
1.49k
  sps.IBC                           = m_pcEncCfg->m_IBCMode != 0;
989
1.49k
  sps.maxNumIBCMergeCand            = 6;
990
991
1.49k
  sps.idrRefParamList               = m_pcEncCfg->m_idrRefParamList;
992
1.49k
  sps.dualITree                     = m_pcEncCfg->m_dualITree && m_pcEncCfg->m_internChromaFormat != VVENC_CHROMA_400;
993
1.49k
  sps.MTS                           = m_pcEncCfg->m_MTS || m_pcEncCfg->m_MTSImplicit;
994
1.49k
  sps.SMVD                          = m_pcEncCfg->m_SMVD;
995
1.49k
  sps.AMVR                          = m_pcEncCfg->m_AMVRspeed != IMV_OFF;
996
1.49k
  sps.LMChroma                      = m_pcEncCfg->m_LMChroma;
997
1.49k
  sps.horCollocatedChroma           = m_pcEncCfg->m_horCollocatedChromaFlag;
998
1.49k
  sps.verCollocatedChroma           = m_pcEncCfg->m_verCollocatedChromaFlag;
999
1.49k
  sps.BDOF                          = m_pcEncCfg->m_BDOF;
1000
1.49k
  sps.DMVR                          = m_pcEncCfg->m_DMVR;
1001
1.49k
  sps.Affine                        = m_pcEncCfg->m_Affine;
1002
1.49k
  sps.PROF                          = m_pcEncCfg->m_PROF;
1003
1.49k
  sps.ProfPresent                   = m_pcEncCfg->m_PROF;
1004
1.49k
  sps.AffineType                    = m_pcEncCfg->m_AffineType;
1005
1.49k
  sps.MMVD                          = m_pcEncCfg->m_MMVD != 0;
1006
1.49k
  sps.fpelMmvd                      = m_pcEncCfg->m_allowDisFracMMVD;
1007
1.49k
  sps.GEO                           = m_pcEncCfg->m_Geo != 0;
1008
1.49k
  sps.MIP                           = m_pcEncCfg->m_MIP;
1009
1.49k
  sps.MRL                           = m_pcEncCfg->m_MRL;
1010
1.49k
  sps.BdofPresent                   = m_pcEncCfg->m_BDOF;
1011
1.49k
  sps.DmvrPresent                   = m_pcEncCfg->m_DMVR;
1012
1.49k
  sps.partitionOverrideEnabled      = true; // needed for the new MaxMTTDepth logic
1013
1.49k
  sps.resChangeInClvsEnabled        = m_pcEncCfg->m_resChangeInClvsEnabled;
1014
1.49k
  sps.rprEnabled                    = m_pcEncCfg->m_rprEnabledFlag != 0;
1015
1.49k
  sps.log2MinCodingBlockSize        = m_pcEncCfg->m_log2MinCodingBlockSize;
1016
1.49k
  sps.log2MaxTbSize                 = m_pcEncCfg->m_log2MaxTbSize;
1017
1.49k
  sps.temporalMVPEnabled            = m_pcEncCfg->m_TMVPModeId == 2 || m_pcEncCfg->m_TMVPModeId == 1;
1018
1.49k
  sps.LFNST                         = m_pcEncCfg->m_LFNST != 0;
1019
1.49k
  sps.entropyCodingSyncEnabled      = m_pcEncCfg->m_entropyCodingSyncEnabled;
1020
1.49k
  sps.entryPointsPresent            = m_pcEncCfg->m_entryPointsPresent;
1021
1.49k
  sps.depQuantEnabled               = m_pcEncCfg->m_DepQuantEnabled;
1022
1.49k
  sps.signDataHidingEnabled         = m_pcEncCfg->m_SignDataHidingEnabled;
1023
1.49k
  sps.MTSIntra                      = m_pcEncCfg->m_MTS ;
1024
1.49k
  sps.ISP                           = m_pcEncCfg->m_ISP;
1025
1.49k
  sps.transformSkip                 = m_pcEncCfg->m_TS != 0;
1026
1.49k
  sps.log2MaxTransformSkipBlockSize = m_pcEncCfg->m_TSsize;
1027
1.49k
  sps.BDPCM                         = m_pcEncCfg->m_useBDPCM != 0;
1028
1.49k
  sps.BCW                           = m_pcEncCfg->m_BCW;
1029
1030
4.47k
  for (uint32_t chType = 0; chType < MAX_NUM_CH; chType++)
1031
2.98k
  {
1032
2.98k
    sps.bitDepths.recon[chType]     = m_pcEncCfg->m_internalBitDepth[chType];
1033
2.98k
    sps.qpBDOffset[chType]          = 6 * (m_pcEncCfg->m_internalBitDepth[chType] - 8);
1034
2.98k
    sps.internalMinusInputBitDepth[chType] = std::max(0, (m_pcEncCfg->m_internalBitDepth[chType] - m_pcEncCfg->m_inputBitDepth[chType]));
1035
2.98k
  }
1036
1037
1.49k
  sps.alfEnabled                    = m_pcEncCfg->m_alf;
1038
1.49k
  sps.ccalfEnabled                  = m_pcEncCfg->m_ccalf && sps.alfEnabled && m_pcEncCfg->m_internChromaFormat != VVENC_CHROMA_400;
1039
1040
1.49k
  sps.saoEnabled                    = m_pcEncCfg->m_bUseSAO;
1041
1.49k
  sps.jointCbCr                     = m_pcEncCfg->m_JointCbCrMode;
1042
1.49k
  sps.maxTLayers                    = m_pcEncCfg->m_maxTLayer + 1;
1043
1.49k
  sps.rpl1CopyFromRpl0              = ! m_pcEncCfg->m_picReordering;
1044
1.49k
  sps.SbtMvp                        = m_pcEncCfg->m_SbTMVP;
1045
1.49k
  sps.CIIP                          = m_pcEncCfg->m_CIIP != 0;
1046
1.49k
  sps.SBT                           = m_pcEncCfg->m_SBT != 0;
1047
1048
1.49k
  CHECK( sps.maxTLayers > VVENC_MAX_TLAYER, "array index out of bounds" );
1049
10.4k
  for( int i = 0; i < sps.maxTLayers; i++ )
1050
8.95k
  {
1051
8.95k
    sps.maxDecPicBuffering[ i ]     = m_gopCfg->getMaxDecPicBuffering()[ i ];
1052
8.95k
    sps.numReorderPics[ i ]         = m_gopCfg->getNumReorderPics()[ i ];
1053
8.95k
  }
1054
1055
1.49k
  sps.vuiParametersPresent          = m_pcEncCfg->m_vuiParametersPresent;
1056
1057
1.49k
  if (sps.vuiParametersPresent)
1058
0
  {
1059
0
    VUI& vui = sps.vuiParameters;
1060
0
    vui.aspectRatioInfoPresent        = m_pcEncCfg->m_aspectRatioInfoPresent;
1061
0
    vui.aspectRatioConstantFlag       = true; // false if SampleAspectRatioInfoSEIEnabled, but this SEI is not used
1062
0
    vui.aspectRatioIdc                = m_pcEncCfg->m_aspectRatioIdc;
1063
0
    vui.sarWidth                      = m_pcEncCfg->m_sarWidth;
1064
0
    vui.sarHeight                     = m_pcEncCfg->m_sarHeight;
1065
0
    vui.colourDescriptionPresent      = m_pcEncCfg->m_colourDescriptionPresent;
1066
0
    vui.colourPrimaries               = m_pcEncCfg->m_colourPrimaries;
1067
0
    vui.transferCharacteristics       = m_pcEncCfg->m_transferCharacteristics;
1068
0
    vui.matrixCoefficients            = m_pcEncCfg->m_matrixCoefficients;
1069
0
    vui.chromaLocInfoPresent          = m_pcEncCfg->m_chromaLocInfoPresent;
1070
0
    vui.chromaSampleLocType           = m_pcEncCfg->m_chromaSampleLocType;
1071
0
    vui.chromaSampleLocTypeTopField   = 0;
1072
0
    vui.chromaSampleLocTypeBottomField= 0;
1073
0
    vui.overscanInfoPresent           = m_pcEncCfg->m_overscanInfoPresent;
1074
0
    vui.overscanAppropriateFlag       = m_pcEncCfg->m_overscanAppropriateFlag;
1075
0
    vui.videoFullRangeFlag            = m_pcEncCfg->m_videoFullRangeFlag;
1076
0
  }
1077
1078
1.49k
  sps.hrdParametersPresent            = m_pcEncCfg->m_hrdParametersPresent;
1079
1080
1.49k
  sps.numLongTermRefPicSPS            = NUM_LONG_TERM_REF_PIC_SPS;
1081
1.49k
  CHECK(!(NUM_LONG_TERM_REF_PIC_SPS <= MAX_NUM_LONG_TERM_REF_PICS), "Unspecified error");
1082
1.49k
  for (int k = 0; k < NUM_LONG_TERM_REF_PIC_SPS; k++)
1083
0
  {
1084
0
    sps.ltRefPicPocLsbSps[k]          = 0;
1085
0
    sps.usedByCurrPicLtSPS[k]         = 0;
1086
0
  }
1087
1.49k
  sps.chromaQpMappingTable.m_numQpTables = (m_pcEncCfg->m_chromaQpMappingTableParams.m_sameCQPTableForAllChromaFlag ? 1 : (sps.jointCbCr ? 3 : 2));
1088
1.49k
  sps.chromaQpMappingTable.setParams(m_pcEncCfg->m_chromaQpMappingTableParams, sps.qpBDOffset[ CH_C ]);
1089
1.49k
  sps.chromaQpMappingTable.derivedChromaQPMappingTables();
1090
1.49k
}
1091
1092
void EncGOP::xInitPPS(PPS &pps, const SPS &sps) const
1093
1.49k
{
1094
1.49k
  bool bUseDQP = m_pcEncCfg->m_cuQpDeltaSubdiv > 0;
1095
1.49k
  bUseDQP |= m_pcEncCfg->m_lumaLevelToDeltaQPEnabled == 1;
1096
1.49k
  bUseDQP |= m_pcEncCfg->m_usePerceptQPA;
1097
1.49k
  bUseDQP |= m_pcEncCfg->hasBlockQpOffsets();
1098
1099
1.49k
  if (m_pcEncCfg->m_costMode==VVENC_COST_SEQUENCE_LEVEL_LOSSLESS || m_pcEncCfg->m_costMode==VVENC_COST_LOSSLESS_CODING)
1100
0
  {
1101
0
    bUseDQP = false;
1102
0
  }
1103
1.49k
  if( m_pcEncCfg->m_maxDeltaQP == 0 )
1104
0
  {
1105
0
    bUseDQP = false;
1106
0
  }
1107
1108
  // pps ID already initialised.
1109
1.49k
  pps.spsId                         = sps.spsId;
1110
1.49k
  pps.jointCbCrQpOffsetPresent      = m_pcEncCfg->m_JointCbCrMode;
1111
1.49k
  pps.picWidthInLumaSamples         = m_pcEncCfg->m_PadSourceWidth;
1112
1.49k
  pps.picHeightInLumaSamples        = m_pcEncCfg->m_PadSourceHeight;
1113
1.49k
  if( pps.picWidthInLumaSamples == sps.maxPicWidthInLumaSamples && pps.picHeightInLumaSamples == sps.maxPicHeightInLumaSamples )
1114
1.49k
  {
1115
1.49k
    pps.conformanceWindow           = sps.conformanceWindow;
1116
1.49k
  }
1117
0
  else
1118
0
  {
1119
0
    pps.conformanceWindow.setWindow( m_pcEncCfg->m_confWinLeft, m_pcEncCfg->m_confWinRight, m_pcEncCfg->m_confWinTop, m_pcEncCfg->m_confWinBottom );
1120
0
  }
1121
1122
1.49k
  pps.picWidthInCtu                 = (pps.picWidthInLumaSamples + (sps.CTUSize-1)) / sps.CTUSize;
1123
1.49k
  pps.picHeightInCtu                = (pps.picHeightInLumaSamples + (sps.CTUSize-1)) / sps.CTUSize;
1124
1.49k
  pps.subPics.clear();
1125
1.49k
  pps.subPics.resize(1);
1126
1.49k
  pps.subPics[0].init( pps.picWidthInCtu, pps.picHeightInCtu, pps.picWidthInLumaSamples, pps.picHeightInLumaSamples);
1127
1.49k
  pps.useDQP                        = bUseDQP;
1128
1129
1.49k
  if ( m_pcEncCfg->m_cuChromaQpOffsetSubdiv >= 0 )
1130
0
  {
1131
    //th check how this is configured now    pps.cuChromaQpOffsetSubdiv = m_pcEncCfg->m_cuChromaQpOffsetSubdiv;
1132
0
    pps.chromaQpOffsetListLen = 0;
1133
0
    pps.setChromaQpOffsetListEntry(1, 6, 6, 6);
1134
0
  }
1135
1136
  // fix PPS init QP to 26 or 32 (depending on BD) to make concatenating bitstreams more robust
1137
1.49k
  pps.picInitQPMinus26 = 6 - sps.qpBDOffset[CH_L] / 2;
1138
1139
1.49k
  pps.chromaQpOffset[COMP_Y]          = 0;
1140
1.49k
  pps.chromaQpOffset[COMP_Cb]         = m_pcEncCfg->m_chromaCbQpOffset;
1141
1.49k
  pps.chromaQpOffset[COMP_Cr]         = m_pcEncCfg->m_chromaCrQpOffset;
1142
1.49k
  pps.chromaQpOffset[COMP_JOINT_CbCr] = m_pcEncCfg->m_chromaCbCrQpOffset;
1143
1144
1.49k
  bool bChromaDeltaQPEnabled = false;
1145
1.49k
  {
1146
1.49k
    bChromaDeltaQPEnabled = ( m_pcEncCfg->m_sliceChromaQpOffsetIntraOrPeriodic[ 0 ] || m_pcEncCfg->m_sliceChromaQpOffsetIntraOrPeriodic[ 1 ] );
1147
1.49k
    bChromaDeltaQPEnabled |= (m_pcEncCfg->m_usePerceptQPA || (m_pcEncCfg->m_LookAhead && m_pcRateCtrl->m_pcEncCfg->m_RCTargetBitrate > 0) || m_pcEncCfg->m_sliceChromaQpOffsetPeriodicity > 0) && (m_pcEncCfg->m_internChromaFormat != VVENC_CHROMA_400);
1148
1.49k
    if( ! bChromaDeltaQPEnabled && sps.dualITree && ( m_pcEncCfg->m_internChromaFormat != VVENC_CHROMA_400 ) )
1149
0
    {
1150
0
      bChromaDeltaQPEnabled = (m_pcEncCfg->m_chromaCbQpOffsetDualTree != 0 || m_pcEncCfg->m_chromaCrQpOffsetDualTree != 0 || m_pcEncCfg->m_chromaCbCrQpOffsetDualTree != 0);
1151
0
    }
1152
1.49k
    if( ! bChromaDeltaQPEnabled )
1153
0
    {
1154
0
      bChromaDeltaQPEnabled = m_gopCfg->isChromaDeltaQPEnabled();
1155
0
    }
1156
1.49k
  }
1157
1.49k
  pps.sliceChromaQpFlag                 = bChromaDeltaQPEnabled;
1158
1.49k
  pps.outputFlagPresent                 = false;
1159
1.49k
  pps.deblockingFilterOverrideEnabled   = !m_pcEncCfg->m_loopFilterOffsetInPPS;
1160
1.49k
  pps.deblockingFilterDisabled          = m_pcEncCfg->m_bLoopFilterDisable;
1161
1162
1.49k
  if (! pps.deblockingFilterDisabled)
1163
1.49k
  {
1164
5.97k
    for( int comp = 0; comp < MAX_NUM_COMP; comp++)
1165
4.47k
    {
1166
4.47k
      pps.deblockingFilterBetaOffsetDiv2[comp]  = m_pcEncCfg->m_loopFilterBetaOffsetDiv2[comp];
1167
4.47k
      pps.deblockingFilterTcOffsetDiv2[comp]    = m_pcEncCfg->m_loopFilterTcOffsetDiv2[comp];
1168
4.47k
    }
1169
1.49k
  }
1170
1171
  // deblockingFilterControlPresent is true if any of the settings differ from the inferred values:
1172
1.49k
  bool deblockingFilterControlPresent   = pps.deblockingFilterOverrideEnabled ||
1173
1.49k
                                          pps.deblockingFilterDisabled     ||
1174
1.49k
                                          pps.deblockingFilterBetaOffsetDiv2[COMP_Y] != 0 ||
1175
1.49k
                                          pps.deblockingFilterTcOffsetDiv2  [COMP_Y] != 0 ||
1176
1.49k
                                          pps.deblockingFilterBetaOffsetDiv2[COMP_Cb] != 0 ||
1177
1.49k
                                          pps.deblockingFilterTcOffsetDiv2  [COMP_Cb] != 0 ||
1178
1.49k
                                          pps.deblockingFilterBetaOffsetDiv2[COMP_Cr] != 0 ||
1179
1.49k
                                          pps.deblockingFilterTcOffsetDiv2  [COMP_Cr] != 0;
1180
1181
1.49k
  pps.deblockingFilterControlPresent    = deblockingFilterControlPresent;
1182
1.49k
  pps.cabacInitPresent                  = m_pcEncCfg->m_cabacInitPresent != 0;
1183
1.49k
  pps.loopFilterAcrossTilesEnabled      = !m_pcEncCfg->m_bDisableLFCrossTileBoundaryFlag;
1184
1.49k
  pps.loopFilterAcrossSlicesEnabled     = !m_pcEncCfg->m_bDisableLFCrossSliceBoundaryFlag;
1185
1.49k
  pps.rpl1IdxPresent                    = sps.rpl1IdxPresent;
1186
1187
1.49k
  const uint32_t chromaArrayType = (int)sps.separateColourPlane ? CHROMA_400 : sps.chromaFormatIdc;
1188
1.49k
  if( chromaArrayType != CHROMA_400  )
1189
1.49k
  {
1190
1.49k
    bool chromaQPOffsetNotZero = ( pps.chromaQpOffset[COMP_Cb] != 0 || pps.chromaQpOffset[COMP_Cr] != 0 || pps.jointCbCrQpOffsetPresent || pps.sliceChromaQpFlag || pps.chromaQpOffsetListLen );
1191
1.49k
    bool chromaDbfOffsetNotAsLuma = ( pps.deblockingFilterBetaOffsetDiv2[COMP_Cb] != pps.deblockingFilterBetaOffsetDiv2[COMP_Y]
1192
1.49k
                                   || pps.deblockingFilterBetaOffsetDiv2[COMP_Cr] != pps.deblockingFilterBetaOffsetDiv2[COMP_Y]
1193
1.49k
                                   || pps.deblockingFilterTcOffsetDiv2[COMP_Cb] != pps.deblockingFilterTcOffsetDiv2[COMP_Y]
1194
1.49k
                                   || pps.deblockingFilterTcOffsetDiv2[COMP_Cr] != pps.deblockingFilterTcOffsetDiv2[COMP_Y]);
1195
1.49k
    pps.usePPSChromaTool = chromaQPOffsetNotZero || chromaDbfOffsetNotAsLuma;
1196
1.49k
  }
1197
1198
1.49k
  pps.numRefIdxL0DefaultActive = std::max( m_gopCfg->getDefaultNumActive( 0 ), 1 );
1199
1.49k
  pps.numRefIdxL1DefaultActive = std::max( m_gopCfg->getDefaultNumActive( 1 ), 1 );
1200
1.49k
  CHECK( pps.numRefIdxL0DefaultActive > 15, "num default ref index active exceeds maximum value");
1201
1.49k
  CHECK( pps.numRefIdxL1DefaultActive > 15, "num default ref index active exceeds maximum value");
1202
1203
1.49k
  pps.noPicPartition = !m_pcEncCfg->m_picPartitionFlag;
1204
1.49k
  pps.ctuSize        = sps.CTUSize;
1205
1.49k
  pps.log2CtuSize    = Log2( sps.CTUSize );
1206
1207
1.49k
  xInitPPSforTiles( pps, sps );
1208
1209
1.49k
  pps.pcv            = new PreCalcValues( sps, pps, m_pcEncCfg->m_MaxQT );
1210
1.49k
}
1211
1212
void EncGOP::xInitPPSforTiles(PPS &pps,const SPS &sps) const
1213
1.49k
{
1214
1.49k
  pps.numExpTileCols = m_pcEncCfg->m_numExpTileCols;
1215
1.49k
  pps.numExpTileRows = m_pcEncCfg->m_numExpTileRows;
1216
1.49k
  pps.numSlicesInPic = m_pcEncCfg->m_numSlicesInPic;
1217
1218
1.49k
  if( pps.noPicPartition )
1219
1.49k
  {
1220
1.49k
    pps.tileColWidth.resize( 1, pps.picWidthInCtu );
1221
1.49k
    pps.tileRowHeight.resize( 1, pps.picHeightInCtu );
1222
1.49k
    pps.initTiles();
1223
1.49k
    pps.sliceMap.clear();
1224
1.49k
    pps.sliceMap.resize(1);
1225
1.49k
    pps.sliceMap[0].addCtusToSlice(0, pps.picWidthInCtu, 0, pps.picHeightInCtu, pps.picWidthInCtu);
1226
1.49k
  }
1227
0
  else
1228
0
  {
1229
0
    for( int i = 0; i < pps.numExpTileCols; i++ )
1230
0
    {
1231
0
      pps.tileColWidth.push_back( m_pcEncCfg->m_tileColumnWidth[i] );
1232
0
    }
1233
0
    for( int i = 0; i < pps.numExpTileRows; i++ )
1234
0
    {
1235
0
      pps.tileRowHeight.push_back( m_pcEncCfg->m_tileRowHeight[i] );
1236
0
    }
1237
0
    pps.initTiles();
1238
0
    pps.rectSlice            = true;
1239
0
    pps.tileIdxDeltaPresent  = false;
1240
0
    pps.initRectSliceMap( &sps );
1241
0
  }
1242
1.49k
}
1243
1244
void EncGOP::xInitRPL(SPS &sps) const
1245
1.49k
{
1246
1.49k
  m_gopCfg->getDefaultRPLLists( sps.rplList[ 0 ], sps.rplList[ 1 ] );
1247
1248
1.49k
  sps.rpl1IdxPresent = ( sps.rplList[ 0 ].size() != sps.rplList[ 1 ].size() );
1249
1250
  //Check if all delta POC of STRP in each RPL has the same sign
1251
  //Check RPLL0 first
1252
1.49k
  bool isAllEntriesinRPLHasSameSignFlag = true;
1253
4.47k
  for( int list = 0; list < 2; list++)
1254
2.98k
  {
1255
2.98k
    const RPLList& rplList = sps.rplList[list];
1256
2.98k
    uint32_t numRPL        = (uint32_t)rplList.size();
1257
1258
2.98k
    bool isFirstEntry = true;
1259
2.98k
    bool lastSign = true;        //true = positive ; false = negative
1260
5.97k
    for (uint32_t ii = 0; isAllEntriesinRPLHasSameSignFlag && ii < numRPL; ii++)
1261
2.98k
    {
1262
2.98k
      const ReferencePictureList& rpl = rplList[ii];
1263
5.97k
      for (uint32_t jj = 0; jj < rpl.numberOfActivePictures; jj++)
1264
4.47k
      {
1265
4.47k
        if(rpl.isLongtermRefPic[jj])
1266
0
          continue;
1267
1268
4.47k
        if( isFirstEntry )
1269
1.49k
        {
1270
1.49k
          lastSign = (rpl.refPicIdentifier[jj] >= 0) ? true : false;
1271
1.49k
          isFirstEntry = false;
1272
1.49k
        }
1273
2.98k
        else
1274
2.98k
        {
1275
2.98k
          int ref = ( jj == 0 && !isFirstEntry ) ? 0 : rpl.refPicIdentifier[jj-1];
1276
2.98k
          if (((rpl.refPicIdentifier[jj] - ref) >= 0 ) != lastSign)
1277
1.49k
          {
1278
1.49k
            isAllEntriesinRPLHasSameSignFlag = false;
1279
1.49k
            break;  // break the inner loop
1280
1.49k
          }
1281
2.98k
        }
1282
4.47k
      }
1283
2.98k
    }
1284
2.98k
  }
1285
1286
1.49k
  sps.allRplEntriesHasSameSign = isAllEntriesinRPLHasSameSignFlag;
1287
1288
1.49k
  bool isRpl1CopiedFromRpl0 = ( sps.rplList[ 0 ].size() == sps.rplList[ 1 ].size() );
1289
4.47k
  for( int i = 0; isRpl1CopiedFromRpl0 && i < (int)sps.rplList[ 0 ].size(); i++)
1290
2.98k
  {
1291
2.98k
    isRpl1CopiedFromRpl0 = ( sps.rplList[0][i].getNumRefEntries() == sps.rplList[1][i].getNumRefEntries() );
1292
2.98k
    if( isRpl1CopiedFromRpl0 )
1293
2.98k
    {
1294
4.47k
      for( int j = 0; j < sps.rplList[0][i].getNumRefEntries(); j++ )
1295
2.98k
      {
1296
2.98k
        if( sps.rplList[0][i].refPicIdentifier[j] != sps.rplList[1][i].refPicIdentifier[j] )
1297
1.49k
        {
1298
1.49k
          isRpl1CopiedFromRpl0 = false;
1299
1.49k
          break;
1300
1.49k
        }
1301
2.98k
      }
1302
2.98k
    }
1303
2.98k
  }
1304
1.49k
  sps.rpl1CopyFromRpl0 = isRpl1CopiedFromRpl0;
1305
1.49k
}
1306
1307
void EncGOP::xInitHrdParameters(SPS &sps)
1308
1.49k
{
1309
1.49k
  m_EncHRD.initHRDParameters( *m_pcEncCfg, sps );
1310
1311
1.49k
  sps.generalHrdParams = m_EncHRD.generalHrdParams;
1312
1313
11.9k
  for(int i = 0; i < VVENC_MAX_TLAYER; i++)
1314
10.4k
  {
1315
10.4k
    sps.olsHrdParams[i] = m_EncHRD.olsHrdParams[i];
1316
10.4k
  }
1317
1.49k
}
1318
1319
/** Function for deciding the nal_unit_type.
1320
 */
1321
1322
vvencNalUnitType EncGOP::xGetNalUnitType( const GOPEntry* _gopEntry ) const
1323
2.98k
{
1324
2.98k
  const GOPEntry& gopEntry = *_gopEntry;
1325
1326
2.98k
  if( gopEntry.m_POC == 0 && m_pcEncCfg->m_poc0idr )
1327
0
  {
1328
0
    return VVENC_NAL_UNIT_CODED_SLICE_IDR_N_LP;
1329
0
  }
1330
1331
2.98k
  if( gopEntry.m_isStartOfIntra )
1332
2.98k
  {
1333
2.98k
    if( m_pcEncCfg->m_DecodingRefreshType == VVENC_DRT_CRA || m_pcEncCfg->m_DecodingRefreshType == VVENC_DRT_CRA_CRE )
1334
2.98k
    {
1335
2.98k
      if( m_lastIDR == 0 && !m_pcEncCfg->m_poc0idr )
1336
2.98k
      {
1337
2.98k
        return VVENC_NAL_UNIT_CODED_SLICE_IDR_W_RADL;
1338
2.98k
      }
1339
0
      else
1340
0
      {
1341
0
        return VVENC_NAL_UNIT_CODED_SLICE_CRA;
1342
0
      }
1343
2.98k
    }
1344
0
    else if( m_pcEncCfg->m_DecodingRefreshType == VVENC_DRT_IDR_NO_RADL )
1345
0
    {
1346
0
      return VVENC_NAL_UNIT_CODED_SLICE_IDR_N_LP;
1347
0
    }
1348
0
    else
1349
0
    {
1350
0
      return VVENC_NAL_UNIT_CODED_SLICE_IDR_W_RADL;
1351
0
    }
1352
2.98k
  }
1353
1354
0
  if( m_pocCRA > 0 && gopEntry.m_POC < m_pocCRA )
1355
0
  {
1356
    // All leading pictures are being marked as TFD pictures here since current encoder uses all
1357
    // reference pictures while encoding leading pictures. An encoder can ensure that a leading
1358
    // picture can be still decodable when random accessing to a CRA/CRANT/BLA/BLANT picture by
1359
    // controlling the reference pictures used for encoding that leading picture. Such a leading
1360
    // picture need not be marked as a TFD picture.
1361
0
    return VVENC_NAL_UNIT_CODED_SLICE_RASL;
1362
0
  }
1363
1364
0
  if( m_lastIDR > 0 && gopEntry.m_POC < m_lastIDR && m_pcEncCfg->m_DecodingRefreshType != VVENC_DRT_IDR_NO_RADL )
1365
0
  {
1366
0
    return VVENC_NAL_UNIT_CODED_SLICE_RADL;
1367
0
  }
1368
1369
0
  return VVENC_NAL_UNIT_CODED_SLICE_TRAIL;
1370
0
}
1371
1372
bool EncGOP::xIsSliceTemporalSwitchingPoint( const Slice* slice, const PicList& picList ) const
1373
1.49k
{
1374
1.49k
  if( slice->TLayer <= 0
1375
0
      || slice->nalUnitType == VVENC_NAL_UNIT_CODED_SLICE_RADL
1376
0
      || slice->nalUnitType == VVENC_NAL_UNIT_CODED_SLICE_RASL
1377
0
      || ! slice->isStepwiseTemporalLayerSwitchingPointCandidate( picList ) )
1378
1.49k
  {
1379
1.49k
    return false;
1380
1.49k
  }
1381
1382
0
  const GOPEntry& gopEntry = *(slice->pic->gopEntry);
1383
0
  const bool isSTSA        = gopEntry.m_isSTSA;
1384
0
  return isSTSA;
1385
1.49k
}
1386
1387
void EncGOP::xSetupPicAps( Picture* pic )
1388
1.49k
{
1389
  // manage global APS list
1390
1.49k
  m_globalApsList.push_back( new PicApsGlobal( pic->poc, pic->TLayer ) );
1391
1.49k
  CHECK( pic->picApsGlobal != nullptr, "Top level APS ptr must be nullptr" );
1392
1393
  // the max size of global APS list is more than enough to support parallelization 
1394
  // additional +2 offset, due two max possible processing delay of two GOPs (Threads=1 mode)
1395
1.49k
  if( m_globalApsList.size() > ( std::max( (int)MAX_NUM_APS, m_pcEncCfg->m_GOPSize ) * ( m_pcEncCfg->m_maxParallelFrames + 2 ) ) )
1396
0
  {
1397
0
    if( m_globalApsList.front()->refCnt == 0 )
1398
0
    {
1399
0
      delete m_globalApsList.front();
1400
0
      m_globalApsList.pop_front();
1401
0
    }
1402
0
  }
1403
1404
1.49k
  pic->picApsGlobal = m_globalApsList.back();
1405
1406
  // determine reference APS
1407
1.49k
  const bool mtPicParallel = m_pcEncCfg->m_numThreads > 0;
1408
1.49k
  if( mtPicParallel && pic->slices[0]->isIntra() )
1409
1.49k
  {
1410
    // reset APS propagation on Intra-Slice in MT-mode
1411
1.49k
    return;
1412
1.49k
  }
1413
1414
  // get previous APS (in coding order) to propagate from it
1415
  // in parallelization case (parallel pictures), we refer to APS from lower temporal layer
1416
  // NOTE: elements in the global APS list are following in coding order
1417
1418
0
  PicApsGlobal* refAps = nullptr;
1419
0
  auto curApsItr = std::find_if( m_globalApsList.begin(), m_globalApsList.end(), [pic]( auto p ) { return p->poc == pic->poc; } );
1420
0
  CHECK( curApsItr == m_globalApsList.end(), "Should not happen" );
1421
1422
0
  if( curApsItr != m_globalApsList.begin() )
1423
0
  {
1424
0
    if( mtPicParallel )
1425
0
    {
1426
0
      auto r_begin = std::reverse_iterator<std::deque<PicApsGlobal*>::iterator>(curApsItr);
1427
0
      auto r_end   = std::reverse_iterator<std::deque<PicApsGlobal*>::iterator>(m_globalApsList.begin());
1428
0
      auto refApsItr = ( pic->TLayer > 0 ) ? std::find_if( r_begin, r_end, [pic]( auto p ) { return p->tid  < pic->TLayer; } ):
1429
0
                                             std::find_if( r_begin, r_end, [pic]( auto p ) { return p->tid == pic->TLayer; } );
1430
0
      if( refApsItr == r_end )
1431
0
        return;
1432
0
      refAps = *refApsItr;
1433
0
    }
1434
0
    else
1435
0
    {
1436
0
      curApsItr--;
1437
0
      refAps = *curApsItr;
1438
0
    }
1439
0
    if( refAps )
1440
0
      refAps->refCnt++;
1441
0
  }
1442
1443
  //CHECK( !refAps, "Faied to get reference APS" );
1444
0
  pic->refApsGlobal = refAps;
1445
0
}
1446
1447
void EncGOP::xAnalyzeFilmGrain( Picture& pic )
1448
0
{
1449
  // Pictures come here in coding order. Analyzing them here rather than when their encoding starts keeps the choice
1450
  // of analyzed pictures, and the parameters each access unit signals, independent of the parallel encoding order.
1451
  // The analysis looks at the first picture of every GOP, which MCTF filtered for it.
1452
0
  if( !pic.m_picShared->getFgaFilteredBuf().valid() && !pic.m_picShared->fgaUsesFilteredBuf() )
1453
0
  {
1454
0
    return;
1455
0
  }
1456
0
  m_fgaTasks.emplace_back( new FgaTask );
1457
0
  FgaTask* task  = m_fgaTasks.back().get();
1458
0
  task->gop        = this;
1459
0
  task->pic        = &pic;
1460
0
  task->codingNum  = pic.gopEntry->m_codingNum;
1461
1462
  // The result applies from the picture after the previously analysed one; when PreProcess found a scene cut in
1463
  // between, from the cut, so that the pictures before it keep the parameters of their scene. The first analysis
1464
  // applies to all pictures before it as well.
1465
0
  const bool first  = m_fgaPrevAnalysedPoc == std::numeric_limits<int>::min();
1466
0
  const int  cutPoc = pic.m_picShared->fgaSceneCut() ? pic.poc : pic.m_picShared->fgaLastSceneCutPoc();
1467
0
  task->sceneCut    = !first && cutPoc > m_fgaPrevAnalysedPoc;
1468
0
  task->startPoc    = first ? std::numeric_limits<int>::min() : task->sceneCut ? cutPoc : m_fgaPrevAnalysedPoc + 1;
1469
0
  m_fgaPrevAnalysedPoc = pic.poc;
1470
0
  const Barrier* previous = m_fgaLast ? &m_fgaLast->counter.done : nullptr;
1471
0
  m_fgaLast = task;
1472
1473
0
  static auto runTask = []( int, void* param )
1474
0
  {
1475
0
    FgaTask* t = static_cast<FgaTask*>( param );
1476
0
    t->gop->xRunFgaTask( *t );
1477
0
    return true;
1478
0
  };
1479
0
  if( m_threadPool )
1480
0
  {
1481
    // off the encoding path: the result is only needed when the access unit of the picture is written
1482
0
    m_threadPool->addBarrierTask( runTask, task, &task->counter, nullptr, { previous } );
1483
0
  }
1484
0
  else
1485
0
  {
1486
0
    runTask( 0, task );
1487
0
  }
1488
0
}
1489
1490
void EncGOP::xRunFgaTask( FgaTask& task )
1491
0
{
1492
0
  PicShared* shared         = task.pic->m_picShared;
1493
0
  PelStorage& fgaBuf        = shared->getFgaFilteredBuf();
1494
0
  PelStorage& residualBuf   = shared->getFgaResidualBuf();
1495
0
  const int residualWeight  = shared->fgaResidualWeight();
1496
0
  const bool ownBuf         = fgaBuf.valid();
1497
0
  const CPelUnitBuf denoised = ownBuf ? CPelUnitBuf( fgaBuf ) : CPelUnitBuf( task.pic->getFilteredOrigBuffer() );
1498
0
  if( residualWeight )
1499
0
  {
1500
0
    const CPelUnitBuf residual( residualBuf );
1501
0
    m_fgAnalyzer.estimateGrainParameters( task.pic->getOrigBuffer(), denoised, &residual, residualWeight, task.sceneCut );
1502
0
  }
1503
0
  else
1504
0
  {
1505
0
    m_fgAnalyzer.estimateGrainParameters( task.pic->getOrigBuffer(), denoised, nullptr, 0, task.sceneCut );
1506
0
  }
1507
0
  residualBuf.destroy();
1508
0
  if( ownBuf )
1509
0
  {
1510
0
    fgaBuf.destroy();
1511
0
  }
1512
0
  task.result = FilmGrainParam( m_fgAnalyzer, m_pcEncCfg->m_fgaStrength );
1513
0
}
1514
1515
void EncGOP::xWaitFgaTasks()
1516
1.49k
{
1517
1.49k
  for( auto& task : m_fgaTasks )
1518
0
  {
1519
0
    task->counter.wait();
1520
0
  }
1521
1.49k
}
1522
1523
void EncGOP::xInitPicsInCodingOrder( const PicList& picList )
1524
2.98k
{
1525
2.98k
  CHECK( m_pcEncCfg->m_maxParallelFrames <= 0 && m_gopEncListInput.size() > 0,  "no frame parallel processing enabled, but multiple pics in flight" );
1526
2.98k
  CHECK( m_pcEncCfg->m_maxParallelFrames <= 0 && m_gopEncListOutput.size() > 0, "no frame parallel processing enabled, but multiple pics in flight" );
1527
1528
  // loop over pic list, which is sorted in coding number order 
1529
5.97k
  for( auto it = picList.begin(); it != picList.end(); ++it )
1530
2.98k
  {
1531
2.98k
    auto pic = (*it);
1532
    // skip pics, which have already been initialized
1533
2.98k
    if( pic->isInitDone )
1534
1.49k
      continue;
1535
1536
    // update visual activity for last start of GOP picture
1537
    // this may have been changed in the shared picture data due to fixStartOfLastGop()
1538
1.49k
    if( pic->gopEntry->m_isStartOfGop && picList.back()->isFlush )
1539
1.49k
    {
1540
1.49k
      xUpdateVAStartOfLastGop( *pic );
1541
1.49k
    }
1542
1543
    // GOP QP adjustments
1544
1.49k
    if( (m_pcEncCfg->m_rateCap || m_pcEncCfg->m_GOPQPA || m_pcEncCfg->m_usePerceptQPA) && pic->gopEntry->m_isStartOfGop )
1545
1.49k
    {
1546
      // note: in case of rate cap, wait until the complete GOP is in the list and update-list is empty
1547
1.49k
      if( !m_pcEncCfg->m_rateCap ||
1548
0
        ((pic->gopEntry->m_gopNum != picList.back()->gopEntry->m_gopNum || picList.back()->isFlush) && m_rcUpdateList.empty()) )
1549
1.49k
      {
1550
1.49k
        xInitGopQpCascade( *pic, it, picList );
1551
1.49k
      }
1552
0
      else
1553
0
      {
1554
        // rate cap: wait until the condition is met
1555
0
        break;
1556
0
      }
1557
1.49k
    }
1558
1559
    // continue with next pic in increasing coding number order
1560
1.49k
    if( pic->gopEntry->m_codingNum != m_lastCodingNum + 1 && ! picList.back()->isFlush )
1561
0
      break;
1562
1563
1.49k
    CHECK( m_lastCodingNum == -1 && ! pic->gopEntry->m_isStartOfIntra, "encoding should start with an I-Slice" );
1564
1565
1.49k
    xForceScc( *pic );
1566
1567
    // initialize slice header
1568
1.49k
    pic->encTime.startTimer();
1569
1.49k
    xInitFirstSlice( *pic, picList, false );
1570
1.49k
    pic->encTime.stopTimer();
1571
1572
    // pictures ready for encoding
1573
1.49k
    m_gopEncListInput.push_back( pic );
1574
1.49k
    m_gopEncListOutput.push_back( pic );
1575
1576
1.49k
    if( m_pcEncCfg->m_alf && m_pcEncCfg->m_alfTempPred )
1577
1.49k
    {
1578
1.49k
        xSetupPicAps( pic );
1579
1.49k
    }
1580
1581
1.49k
    if( m_pcEncCfg->m_fga && !m_isPreAnalysis && m_pcRateCtrl->rcIsFinalPass )
1582
0
    {
1583
0
      xAnalyzeFilmGrain( *pic );
1584
0
    }
1585
1586
    // continue with next picture
1587
1.49k
    m_lastCodingNum = pic->gopEntry->m_codingNum;
1588
1589
    // in single threading initialize only one picture per encoding loop
1590
1.49k
    if( m_pcEncCfg->m_maxParallelFrames <= 0 )
1591
0
      break;
1592
1.49k
  }
1593
1594
2.98k
  CHECK( !(m_pcEncCfg->m_rateCap || m_pcEncCfg->m_GOPQPA || m_pcEncCfg->m_usePerceptQPA) && picList.size() && m_pcEncCfg->m_maxParallelFrames <= 0 && m_gopEncListInput.size() != 1,  "no new picture for encoding found" );
1595
2.98k
  CHECK( !(m_pcEncCfg->m_rateCap || m_pcEncCfg->m_GOPQPA || m_pcEncCfg->m_usePerceptQPA) && picList.size() && m_pcEncCfg->m_maxParallelFrames <= 0 && m_gopEncListOutput.size() != 1, "no new picture for encoding found" );
1596
2.98k
}
1597
1598
void EncGOP::xUpdateRcIfp()
1599
0
{
1600
  // deterministic behavior: RC update on next finished frame in sliding window coding order,
1601
  //                         evaluate only one finished frame at front of the list that makes place for the next frame
1602
  //                         whose parameters can be set using the finished frame bits info
1603
  //
1604
  // non-deterministic behavior: RC update on any finished frame
1605
1606
#if IFP_RC_DETERMINISTIC
1607
  if( m_rcUpdateList.front()->isReconstructed && m_rcUpdateList.back()->encRCPic && ( m_rcUpdateList.front()->isFlush || m_rcUpdateList.size() == m_pcEncCfg->m_maxParallelFrames ) )
1608
  {   
1609
#endif
1610
0
    for( auto it = m_rcUpdateList.begin(); it != m_rcUpdateList.end(); )
1611
0
    {
1612
0
      auto pic = *it;
1613
0
      if( pic->isReconstructed )
1614
0
      {
1615
0
        pic->actualTotalBits = pic->sliceDataStreams[0].getNumberOfWrittenBits();
1616
0
        pic->refCounter--;
1617
0
        m_pcRateCtrl->updateAfterPicEncRC( pic );
1618
0
        it = m_rcUpdateList.erase( it );
1619
0
      }
1620
0
      else
1621
0
      {
1622
0
        ++it;
1623
0
      }
1624
#if IFP_RC_DETERMINISTIC
1625
      // in deterministic case, only one frame is allowed to update the RC
1626
      break;
1627
#endif
1628
0
    }
1629
#if IFP_RC_DETERMINISTIC
1630
  }
1631
#endif
1632
0
}
1633
1634
inline bool getReorderedProcList( std::list<Picture*>& inputList, std::list<Picture*>& procList, const int maxSize, bool isIFP, bool restrictToGOP )
1635
0
{
1636
  // deliver frames of the same TID (temporal layer) and from the same GOP
1637
0
  const int procTL = inputList.size() ? inputList.front()->TLayer             : -1;
1638
0
  const int gopNum = inputList.size() ? inputList.front()->gopEntry->m_gopNum : -1;
1639
0
  bool added = false;
1640
0
  for( auto it = inputList.begin(); it != inputList.end(); )
1641
0
  {
1642
0
    auto pic = *it;
1643
0
    if( ( pic->gopEntry->m_gopNum == gopNum || !restrictToGOP )
1644
0
        && pic->TLayer == procTL
1645
0
        && ( isIFP ? pic->slices[ 0 ]->checkAllRefPicsAccessible(): pic->slices[ 0 ]->checkAllRefPicsReconstructed() ) )
1646
0
    {
1647
0
      pic->isInProcessList = true;
1648
0
      procList.push_back  ( pic );
1649
0
      it = inputList.erase( it );
1650
0
      added = true;
1651
0
    }
1652
0
    else
1653
0
    {
1654
0
      ++it;
1655
0
    }
1656
0
    if( (int)procList.size() >= maxSize )
1657
0
      break;
1658
0
  }
1659
0
  return added;
1660
0
}
1661
1662
inline void getProcListForOneGOP( std::list<Picture*>& inputList, std::list<Picture*>& procList )
1663
0
{
1664
  // provide frames of the same GOP
1665
0
  const int gopNum = inputList.size() ? inputList.front()->gopEntry->m_gopNum : -1;
1666
0
  for( auto it = inputList.begin(); it != inputList.end(); )
1667
0
  {
1668
0
    auto pic = *it;
1669
0
    if( pic->gopEntry->m_gopNum == gopNum )
1670
0
    {
1671
0
      procList.push_back  ( pic );
1672
0
      it = inputList.erase( it );
1673
0
    }
1674
0
    else
1675
0
    {
1676
0
      ++it;
1677
0
    }
1678
0
  }
1679
0
}
1680
1681
void EncGOP::xGetProcessingLists( std::list<Picture*>& procList, std::list<Picture*>& rcUpdateList, const bool lockStepMode )
1682
1.49k
{
1683
  // in lockstep mode, frames are reordered in a specific processing order
1684
1.49k
  if( lockStepMode )
1685
0
  {
1686
0
    if( m_pcEncCfg->m_ifpLines )
1687
0
    {
1688
      // prepare reordered list
1689
      // we need an additional reordering list to ensure causality of the coding order (ref.pics) on irregular GOP structures
1690
      // in the first step, the reordered list is filled
1691
      // in the second, the frames from reordered list are moved to proc. list up to required update-list size
1692
0
      const int maxUpdateListSize = m_pcEncCfg->m_maxParallelFrames;
1693
0
      if( rcUpdateList.size() < maxUpdateListSize && ( !m_gopEncListInput.empty() || !m_rcInputReorderList.empty()))
1694
0
      {
1695
0
        while( rcUpdateList.size() < maxUpdateListSize && ( !m_gopEncListInput.empty() || !m_rcInputReorderList.empty()) )
1696
0
        {
1697
0
          if( !m_rcInputReorderList.empty() )
1698
0
          {
1699
0
            auto pic = m_rcInputReorderList.front();
1700
0
            m_rcInputReorderList.pop_front();
1701
0
            pic->refCounter++;
1702
0
            procList.push_back( pic );
1703
0
            rcUpdateList.push_back( pic );
1704
0
          }
1705
0
          else
1706
0
          {
1707
0
            while( m_rcInputReorderList.size() < maxUpdateListSize && !m_gopEncListInput.empty() )
1708
0
            {
1709
0
              getReorderedProcList( m_gopEncListInput, m_rcInputReorderList, maxUpdateListSize, true, true );
1710
0
            }
1711
0
          }
1712
0
        }
1713
0
      }
1714
0
    }
1715
0
    else if( rcUpdateList.empty() )
1716
0
    {
1717
      // retrieve next lockstep chunk from reordered list
1718
0
      const int procTL         = m_gopEncListInput.size() ? m_gopEncListInput.front()->TLayer : -1;
1719
0
      const int minSerialDepth = m_pcEncCfg->m_maxParallelFrames > 2 ? 1 : 2;  // up to this temporal layer encode pictures only in serial mode
1720
0
      const int maxSize        = procTL <= minSerialDepth ? 1 : m_pcEncCfg->m_maxParallelFrames;
1721
0
      getReorderedProcList( m_gopEncListInput, procList, maxSize, false, true );
1722
0
      std::copy( procList.begin(), procList.end(), std::back_inserter(rcUpdateList) );
1723
0
    }
1724
0
  }
1725
1.49k
  else
1726
1.49k
  {
1727
    // regular coding mode (non-RC)
1728
    // in case of IFP, using the reordered list brings an additional speedup
1729
1.49k
    if( m_pcEncCfg->m_ifpLines )
1730
0
    {
1731
0
      const size_t inputListSize = m_gopEncListInput.size();
1732
1733
      // in case of GOP parallel processing, we do not put all the frames from the current GOP in proc.list.
1734
      // the reason for this is that we want to add frames from the next GOP as soon as possible.
1735
0
      const size_t targetProcListSize = procList.size() + (m_pcEncCfg->m_numParallelGOPs ? m_pcEncCfg->m_maxParallelFrames: inputListSize);
1736
1737
0
      while( !m_gopEncListInput.empty() && procList.size() < targetProcListSize )
1738
0
      {
1739
0
        if( !getReorderedProcList( m_gopEncListInput, procList, (int)procList.size() + m_pcEncCfg->m_maxParallelFrames, true, !m_pcEncCfg->m_numParallelGOPs ) )
1740
0
          break;
1741
0
      }
1742
0
      if( m_gopEncListInput.size() == inputListSize )
1743
0
        msg.log( VVENC_WARNING, "Processing list derivation: attempting to run in a deadlock" );
1744
0
    }
1745
1.49k
    else
1746
1.49k
    {
1747
1.49k
      if( m_pcEncCfg->m_rateCap ) // TODO helmrich: what about || *->m_GOPQPA?
1748
0
      {
1749
        // ensure that procList contains only pictures from one GOP
1750
0
        getProcListForOneGOP( m_gopEncListInput, procList );
1751
0
      }
1752
1.49k
      else
1753
1.49k
      {
1754
        // just pass the input list to processing list
1755
1.49k
        procList.splice( procList.end(), m_gopEncListInput );
1756
1.49k
        m_gopEncListInput.clear();
1757
1.49k
      }
1758
1.49k
    }
1759
1.49k
    if( m_pcEncCfg->m_RCTargetBitrate > 0 || m_pcEncCfg->m_rateCap || m_pcEncCfg->m_ifpLines )
1760
0
    {
1761
      // update-list is used for RC, RateCapping or IFP
1762
0
      std::copy( procList.begin(), procList.end(), std::back_inserter( rcUpdateList ) );
1763
0
    }
1764
1.49k
  }
1765
1.49k
  CHECK( ! rcUpdateList.empty() && m_gopEncListOutput.empty(), "first picture in RC update and in output list have to be the same" );
1766
1.49k
}
1767
1768
void EncGOP::xUpdateRateCap()
1769
0
{
1770
0
  for( auto it = m_rcUpdateList.begin(); it != m_rcUpdateList.end(); )
1771
0
  {
1772
0
    auto pic = *it;
1773
0
    if( pic->isReconstructed )
1774
0
    {
1775
0
      const unsigned uibits = pic->sliceDataStreams[0].getNumberOfWrittenBits();
1776
1777
0
      if( !pic->gopEntry->m_isStartOfIntra && pic->gopEntry->m_scType == SCT_NONE )
1778
0
      {
1779
0
        xUpdateRateCapBits( pic, uibits );
1780
0
      }
1781
0
      else if( pic->gopEntry->m_isStartOfIntra && pic->gopEntry->m_gopNum == 0 && pic->poc < m_pcEncCfg->m_GOPSize && m_rcap.accumTargetBits * (uint32_t) m_pcEncCfg->m_GOPSize < uibits )
1782
0
      {
1783
0
        m_rcap.accumActualBits += uibits - m_rcap.accumTargetBits * (uint32_t) m_pcEncCfg->m_GOPSize; // capped CQF: compensate for overspending in first I-frame
1784
0
      }
1785
1786
0
      it = m_rcUpdateList.erase( it );
1787
0
    }
1788
0
    else
1789
0
    {
1790
0
      ++it;
1791
0
    }
1792
0
  }
1793
0
}
1794
1795
void EncGOP::xUpdateRateCapBits( const Picture* pic, const uint32_t uibits )
1796
0
{
1797
  // try to adjust the rate for the first GOP on the scene-cut (or start of the sequence)
1798
0
  if( pic->gopEntry->m_isStartOfGop )
1799
0
  {
1800
0
    m_rcap.gopAdaptedQPAdj = 0;
1801
0
  }
1802
0
  else if( pic->isSceneCutCheckAdjQP )
1803
0
  {
1804
0
    CHECK( uibits == 0 || m_rcap.accumTargetBits == 0, "Not expected" );
1805
0
    const double f = std::min (1.5, pow (uibits / double (3u * m_rcap.accumTargetBits), 0.25));
1806
0
    if( f > 1.0 )
1807
0
    {
1808
0
      const Slice* slice = pic->slices[0];
1809
0
      const double d = (105.0 / 128.0) * sqrt( (double)std::max( 1, slice->sliceQp ) ) * log( f ) / log( 2.0 );
1810
0
      m_rcap.gopAdaptedQPAdj = int(d + 0.5);
1811
0
      m_rcap.nonRateCapEstim = f;
1812
      //uibits = 3u * m_rcap.AccumTargetBits; // can be used to avoid overweighting of TL1 picture
1813
0
    }
1814
0
  }
1815
0
  m_rcap.accumActualBits += unsigned (0.5 + uibits * m_rcap.nonRateCapEstim);
1816
0
}
1817
1818
void EncGOP::xUpdateVAStartOfLastGop( Picture& keyPic ) const
1819
1.49k
{
1820
1.49k
  keyPic.picVA = keyPic.m_picShared->m_picVA;
1821
1.49k
}
1822
1823
void EncGOP::xInitGopQpCascade( Picture& keyPic, PicList::const_iterator picListBegin, const PicList& picList )
1824
1.49k
{
1825
1.49k
  CHECK( !keyPic.gopEntry->m_isStartOfGop, "Expecting key picture as start of GOP")
1826
1.49k
  uint32_t gopMotEstCount = 0, gopMotEstError = 0;
1827
1.49k
  const double resRatio4K = double (m_pcEncCfg->m_SourceWidth * m_pcEncCfg->m_SourceHeight) / (3840.0 * 2160.0);
1828
1.49k
  const bool isHighRes    = (std::min (m_pcEncCfg->m_SourceWidth, m_pcEncCfg->m_SourceHeight) > 1280);
1829
1.49k
  const int gopNum        = keyPic.gopEntry->m_gopNum;
1830
1.49k
  const bool keyPicIsIdrNLP      = xGetNalUnitType(keyPic.gopEntry) == VVENC_NAL_UNIT_CODED_SLICE_IDR_N_LP;
1831
1.49k
  PicList::const_iterator picItr = picListBegin;
1832
1.49k
  const bool nextKeyPicAfterIDR  = keyPicIsIdrNLP && (++picItr != picList.end()) && (*picItr)->gopEntry->m_isStartOfGop;
1833
1834
1.49k
  int dQP = 0;
1835
1.49k
  double qpStart = 24.0;
1836
1.49k
  unsigned num = 0, sum = 0;
1837
1.49k
  unsigned nSC = 0, sSC = 0;
1838
1.49k
  uint8_t gopMinNoiseLevels[QPA_MAX_NOISE_LEVELS];
1839
1840
1.49k
  std::fill_n (gopMinNoiseLevels, QPA_MAX_NOISE_LEVELS, 255u);
1841
1842
  // get spatial activity of current and previous TL0 pic
1843
1.49k
  int spVisActTL0[2] = { 0, 0 };
1844
1.49k
  for( auto ch : { CH_L, CH_C } )
1845
2.98k
  {
1846
2.98k
    const int count = ( keyPic.picVA.spatAct[ ch ] > 0 && keyPic.picVA.prevTL0spatAct[ ch ] > 0 ) ? 2 : 1;
1847
2.98k
    spVisActTL0[ch] = ( keyPic.picVA.spatAct[ ch ] + keyPic.picVA.prevTL0spatAct[ ch ] + ( count >> 1 ) ) / count;
1848
2.98k
  }
1849
1850
2.98k
  for (auto picItr = picListBegin; picItr != picList.end(); ++picItr)
1851
1.49k
  {
1852
1.49k
    auto pic = (*picItr);
1853
1.49k
    if( pic->gopEntry->m_gopNum == gopNum )
1854
1.49k
    {
1855
1.49k
      if( pic->m_picShared->m_picMotEstError > 0 )
1856
0
      {
1857
0
        CHECK( pic->isInitDone, "try to modify GOP qp of picture, which has already been initialized" );
1858
        // summarize motion errors of all MCTF filtered pictures in GOP
1859
0
        gopMotEstCount++;
1860
0
        gopMotEstError += pic->m_picShared->m_picMotEstError;
1861
        // go through ranges, search per-range minimum in GOP
1862
0
        for (int i = 0; i < QPA_MAX_NOISE_LEVELS; i++)
1863
0
        {
1864
0
          gopMinNoiseLevels[i] = std::min<uint8_t> (gopMinNoiseLevels[i], pic->m_picShared->m_minNoiseLevels[i]);
1865
0
        }
1866
0
      }
1867
1.49k
      nSC++;
1868
1.49k
      sSC += (pic->isSccStrong ? 1 : 0) + (pic->isSccWeak ? 1 : 0);
1869
1870
1.49k
      if( pic == &keyPic && nextKeyPicAfterIDR ) // consider a virtual GOP containing only one IDR pic
1871
0
        break;
1872
1.49k
    }
1873
1.49k
  }
1874
1875
1.49k
  gopMotEstError = (gopMotEstError + (gopMotEstCount >> 1)) / std::max (1u, gopMotEstCount);
1876
1877
13.4k
  for (int i = 0; i < QPA_MAX_NOISE_LEVELS; i++) // go through ranges again, find overall min-average in GOP
1878
11.9k
  {
1879
11.9k
    if (gopMinNoiseLevels[i] < 255)
1880
0
    {
1881
0
      num++;
1882
0
      sum += gopMinNoiseLevels[i];
1883
0
    }
1884
11.9k
  }
1885
1886
  // force 2nd-order filter
1887
1.49k
  const bool f2O = (m_pcEncCfg->m_usePerceptQPA) && (sSC >= (nSC >> 1)) && (sum < 18 * num); // low-noise SC
1888
  
1889
  // adapt GOP's QP offsets
1890
1.49k
  if (num > 0 && sum > 0)
1891
0
  {
1892
0
    qpStart += 0.5 * (6.0 * log ((double) sum / (double) num) / log (2.0) - 1.0 - 24.0); // see RateCtrl.cpp
1893
0
    if (m_pcEncCfg->m_GOPQPA)
1894
0
    {
1895
0
      if (((qpStart > 29) && (spVisActTL0[CH_L] > 600)) ||
1896
0
          ((qpStart > 27) && (spVisActTL0[CH_L] > 850)) || (spVisActTL0[CH_L] > 1300))
1897
0
      {
1898
0
        dQP += 1;
1899
0
      }
1900
0
      if ((qpStart < 24) && (spVisActTL0[CH_L] < 400))
1901
0
      {
1902
0
        dQP -= 1;
1903
0
      }
1904
0
    }
1905
0
  }
1906
1.49k
  qpStart += log (resRatio4K) / log (2.0); // ICIP23 paper
1907
1908
1.49k
  if (keyPic.gopEntry->m_scType == SCT_TL0_SCENE_CUT)
1909
0
  {
1910
0
    m_rcap.reset();
1911
0
  }
1912
1913
  // derive rate capping parameters
1914
1.49k
  if (m_pcEncCfg->m_rateCap)
1915
0
  {
1916
0
    const int bDepth = m_pcEncCfg->m_internalBitDepth[CH_L];
1917
0
    const int intraP = Clip3(m_pcEncCfg->m_GOPSize, 4 * VVENC_MAX_GOP, m_pcEncCfg->m_IntraPeriod);
1918
0
    const int visAct = std::max(uint16_t(spVisActTL0[CH_L] >> (12 - bDepth)), keyPic.picVA.visAct); // when vaY=0
1919
0
    const double apa = sqrt((m_pcEncCfg->m_internalUsePerceptQPATempFiltISlice ? 32.0 : 16.0) * double(1 << (2 * bDepth - 10)) / sqrt(resRatio4K)); // average picture activity
1920
0
    const int auxOff = (m_pcEncCfg->hasBlockQpOffsets() && !keyPic.m_picShared->m_ctuBimQpOffset.empty() ? keyPic.m_picShared->m_picAuxQpOffset : 0) + dQP;
1921
0
    const int iFrmQP = std::min(MAX_QP, m_pcEncCfg->m_QP + m_pcEncCfg->m_intraQPOffset + auxOff + int(floor(m_pcEncCfg->m_QPFraction + 3.0 * log(visAct / apa) / log(2.0) + 0.5)));
1922
0
    const int qp32BC = int(16384.0 + 7.21875 * pow((double)spVisActTL0[CH_L], 4.0 / 3.0) + 1.46875 * pow((double)spVisActTL0[CH_C], 4.0 / 3.0)) * (isHighRes ? 96 : 24); // TODO hlm
1923
0
    const int iFrmBC = int(0.5 + qp32BC * pow(2.0, (32.0 - iFrmQP) * 11.0 / 64.0) * pow(resRatio4K, 2.0 / 3.0)); // * HD tuning
1924
0
    const int  shift = (gopMotEstError < 32 ? 5 - (gopMotEstError >> 4) : 3);
1925
0
    if (keyPic.m_picShared->m_picMotEstError >= 256) gopMotEstError >>= 2; else // avoid 2 much capping at cuts
1926
0
    if (gopMotEstError >= 120) /*TODO tune this*/ gopMotEstError >>= 1;
1927
0
    const int bFrmBC = int((4.0 * iFrmBC * (intraP - 1)) / sqrt((double)std::max(spVisActTL0[CH_L], spVisActTL0[CH_C])) * std::max(int(gopMotEstError * gopMotEstError) >> (bDepth / 2), (keyPic.picVA.visActTL0 - visAct) >> shift) * pow(2.0, -1.0 * bDepth));
1928
0
    const int meanGopSizeInIntraP = intraP / ((intraP + m_pcEncCfg->m_GOPSize - 1) / m_pcEncCfg->m_GOPSize);
1929
1930
0
    const double eps = 1.0 - 1.0 / double(1u << std::min(31u, m_rcap.accumGopCounter));
1931
0
    const double nonKeyPicsFactor = (m_rcap.accumTargetBits == 0) ? 1.0 : pow((double)m_rcap.accumActualBits / ((meanGopSizeInIntraP - 1.0) * m_rcap.accumTargetBits), eps);
1932
0
    const unsigned bFrmBC_final = bFrmBC * nonKeyPicsFactor;
1933
0
    const unsigned targetBits = (unsigned)((bFrmBC + (intraP >> 1)) / (intraP - 1));
1934
0
    m_rcap.accumTargetBits += targetBits;
1935
0
    if (keyPic.gopEntry->m_isStartOfIntra && keyPic.gopEntry->m_gopNum == 0 && keyPic.poc < m_pcEncCfg->m_GOPSize && m_rcap.accumTargetBits * (int64_t)intraP < iFrmBC)
1936
0
    {
1937
0
      m_rcap.accumTargetBits = (iFrmBC + (intraP >> 1)) / intraP;
1938
0
    }
1939
0
    m_rcap.nonRateCapEstim = 1.0;     // changed in case of capping
1940
0
    m_rcap.gopAdaptedQPAdj = 0;       // changed in first GOP of scene
1941
1942
0
    const int  gopQP = (iFrmQP + MAX_QP + 1) >> 1;
1943
0
    const double fac = double(m_pcEncCfg->m_FrameScale * intraP) / m_pcEncCfg->m_FrameRate;
1944
0
    const double mBC = (m_pcEncCfg->m_RCMaxBitrate > 0 && m_pcEncCfg->m_RCMaxBitrate != INT32_MAX ? m_pcEncCfg->m_RCMaxBitrate * fac : 0.0);
1945
1946
0
    if (mBC > 0.0 && iFrmBC + bFrmBC_final > mBC) // max. I-period bit-count exceeded
1947
0
    {
1948
0
      m_rcap.nonRateCapEstim = double(iFrmBC + bFrmBC_final) / mBC;
1949
0
      const double d = std::max(0, gopQP) + (105.0 / 128.0) * sqrt((double)std::max(1, gopQP)) * log(m_rcap.nonRateCapEstim) / log(2.0);
1950
1951
0
      dQP += Clip3(0, MAX_QP, int(0.5 + d + 0.5 * std::max(0.0, qpStart - d))) - std::max(0, gopQP);
1952
0
    }
1953
0
  }
1954
1955
  // assign dQP to pictures 
1956
2.98k
  for (auto picItr = picListBegin; picItr != picList.end(); ++picItr)
1957
1.49k
  {
1958
1.49k
    auto pic = (*picItr);
1959
1.49k
    if( pic->gopEntry->m_gopNum == gopNum )
1960
1.49k
    {
1961
1.49k
      pic->gopAdaptedQP = dQP;
1962
1.49k
      pic->force2ndOrder = f2O;
1963
1.49k
    }
1964
1.49k
    if( pic == &keyPic && nextKeyPicAfterIDR ) // consider a virtual GOP containing only one IDR pic
1965
0
      break;
1966
1.49k
  }
1967
1968
1.49k
  keyPic.gopAdaptedQP = dQP; // TODO: add any additional key-frame offset here
1969
1.49k
  keyPic.force2ndOrder = f2O;
1970
1.49k
  if( m_pcEncCfg->m_disableForce2ndOderFilter )
1971
0
  {
1972
0
    keyPic.force2ndOrder = false;
1973
0
  }
1974
1975
1.49k
  if(m_pcEncCfg->m_rateCap)
1976
0
  {
1977
    // enable QP adjustment after coded Intra in the first GOP or on a scene cut
1978
    // NOTE: on some scene cuts, in case of low motion activity, targetBits equals zero (QPA)
1979
0
    if(m_rcap.accumGopCounter == 0 && m_rcap.accumTargetBits > 0 && !nextKeyPicAfterIDR)
1980
0
    {
1981
0
      for(auto picItr = picListBegin; picItr != picList.end(); ++picItr)
1982
0
      {
1983
0
        auto pic = (*picItr);
1984
        // just on the next picture in decoding order after start of GOP
1985
0
        if(pic->gopEntry->m_gopNum == gopNum && !pic->gopEntry->m_isStartOfGop)
1986
0
        {
1987
0
          pic->isSceneCutCheckAdjQP = true;
1988
0
          break;
1989
0
        }
1990
0
      }
1991
0
      for(auto picItr = picListBegin; picItr != picList.end(); ++picItr)
1992
0
      {
1993
0
        auto pic = (*picItr);
1994
0
        if(pic->gopEntry->m_gopNum == gopNum && !pic->gopEntry->m_isStartOfGop && !pic->isSceneCutCheckAdjQP)
1995
0
        {
1996
0
          pic->isSceneCutGOP = true;
1997
0
        }
1998
0
      }
1999
0
    }
2000
0
    m_rcap.accumGopCounter++;
2001
0
  }
2002
1.49k
}
2003
2004
void EncGOP::xInitFirstSlice( Picture& pic, const PicList& picList, bool isEncodeLtRef )
2005
1.49k
{
2006
1.49k
  memset( pic.cs->alfAps, 0, sizeof(pic.cs->alfAps));
2007
2008
1.49k
  const int curPoc          = pic.getPOC();
2009
1.49k
  Slice* slice              = pic.allocateNewSlice();
2010
1.49k
  pic.cs->picHeader         = new PicHeader;
2011
1.49k
  const SPS& sps            = *(slice->sps);
2012
1.49k
  vvencNalUnitType naluType = xGetNalUnitType( pic.gopEntry );
2013
1.49k
  const GOPEntry& gopEntry  = *pic.gopEntry;
2014
1.49k
  SliceType sliceType       = gopEntry.m_sliceType == 'B' ? VVENC_B_SLICE : ( gopEntry.m_sliceType == 'P' ? VVENC_P_SLICE : VVENC_I_SLICE );
2015
2016
  // correct slice type at start of intra period
2017
1.49k
  if( gopEntry.m_isStartOfIntra )
2018
1.49k
  {
2019
1.49k
    sliceType = VVENC_I_SLICE;
2020
1.49k
  }
2021
2022
  // update IRAP
2023
1.49k
  if( naluType == VVENC_NAL_UNIT_CODED_SLICE_IDR_W_RADL
2024
0
      || naluType == VVENC_NAL_UNIT_CODED_SLICE_IDR_N_LP
2025
0
      || naluType == VVENC_NAL_UNIT_CODED_SLICE_CRA )
2026
1.49k
  {
2027
1.49k
    m_associatedIRAPType = naluType;
2028
1.49k
    m_associatedIRAPPOC  = curPoc;
2029
1.49k
  }
2030
2031
  // update last IDR
2032
1.49k
  if( naluType == VVENC_NAL_UNIT_CODED_SLICE_IDR_W_RADL || naluType == VVENC_NAL_UNIT_CODED_SLICE_IDR_N_LP )
2033
1.49k
  {
2034
1.49k
    m_lastIDR = curPoc;
2035
1.49k
  }
2036
2037
1.49k
  slice->picHeader                 = pic.cs->picHeader;
2038
1.49k
  slice->independentSliceIdx       = 0;
2039
1.49k
  slice->sliceType                 = sliceType;
2040
1.49k
  slice->poc                       = curPoc;
2041
1.49k
  slice->TLayer                    = gopEntry.m_temporalId;
2042
1.49k
  slice->nalUnitType               = naluType;
2043
1.49k
  slice->lastIDR                   = m_lastIDR;
2044
1.49k
  slice->depQuantEnabled           = m_pcEncCfg->m_DepQuantEnabled;
2045
1.49k
  slice->signDataHidingEnabled     = m_pcEncCfg->m_SignDataHidingEnabled;
2046
2047
1.49k
  slice->picHeader->splitConsOverride = false;
2048
5.97k
  for( int i = 0; i < 3; i++ )
2049
4.47k
  {
2050
4.47k
    slice->picHeader->minQTSize[i]   = sps.minQTSize[i];
2051
4.47k
    slice->picHeader->maxMTTDepth[i] = sps.maxMTTDepth[i];
2052
4.47k
    slice->picHeader->maxBTSize[i]   = sps.maxBTSize[i];
2053
4.47k
    slice->picHeader->maxTTSize[i]   = sps.maxTTSize[i];
2054
4.47k
    if( ( i == 1 ) && ( m_pcEncCfg->m_maxMTTDepth >= 10 ) )
2055
0
    {
2056
0
      slice->picHeader->maxMTTDepth[i]    = int( m_pcEncCfg->m_maxMTTDepth / pow( 10, sps.maxTLayers - slice->TLayer - 1 ) ) % 10;
2057
0
      slice->picHeader->splitConsOverride = slice->picHeader->maxMTTDepth[i] != sps.maxMTTDepth[i];
2058
0
    }
2059
4.47k
  }
2060
2061
1.49k
  slice->associatedIRAPType        = m_associatedIRAPType;
2062
1.49k
  slice->associatedIRAP            = m_associatedIRAPPOC;
2063
1.49k
  CHECK( MAX_REF_PICS <= gopEntry.m_numRefPicsActive[ 0 ], "number of ref pics out of supported range" );
2064
1.49k
  CHECK( MAX_REF_PICS <= gopEntry.m_numRefPicsActive[ 1 ], "number of ref pics out of supported range" );
2065
1.49k
  slice->numRefIdx[REF_PIC_LIST_0] = gopEntry.m_numRefPicsActive[ 0 ];
2066
1.49k
  slice->numRefIdx[REF_PIC_LIST_1] = gopEntry.m_numRefPicsActive[ 1 ];
2067
1.49k
  slice->setDecodingRefreshMarking ( m_pocCRA, m_bRefreshPending, picList );
2068
1.49k
  slice->setDefaultClpRng          ( sps );
2069
2070
  // reference list
2071
1.49k
  xSelectReferencePictureList( slice );
2072
1.49k
  int missingPoc;
2073
1.49k
  int ipc = ( m_pcEncCfg->m_DecodingRefreshType == VVENC_DRT_IDR_NO_RADL ) ? m_pcEncCfg->m_IntraPeriod : 0;
2074
1.49k
  if ( slice->isRplPicMissing( picList, REF_PIC_LIST_0, missingPoc, ipc ) || slice->isRplPicMissing( picList, REF_PIC_LIST_1, missingPoc, ipc ) )
2075
0
  {
2076
0
    slice->createExplicitReferencePictureSetFromReference( picList, slice->rpl[0], slice->rpl[1], ipc );
2077
0
  }
2078
1.49k
  slice->applyReferencePictureListBasedMarking( picList, slice->rpl[0], slice->rpl[1], 0, *slice->pps, m_pcEncCfg->m_numThreads == 0 );
2079
2080
  // nalu type refinement
2081
1.49k
  if ( xIsSliceTemporalSwitchingPoint( slice, picList ) )
2082
0
  {
2083
0
    naluType = VVENC_NAL_UNIT_CODED_SLICE_STSA;
2084
0
    slice->nalUnitType = naluType;
2085
0
  }
2086
2087
1.49k
  const int maxTLayer  = m_pcEncCfg->m_picReordering && m_pcEncCfg->m_GOPSize > 1 ? vvenc::ceilLog2( m_pcEncCfg->m_GOPSize ) : 0;
2088
1.49k
  const int numRefCode = pic.useNumRefs ? m_pcEncCfg->m_numRefPicsSCC : m_pcEncCfg->m_numRefPics;
2089
1.49k
  const int tLayer     = slice->TLayer;
2090
1.49k
  const int numRefs    = numRefCode < 10 ? numRefCode : ( int( numRefCode / pow( 10, maxTLayer - tLayer ) ) % 10 );
2091
2092
  // reference list
2093
1.49k
  slice->numRefIdx[REF_PIC_LIST_0] = sliceType == VVENC_I_SLICE ? 0 : ( numRefs ? std::min( numRefs, slice->rpl[0]->numberOfActivePictures ) : slice->rpl[0]->numberOfActivePictures );
2094
1.49k
  slice->numRefIdx[REF_PIC_LIST_1] = sliceType != VVENC_B_SLICE ? 0 : ( numRefs ? std::min( numRefs, slice->rpl[1]->numberOfActivePictures ) : slice->rpl[1]->numberOfActivePictures );
2095
1.49k
  slice->constructRefPicList  ( picList, false, m_pcEncCfg->m_numThreads == 0 );
2096
2097
1.49k
  slice->setRefPOCList        ();
2098
1.49k
  slice->setList1IdxToList0Idx();
2099
1.49k
  slice->updateRefPicCounter  ( +1 );
2100
1.49k
  slice->setSMVDParam();
2101
2102
  // slice type refinement
2103
1.49k
  if ( sliceType == VVENC_B_SLICE && slice->numRefIdx[ REF_PIC_LIST_1 ] == 0 )
2104
0
  {
2105
0
    sliceType = VVENC_P_SLICE;
2106
0
    slice->sliceType = sliceType;
2107
0
  }
2108
2109
1.49k
  slice->picHeader->gdrPic      = false;
2110
1.49k
  slice->picHeader->disBdofFlag = false;
2111
1.49k
  slice->picHeader->disDmvrFlag = false;
2112
1.49k
  slice->picHeader->disProfFlag = false;
2113
2114
1.49k
  slice->picHeader->gdrOrIrapPic = slice->picHeader->gdrPic || slice->isIRAP();
2115
1.49k
  slice->picHeader->picInterSliceAllowed = sliceType != VVENC_I_SLICE;
2116
1.49k
  slice->picHeader->picIntraSliceAllowed = sliceType == VVENC_I_SLICE;
2117
2118
1.49k
  slice->deblockingFilterOverride = sliceType != VVENC_I_SLICE && (gopEntry.m_betaOffsetDiv2 || gopEntry.m_tcOffsetDiv2);
2119
2120
1.49k
  if( m_pcEncCfg->m_deblockLastTLayers > 0 && slice->TLayer <= m_pcEncCfg->m_maxTLayer - m_pcEncCfg->m_deblockLastTLayers )
2121
0
  {
2122
0
    slice->deblockingFilterOverride = true;
2123
0
    slice->deblockingFilterDisable  = true;
2124
0
  }
2125
2126
1.49k
  if( slice->deblockingFilterOverride )
2127
0
  {
2128
0
    for( int comp = 0; comp < MAX_NUM_COMP; comp++)
2129
0
    {
2130
      //TODO: gopEntry.m_tcOffsetDiv2 and gopEntry.m_betaOffsetDiv2 are set with the luma value also for the chroma components (currently not used or all values are equal)
2131
0
      slice->deblockingFilterTcOffsetDiv2[comp]    = slice->picHeader->deblockingFilterTcOffsetDiv2[comp]   = gopEntry.m_tcOffsetDiv2   + m_pcEncCfg->m_loopFilterTcOffsetDiv2[comp];
2132
0
      slice->deblockingFilterBetaOffsetDiv2[comp]  = slice->picHeader->deblockingFilterBetaOffsetDiv2[comp] = gopEntry.m_betaOffsetDiv2 +   m_pcEncCfg->m_loopFilterBetaOffsetDiv2[comp];
2133
0
    }
2134
0
  }
2135
1.49k
  else
2136
1.49k
  {
2137
5.97k
    for( int comp = 0; comp < MAX_NUM_COMP; comp++)
2138
4.47k
    {
2139
4.47k
      slice->deblockingFilterTcOffsetDiv2[comp]    = slice->picHeader->deblockingFilterTcOffsetDiv2[comp]   = slice->pps->deblockingFilterTcOffsetDiv2[comp];
2140
4.47k
      slice->deblockingFilterBetaOffsetDiv2[comp]  = slice->picHeader->deblockingFilterBetaOffsetDiv2[comp] = slice->pps->deblockingFilterBetaOffsetDiv2[comp];
2141
4.47k
    }
2142
1.49k
  }
2143
2144
1.49k
  if (slice->pps->useDQP)
2145
1.49k
  {
2146
1.49k
    const uint32_t cuLumaQpSubdiv = (m_pcEncCfg->m_cuQpDeltaSubdiv > 0 ? (uint32_t) m_pcEncCfg->m_cuQpDeltaSubdiv : 0);
2147
2148
1.49k
    slice->picHeader->cuQpDeltaSubdivInter = m_pcEncCfg->m_usePerceptQPA ? 0 : cuLumaQpSubdiv;
2149
1.49k
    slice->picHeader->cuQpDeltaSubdivIntra = cuLumaQpSubdiv;
2150
1.49k
  }
2151
1.49k
  if( slice->pps->chromaQpOffsetListLen > 0)
2152
0
  {
2153
0
    const uint32_t cuChromaQpSubdiv = (m_pcEncCfg->m_cuChromaQpOffsetSubdiv > 0 ? (uint32_t) m_pcEncCfg->m_cuChromaQpOffsetSubdiv : 0);
2154
2155
0
    slice->picHeader->cuChromaQpOffsetSubdivInter = m_pcEncCfg->m_usePerceptQPA ? 0 : cuChromaQpSubdiv;
2156
0
    slice->picHeader->cuChromaQpOffsetSubdivIntra = cuChromaQpSubdiv;
2157
0
  }
2158
2159
1.49k
  slice->picHeader->ppsId = slice->pps->ppsId;
2160
1.49k
  slice->picHeader->spsId = slice->sps->spsId;
2161
2162
1.49k
  pic.cs->picHeader->pic = &pic;
2163
1.49k
  xInitSliceTMVPFlag ( pic.cs->picHeader, slice );
2164
1.49k
  xInitSliceMvdL1Zero( pic.cs->picHeader, slice );
2165
1.49k
  slice->picHeader->maxNumAffineMergeCand = sps.Affine ? sps.maxNumAffineMergeCand : ( sps.SbtMvp && slice->picHeader->enableTMVP ? 1 : 0 );
2166
2167
1.49k
  if( slice->nalUnitType == VVENC_NAL_UNIT_CODED_SLICE_RASL && m_pcEncCfg->m_rprRASLtoolSwitch )
2168
0
  {
2169
0
    slice->lmChromaCheckDisable = true;
2170
0
    pic.cs->picHeader->disDmvrFlag = true;
2171
0
    xUpdateRPRtmvp( pic.cs->picHeader, slice );
2172
0
  }
2173
2174
  // update RAS
2175
1.49k
  xUpdateRasInit( slice );
2176
2177
1.49k
  if( m_pcEncCfg->m_useAMaxBT )
2178
0
  {
2179
0
    m_BlkStat.setSliceMaxBT( *slice );
2180
0
  }
2181
2182
1.49k
  {
2183
1.49k
    bool identicalToSPS=true;
2184
1.49k
    const SPS* sps =slice->sps;
2185
1.49k
    PicHeader* picHeader = slice->picHeader;
2186
1.49k
    if (picHeader->picInterSliceAllowed)
2187
0
    {
2188
0
      identicalToSPS = (picHeader->minQTSize[1] == sps->minQTSize[1] &&
2189
0
                        picHeader->maxMTTDepth[1] == sps->maxMTTDepth[1] &&
2190
0
                        picHeader->maxBTSize[1] == sps->maxBTSize[1] &&
2191
0
                        picHeader->maxTTSize[1] == sps->maxTTSize[1] );
2192
0
    }
2193
2194
1.49k
    if (identicalToSPS && picHeader->picIntraSliceAllowed)
2195
1.49k
    {
2196
1.49k
      identicalToSPS = (picHeader->minQTSize[0] == sps->minQTSize[0] &&
2197
1.49k
                        picHeader->maxMTTDepth[0] == sps->maxMTTDepth[0] &&
2198
1.49k
                        picHeader->maxBTSize[0] == sps->maxBTSize[0] &&
2199
1.49k
                        picHeader->maxTTSize[0] == sps->maxTTSize[0] );
2200
1.49k
    }
2201
2202
1.49k
    if (identicalToSPS && sps->dualITree)
2203
1.49k
    {
2204
1.49k
      identicalToSPS = (picHeader->minQTSize[2] == sps->minQTSize[2] &&
2205
1.49k
                        picHeader->maxMTTDepth[2] == sps->maxMTTDepth[2] &&
2206
1.49k
                        picHeader->maxBTSize[2] == sps->maxBTSize[2] &&
2207
1.49k
                        picHeader->maxTTSize[2] == sps->maxTTSize[2] );
2208
1.49k
    }
2209
2210
1.49k
    if (identicalToSPS)
2211
1.49k
    {
2212
1.49k
      picHeader->splitConsOverride = false;
2213
1.49k
    }
2214
2215
1.49k
  }
2216
2217
1.49k
  CHECK( slice->TLayer != 0 && slice->sliceType == VVENC_I_SLICE, "Unspecified error" );
2218
2219
1.49k
  pic.cs->slice = slice;
2220
1.49k
  pic.isReferenced = true;
2221
2222
1.49k
  pic.picApsMap.clearActive();
2223
1.49k
  pic.picApsMap.setApsIdStart( ALF_CTB_MAX_NUM_APS );
2224
13.4k
  for ( int i = 0; i < ALF_CTB_MAX_NUM_APS; i++ )
2225
11.9k
  {
2226
11.9k
    const int apsMapIdx = ( i << NUM_APS_TYPE_LEN ) + ALF_APS;
2227
11.9k
    APS* alfAPS = pic.picApsMap.getPS( apsMapIdx );
2228
11.9k
    if ( alfAPS )
2229
0
    {
2230
0
      alfAPS->apsId      = MAX_UINT;
2231
0
      alfAPS->temporalId = MAX_INT;
2232
0
      alfAPS->poc        = MAX_INT;
2233
0
      pic.picApsMap.clearChangedFlag( apsMapIdx );
2234
0
      alfAPS->alfParam.reset();
2235
0
      alfAPS->ccAlfParam.reset();
2236
0
    }
2237
11.9k
  }
2238
1.49k
  CHECK( slice->enableDRAPSEI && m_pcEncCfg->m_maxParallelFrames, "Dependent Random Access Point is not supported by Frame Parallel Processing" );
2239
2240
1.49k
  pic.isInitDone = true;
2241
1.49k
}
2242
2243
void EncGOP::xInitSliceTMVPFlag( PicHeader* picHeader, const Slice* slice )
2244
1.49k
{
2245
1.49k
  if( m_pcEncCfg->m_TMVPModeId == 2 )
2246
0
  {
2247
0
    const GOPEntry& gopEntry = *(slice->pic->gopEntry);
2248
0
    picHeader->enableTMVP    = ! gopEntry.m_useBckwdOnly;
2249
0
  }
2250
1.49k
  else if( m_pcEncCfg->m_TMVPModeId == 1 )
2251
1.49k
  {
2252
1.49k
    picHeader->enableTMVP = true;
2253
1.49k
  }
2254
0
  else
2255
0
  {
2256
0
    picHeader->enableTMVP = false;
2257
0
  }
2258
2259
  // disable TMVP when current picture is the only ref picture
2260
1.49k
  if( slice->isIRAP() && slice->sps->IBC )
2261
1.49k
  {
2262
1.49k
    picHeader->enableTMVP = false;
2263
1.49k
  }
2264
1.49k
}
2265
2266
void EncGOP::xUpdateRPRtmvp( PicHeader* picHeader, Slice* slice )
2267
0
{
2268
0
  if( slice->sliceType != VVENC_I_SLICE && picHeader->enableTMVP && m_pcEncCfg->m_rprRASLtoolSwitch )
2269
0
  {
2270
0
    int colRefIdxL0 = -1, colRefIdxL1 = -1;
2271
2272
0
    for( int refIdx = 0; refIdx < slice->numRefIdx[REF_PIC_LIST_0]; refIdx++ )
2273
0
    {
2274
0
      if( !( slice->getRefPic( REF_PIC_LIST_0, refIdx )->slices[0]->nalUnitType != VVENC_NAL_UNIT_CODED_SLICE_RASL &&
2275
0
             slice->getRefPic( REF_PIC_LIST_0, refIdx )->poc <= m_pocCRA ) )
2276
0
      {
2277
0
        colRefIdxL0 = refIdx;
2278
0
        break;
2279
0
      }
2280
0
    }
2281
2282
0
    if( slice->sliceType == VVENC_B_SLICE )
2283
0
    {
2284
0
      for( int refIdx = 0; refIdx < slice->numRefIdx[REF_PIC_LIST_1]; refIdx++ )
2285
0
      {
2286
0
        if( !( slice->getRefPic( REF_PIC_LIST_1, refIdx )->slices[0]->nalUnitType != VVENC_NAL_UNIT_CODED_SLICE_RASL &&
2287
0
               slice->getRefPic( REF_PIC_LIST_1, refIdx )->poc <= m_pocCRA ) )
2288
0
        {
2289
0
          colRefIdxL1 = refIdx;
2290
0
          break;
2291
0
        }
2292
0
      }
2293
0
    }
2294
2295
0
    if( colRefIdxL0 >= 0 && colRefIdxL1 >= 0 )
2296
0
    {
2297
0
      const Picture *refPicL0 = slice->getRefPic( REF_PIC_LIST_0, colRefIdxL0 );
2298
0
      const Picture *refPicL1 = slice->getRefPic( REF_PIC_LIST_1, colRefIdxL1 );
2299
2300
0
      CHECK( !refPicL0->slices.size(), "Wrong L0 reference picture" );
2301
0
      CHECK( !refPicL1->slices.size(), "Wrong L1 reference picture" );
2302
2303
0
      const uint32_t uiColFromL0 = refPicL0->slices[0]->sliceQp > refPicL1->slices[0]->sliceQp;
2304
0
      picHeader->picColFromL0 = uiColFromL0;
2305
0
      slice->colFromL0Flag = uiColFromL0;
2306
0
      slice->colRefIdx = uiColFromL0 ? colRefIdxL0 : colRefIdxL1;
2307
0
      picHeader->colRefIdx = uiColFromL0 ? colRefIdxL0 : colRefIdxL1;
2308
0
    }
2309
0
    else if( colRefIdxL0 < 0 && colRefIdxL1 >= 0 )
2310
0
    {
2311
0
      picHeader->picColFromL0 = false;
2312
0
      slice->colFromL0Flag = false;
2313
0
      slice->colRefIdx = colRefIdxL1;
2314
0
      picHeader->colRefIdx = colRefIdxL1;
2315
0
    }
2316
0
    else if( colRefIdxL0 >= 0 && colRefIdxL1 < 0 )
2317
0
    {
2318
0
      picHeader->picColFromL0 = true;
2319
0
      slice->colFromL0Flag = true;
2320
0
      slice->colRefIdx = colRefIdxL0;
2321
0
      picHeader->colRefIdx = colRefIdxL0;
2322
0
    }
2323
0
    else
2324
0
    {
2325
0
      picHeader->enableTMVP = false;
2326
0
    }
2327
0
  }
2328
0
}
2329
2330
void EncGOP::xInitSliceMvdL1Zero( PicHeader* picHeader, const Slice* slice )
2331
1.49k
{
2332
1.49k
  bool bGPBcheck = false;
2333
1.49k
  if ( slice->sliceType == VVENC_B_SLICE)
2334
0
  {
2335
0
    if ( slice->numRefIdx[ 0 ] == slice->numRefIdx[ 1 ] )
2336
0
    {
2337
0
      bGPBcheck = true;
2338
0
      int i;
2339
0
      for ( i=0; i < slice->numRefIdx[ 1 ]; i++ )
2340
0
      {
2341
0
        if ( slice->getRefPOC( RefPicList( 1 ), i ) != slice->getRefPOC( RefPicList( 0 ), i ) )
2342
0
        {
2343
0
          bGPBcheck = false;
2344
0
          break;
2345
0
        }
2346
0
      }
2347
0
    }
2348
0
  }
2349
2350
1.49k
  if ( bGPBcheck )
2351
0
  {
2352
0
    picHeader->mvdL1Zero = true;
2353
0
  }
2354
1.49k
  else
2355
1.49k
  {
2356
1.49k
    picHeader->mvdL1Zero = false;
2357
1.49k
  }
2358
1.49k
}
2359
2360
void EncGOP::xSelectReferencePictureList( Slice* slice ) const
2361
1.49k
{
2362
1.49k
  const GOPEntry& gopEntry = *(slice->pic->gopEntry);
2363
2364
4.47k
  for( int l = 0; l < 2; l++ )
2365
2.98k
  {
2366
2.98k
    slice->rplIdx[ l ] = gopEntry.m_defaultRPLIdx;
2367
2.98k
    if( slice->rplIdx[ l ] >= 0 )
2368
2.98k
    {
2369
2.98k
      slice->rpl[ l ] = &(slice->sps->rplList[ l ][ slice->rplIdx[ l ] ]);
2370
2.98k
    }
2371
0
    else
2372
0
    {
2373
0
      slice->rplLocal[ l ].initFromGopEntry( gopEntry, l );
2374
0
      slice->rpl[ l ] = &slice->rplLocal[ l ];
2375
0
    }
2376
2.98k
  }
2377
1.49k
}
2378
2379
void EncGOP::xWritePicture( Picture& pic, AccessUnitList& au, bool isEncodeLtRef )
2380
1.49k
{
2381
  // first pass temporal down-sampling
2382
1.49k
  if( ( ! m_pcRateCtrl->rcIsFinalPass || m_isPreAnalysis ) && pic.gopEntry->m_skipFirstPass )
2383
0
  {
2384
0
    m_pcRateCtrl->addRCPassStats( pic.cs->slice->poc,
2385
0
        0,                /* qp */
2386
0
        0,                /* lambda */
2387
0
        pic.picVA.visAct,
2388
0
        0,                /* numBits */
2389
0
        0,                /* psnrY */
2390
0
        pic.cs->slice->isIntra(),
2391
0
        pic.cs->slice->TLayer,
2392
0
        pic.gopEntry->m_isStartOfIntra,
2393
0
        pic.gopEntry->m_isStartOfGop,
2394
0
        pic.gopEntry->m_gopNum,
2395
0
        pic.gopEntry->m_scType,
2396
0
        pic.picVA.spatAct[CH_L],
2397
0
        pic.m_picShared->m_picMotEstError,
2398
0
        pic.m_picShared->m_minNoiseLevels );
2399
0
    return;
2400
0
  }
2401
2402
1.49k
  DTRACE_UPDATE( g_trace_ctx, std::make_pair( "bsfinal", 1 ) );
2403
1.49k
  pic.encTime.startTimer();
2404
2405
1.49k
  au.poc           = pic.poc;
2406
1.49k
  au.temporalLayer = pic.TLayer;
2407
1.49k
  au.refPic        = pic.isReferenced;
2408
1.49k
  au.userData      = pic.userData;
2409
1.49k
  if( ! pic.slices.empty() )
2410
1.49k
  {
2411
1.49k
    au.sliceType = pic.slices[ 0 ]->sliceType;
2412
1.49k
  }
2413
2414
1.49k
  if( pic.ctsValid )
2415
1.49k
  {
2416
1.49k
    const int64_t iDiffFrames = m_numPicsCoded - pic.poc - pic.picsInMissing;
2417
1.49k
    au.cts      = pic.cts;
2418
1.49k
    au.ctsValid = pic.ctsValid;
2419
1.49k
    if ( pic.picOutOffset )
2420
0
    {
2421
0
      m_numPicsOutOffset += pic.picOutOffset;
2422
0
    }
2423
1.49k
    if( m_pcEncCfg->m_TicksPerSecond > 0 )
2424
1.49k
      au.dts      = ( ( iDiffFrames - m_pcEncCfg->m_maxTLayer + m_numPicsOutOffset ) * m_ticksPerFrameMul4 ) / 4 + au.cts;
2425
0
    else
2426
0
      au.dts      = ( ( iDiffFrames - m_pcEncCfg->m_maxTLayer + m_numPicsOutOffset )) + au.cts;
2427
1.49k
    au.dtsValid = pic.ctsValid;
2428
1.49k
  }
2429
2430
1.49k
  pic.actualTotalBits += xWriteParameterSets( pic, au, m_HLSWriter );
2431
1.49k
  xWriteLeadingSEIs( pic, au );
2432
1.49k
  pic.actualTotalBits += xWritePictureSlices( pic, au, m_HLSWriter );
2433
2434
1.49k
  pic.encTime.stopTimer();
2435
2436
1.49k
  std::string digestStr;
2437
1.49k
  xWriteTrailingSEIs( pic, au, digestStr );
2438
1.49k
  xPrintPictureInfo ( pic, au, digestStr, m_pcEncCfg->m_printFrameMSE, isEncodeLtRef );
2439
1.49k
  DTRACE_UPDATE( g_trace_ctx, std::make_pair( "bsfinal", 0 ) );
2440
1.49k
}
2441
2442
int EncGOP::xWriteParameterSets( Picture& pic, AccessUnitList& accessUnit, HLSWriter& hlsWriter )
2443
1.49k
{
2444
1.49k
  Slice* slice        = pic.slices[0];
2445
1.49k
  const SPS& sps      = *(slice->sps);
2446
1.49k
  const PPS& pps      = *(slice->pps);
2447
1.49k
  int actualTotalBits = 0;
2448
2449
1.49k
  if ( m_bFirstWrite || ( m_pcEncCfg->m_rewriteParamSets && slice->isIRAP() ) )
2450
1.49k
  {
2451
1.49k
    if (slice->sps->vpsId != 0)
2452
0
    {
2453
0
      actualTotalBits += xWriteVPS( accessUnit, pic.vps, hlsWriter );
2454
0
    }
2455
1.49k
    actualTotalBits += xWriteDCI( accessUnit, pic.dci, hlsWriter );
2456
1.49k
    actualTotalBits += xWriteSPS( accessUnit, &sps, hlsWriter );
2457
1.49k
    actualTotalBits += xWritePPS( accessUnit, &pps, &sps, hlsWriter );
2458
1.49k
    m_bFirstWrite = false;
2459
1.49k
  }
2460
2461
1.49k
  bool IrapOrGdrAu = slice->picHeader->gdrPic || (slice->isIRAP() && !slice->pps->mixedNaluTypesInPic);
2462
1.49k
  if ((( slice->vps->maxLayers > 1 && IrapOrGdrAu) || m_pcEncCfg->m_AccessUnitDelimiter) && !slice->nuhLayerId )
2463
0
  {
2464
0
    xWriteAccessUnitDelimiter( accessUnit, slice, IrapOrGdrAu, hlsWriter );
2465
0
  }
2466
2467
  // send ALF APS
2468
1.49k
  if ( sps.alfEnabled && (slice->alfEnabled[COMP_Y] || slice->ccAlfCbEnabled || slice->ccAlfCrEnabled ))
2469
0
  {
2470
0
    for ( int apsId = 0; apsId < ALF_CTB_MAX_NUM_APS; apsId++ )
2471
0
    {
2472
0
      ParameterSetMap<APS>& apsMap = pic.picApsMap;
2473
0
      const int apsMapIdx          = ( apsId << NUM_APS_TYPE_LEN ) + ALF_APS;
2474
0
      APS* aps                     = apsMap.getPS( apsMapIdx );
2475
0
      bool writeAps                = aps && apsMap.getChangedFlag( apsMapIdx );
2476
0
      if ( writeAps )
2477
0
      {
2478
0
        aps->chromaPresent = slice->sps->chromaFormatIdc != CHROMA_400;
2479
0
        aps->temporalId = slice->TLayer;
2480
0
        actualTotalBits += xWriteAPS( accessUnit, aps, hlsWriter, VVENC_NAL_UNIT_PREFIX_APS );
2481
0
        apsMap.clearChangedFlag( apsMapIdx );
2482
0
      }
2483
0
    }
2484
0
  }
2485
2486
1.49k
  return actualTotalBits;
2487
1.49k
}
2488
2489
int EncGOP::xWritePictureSlices( Picture& pic, AccessUnitList& accessUnit, HLSWriter& hlsWriter )
2490
1.49k
{
2491
1.49k
  Slice* slice        = pic.slices[ 0 ];
2492
1.49k
  const int numSlices = (int)( pic.slices.size() );
2493
1.49k
  unsigned  numBytes  = 0;
2494
2495
2.98k
  for ( int sliceIdx = 0; sliceIdx < numSlices; sliceIdx++ )
2496
1.49k
  {
2497
1.49k
    slice = pic.slices[ sliceIdx ];
2498
2499
1.49k
    if ( sliceIdx > 0 && slice->sliceType != VVENC_I_SLICE )
2500
0
    {
2501
0
      slice->checkColRefIdx( sliceIdx, &pic );
2502
0
    }
2503
2504
    // start slice NALUnit
2505
1.49k
    OutputNALUnit nalu( slice->nalUnitType, slice->TLayer );
2506
1.49k
    hlsWriter.setBitstream( &nalu.m_Bitstream );
2507
2508
    // slice header and data
2509
1.49k
    int bitsBeforeWriting = hlsWriter.getNumberOfWrittenBits();
2510
1.49k
    hlsWriter.codeSliceHeader( slice );
2511
1.49k
    pic.actualHeadBits += ( hlsWriter.getNumberOfWrittenBits() - bitsBeforeWriting );
2512
1.49k
    hlsWriter.codeTilesWPPEntryPoint( slice );
2513
1.49k
    xAttachSliceDataToNalUnit( nalu, &pic.sliceDataStreams[ sliceIdx ] );
2514
2515
1.49k
    accessUnit.push_back( new NALUnitEBSP( nalu ) );
2516
1.49k
    numBytes += unsigned( accessUnit.back()->m_nalUnitData.str().size() );
2517
1.49k
  }
2518
2519
1.49k
  xCabacZeroWordPadding( pic, slice, pic.sliceDataNumBins, numBytes, accessUnit.back()->m_nalUnitData );
2520
2521
1.49k
  return numBytes * 8;
2522
1.49k
}
2523
2524
// True when the two film grain characteristics SEI messages would be written with the same syntax element values.
2525
static bool sameFilmGrainCharacteristics( const SeiFgc& a, const SeiFgc& b )
2526
0
{
2527
0
  if( a.fgcCancelFlag || b.fgcCancelFlag )
2528
0
  {
2529
0
    return a.fgcCancelFlag == b.fgcCancelFlag;
2530
0
  }
2531
0
  if( a.filmGrainModelId != b.filmGrainModelId || a.separateColourDescriptionPresent != b.separateColourDescriptionPresent
2532
0
      || a.blendingModeId != b.blendingModeId || a.log2ScaleFactor != b.log2ScaleFactor || a.fgcPersistenceFlag != b.fgcPersistenceFlag )
2533
0
  {
2534
0
    return false;
2535
0
  }
2536
0
  if( a.separateColourDescriptionPresent
2537
0
      && ( a.filmGrainBitDepthLumaMinus8 != b.filmGrainBitDepthLumaMinus8 || a.filmGrainBitDepthChromaMinus8 != b.filmGrainBitDepthChromaMinus8
2538
0
           || a.filmGrainFullRangeFlag != b.filmGrainFullRangeFlag || a.filmGrainColourPrimaries != b.filmGrainColourPrimaries
2539
0
           || a.filmGrainTransferCharacteristics != b.filmGrainTransferCharacteristics || a.filmGrainMatrixCoeffs != b.filmGrainMatrixCoeffs ) )
2540
0
  {
2541
0
    return false;
2542
0
  }
2543
0
  for( int c = 0; c < MAX_NUM_COMP; c++ )
2544
0
  {
2545
0
    const SeiFgc::CompModel& ca = a.compModel[ c ];
2546
0
    const SeiFgc::CompModel& cb = b.compModel[ c ];
2547
    // as in SEIWriter: a component is written as present only with intervals and model values
2548
0
    const bool presentA = ca.presentFlag && ca.numIntensityIntervals > 0 && ca.numModelValues > 0;
2549
0
    const bool presentB = cb.presentFlag && cb.numIntensityIntervals > 0 && cb.numModelValues > 0;
2550
0
    if( presentA != presentB )
2551
0
    {
2552
0
      return false;
2553
0
    }
2554
0
    if( !presentA )
2555
0
    {
2556
0
      continue;
2557
0
    }
2558
0
    if( ca.numIntensityIntervals != cb.numIntensityIntervals || ca.numModelValues != cb.numModelValues )
2559
0
    {
2560
0
      return false;
2561
0
    }
2562
0
    for( int i = 0; i < ca.numIntensityIntervals; i++ )
2563
0
    {
2564
0
      const SeiFgc::CompModelIntensityValues& va = ca.intensityValues[ i ];
2565
0
      const SeiFgc::CompModelIntensityValues& vb = cb.intensityValues[ i ];
2566
0
      if( va.intensityIntervalLowerBound != vb.intensityIntervalLowerBound || va.intensityIntervalUpperBound != vb.intensityIntervalUpperBound
2567
0
          || !std::equal( va.compModelValue.begin(), va.compModelValue.begin() + ca.numModelValues, vb.compModelValue.begin() ) )
2568
0
      {
2569
0
        return false;
2570
0
      }
2571
0
    }
2572
0
  }
2573
0
  return true;
2574
0
}
2575
2576
// Adds the parameter set of a finished analysis. vvdec keeps synthesizing the grain of a component that a later SEI
2577
// marks as absent (fixed by its pull request 414, open in October 2026), so once a component had grain, a parameter set
2578
// without it signals one interval with scaling factor 0 instead.
2579
void EncGOP::xAddFilmGrainParameters( const FgaTask& task )
2580
0
{
2581
0
  FilmGrainParam param = task.result;
2582
0
  for( int c = 0; c < MAX_NUM_COMP; c++ )
2583
0
  {
2584
0
    SeiFgc::CompModel& cm = param.compModel[ c ];
2585
0
    if( m_pcEncCfg->m_fga && !cm.presentFlag && m_fgcCompSignalled[ c ] )
2586
0
    {
2587
0
      cm.presentFlag           = true;
2588
0
      cm.numIntensityIntervals = 1;
2589
0
      cm.numModelValues        = 1;
2590
0
      cm.intensityValues.resize( 1 );
2591
0
      cm.intensityValues[ 0 ].intensityIntervalLowerBound = 0;
2592
0
      cm.intensityValues[ 0 ].intensityIntervalUpperBound = 255;
2593
0
      cm.intensityValues[ 0 ].compModelValue.assign( 1, 0 );
2594
0
    }
2595
0
    m_fgcCompSignalled[ c ] = m_fgcCompSignalled[ c ] || cm.presentFlag;
2596
0
  }
2597
0
  m_fgcByStartPoc.erase( m_fgcByStartPoc.lower_bound( task.startPoc ), m_fgcByStartPoc.end() );
2598
0
  m_fgcByStartPoc[ task.startPoc ] = param;
2599
0
  while( m_fgcByStartPoc.size() > FGC_PARAM_SETS )
2600
0
  {
2601
    // the oldest set is dropped; the next one then applies to all earlier pictures, all of them written long ago
2602
0
    m_fgcByStartPoc.erase( m_fgcByStartPoc.begin() );
2603
0
    FilmGrainParam oldest = m_fgcByStartPoc.begin()->second;
2604
0
    m_fgcByStartPoc.erase( m_fgcByStartPoc.begin() );
2605
0
    m_fgcByStartPoc[ std::numeric_limits<int>::min() ] = oldest;
2606
0
  }
2607
0
}
2608
2609
const FilmGrainParam& EncGOP::xFilmGrainParametersAt( int poc ) const
2610
0
{
2611
0
  return std::prev( m_fgcByStartPoc.upper_bound( poc ) )->second;   // the first set starts at the lowest int
2612
0
}
2613
2614
// The film grain SEI of a parameter set; the components the configuration marks absent stay absent.
2615
SeiFgc EncGOP::xFilmGrainSei( const FilmGrainParam& param, ChromaFormat chromaFormat )
2616
0
{
2617
0
  SeiFgc sei;
2618
0
  m_seiEncoder.initSeiFgc( &sei );
2619
0
  sei.log2ScaleFactor = param.log2ScaleFactor;
2620
0
  for( int c = 0; c < getNumberValidComponents( chromaFormat ); c++ )
2621
0
  {
2622
0
    if( sei.compModel[ c ].presentFlag )
2623
0
    {
2624
0
      sei.compModel[ c ] = param.compModel[ c ];
2625
0
    }
2626
0
  }
2627
0
  return sei;
2628
0
}
2629
2630
// A persistent film grain SEI message applies to its picture and the following pictures in output order, up to the next
2631
// picture with a message, so repeating it in every access unit (45-60 bytes, 2-3.5% of a low rate encode) is not needed:
2632
// a picture needs it when the picture before it in output order signals other parameters. Random access points, where
2633
// decoding can start, always get the message, and so does a picture with no picture before it written yet in this coded
2634
// layer video sequence (the leading pictures of an IDR, of which the first in output order is not known in coding
2635
// order). Decoding from a CRA, vvdec skips its RASL pictures but applies their messages to the next decoded picture, so
2636
// the first picture after RASL pictures with a message gets one as well.
2637
bool EncGOP::xFilmGrainSeiNeeded( const Slice& slice, const SeiFgc& sei, ChromaFormat chromaFormat )
2638
0
{
2639
0
  if( slice.isIDRorBLA() )
2640
0
  {
2641
0
    m_fgcWrittenPocs.clear();   // a new coded layer video sequence, where no earlier message persists
2642
0
  }
2643
0
  const bool rasl         = slice.nalUnitType == VVENC_NAL_UNIT_CODED_SLICE_RASL;
2644
0
  const bool randomAccess = slice.isIRAP() || slice.picHeader->gdrPic;
2645
0
  const bool firstWritten = m_fgcWrittenPocs.lower_bound( slice.poc ) == m_fgcWrittenPocs.begin();
2646
0
  const bool afterRasl    = !rasl && m_fgcRaslSeiPending;
2647
0
  const bool changed      = !sameFilmGrainCharacteristics( xFilmGrainSei( xFilmGrainParametersAt( slice.poc - 1 ), chromaFormat ), sei );
2648
0
  const bool needed       = !sei.fgcPersistenceFlag || randomAccess || firstWritten || afterRasl || changed;
2649
2650
0
  m_fgcWrittenPocs.insert( slice.poc );
2651
0
  while( m_fgcWrittenPocs.size() > FGC_SEI_HISTORY )
2652
0
  {
2653
0
    m_fgcWrittenPocs.erase( m_fgcWrittenPocs.begin() );
2654
0
  }
2655
0
  m_fgcRaslSeiPending = rasl && ( m_fgcRaslSeiPending || needed );
2656
0
  return needed;
2657
0
}
2658
2659
void EncGOP::xWriteLeadingSEIs( const Picture& pic, AccessUnitList& accessUnit )
2660
1.49k
{
2661
1.49k
  const Slice* slice = pic.slices[ 0 ];
2662
1.49k
  SEIMessages leadingSeiMessages;
2663
2664
1.49k
  bool bpPresentInAU = false;
2665
2666
1.49k
  if((m_pcEncCfg->m_bufferingPeriodSEIEnabled) && (slice->isIRAP() || slice->nalUnitType == VVENC_NAL_UNIT_CODED_SLICE_GDR) &&
2667
0
    slice->nuhLayerId==slice->vps->layerId[0] && (slice->sps->hrdParametersPresent) && m_pcRateCtrl->rcIsFinalPass && !m_isPreAnalysis )
2668
0
  {
2669
0
    SEIBufferingPeriod *bufferingPeriodSEI = new SEIBufferingPeriod();
2670
0
    bool noLeadingPictures = ( (slice->nalUnitType!= VVENC_NAL_UNIT_CODED_SLICE_IDR_W_RADL) && (slice->nalUnitType!= VVENC_NAL_UNIT_CODED_SLICE_CRA) );
2671
0
    m_seiEncoder.initBufferingPeriodSEI(*bufferingPeriodSEI, noLeadingPictures);
2672
0
    m_EncHRD.bufferingPeriodSEI = *bufferingPeriodSEI;
2673
0
    m_EncHRD.bufferingPeriodInitialized = true;
2674
2675
0
    leadingSeiMessages.push_back(bufferingPeriodSEI);
2676
0
    bpPresentInAU = true;
2677
0
  }
2678
2679
//  if (m_pcEncCfg->m_dependentRAPIndicationSEIEnabled && slice->isDRAP )
2680
//  {
2681
//    SEIDependentRAPIndication *dependentRAPIndicationSEI = new SEIDependentRAPIndication();
2682
//    m_seiEncoder.initDrapSEI( dependentRAPIndicationSEI );
2683
//    leadingSeiMessages.push_back(dependentRAPIndicationSEI);
2684
//  }
2685
2686
1.49k
  if( m_pcEncCfg->m_pictureTimingSEIEnabled && m_pcEncCfg->m_bufferingPeriodSEIEnabled && m_pcRateCtrl->rcIsFinalPass && !m_isPreAnalysis )
2687
0
  {
2688
0
    SEIMessages nestedSeiMessages;
2689
0
    SEIMessages duInfoSeiMessages;
2690
0
    uint32_t numDU = 1;
2691
0
    m_seiEncoder.initPictureTimingSEI( leadingSeiMessages, nestedSeiMessages, duInfoSeiMessages, slice, numDU, bpPresentInAU );
2692
0
  }
2693
2694
1.49k
  if( m_pcEncCfg->m_preferredTransferCharacteristics )
2695
0
  {
2696
0
    SEIAlternativeTransferCharacteristics *seiAlternativeTransferCharacteristics = new SEIAlternativeTransferCharacteristics;
2697
0
    m_seiEncoder.initSEIAlternativeTransferCharacteristics( seiAlternativeTransferCharacteristics );
2698
0
    leadingSeiMessages.push_back(seiAlternativeTransferCharacteristics);
2699
0
  }
2700
2701
  // film grain SEI
2702
1.49k
  if ( m_pcEncCfg->m_fg.m_fgcSEIEnabled && !m_pcEncCfg->m_fg.m_fgcSEIPerPictureSEI )
2703
0
  {
2704
    // Access units are written in coding order. An analysis result is added with the access unit of the analyzed
2705
    // picture, the first coded picture of its GOP, and applies from its start picture on (xAddFilmGrainParameters).
2706
0
    while( !m_fgaTasks.empty() && m_fgaTasks.front()->codingNum <= pic.gopEntry->m_codingNum )
2707
0
    {
2708
0
      m_fgaTasks.front()->counter.wait();
2709
0
      xAddFilmGrainParameters( *m_fgaTasks.front() );
2710
      // the consumed task has run, so the task after it no longer looks at its barrier once that one has run too
2711
0
      m_fgaConsumed = std::move( m_fgaTasks.front() );
2712
0
      m_fgaTasks.pop_front();
2713
0
    }
2714
2715
0
    const SeiFgc sei = xFilmGrainSei( xFilmGrainParametersAt( slice->poc ), pic.chromaFormat );
2716
0
    if( xFilmGrainSeiNeeded( *slice, sei, pic.chromaFormat ) )
2717
0
    {
2718
0
      leadingSeiMessages.push_back( new SeiFgc( sei ) );
2719
0
    }
2720
0
  }
2721
2722
  // mastering display colour volume
2723
1.49k
  if( (m_pcEncCfg->m_masteringDisplay[0] != 0 && m_pcEncCfg->m_masteringDisplay[1] != 0) ||
2724
1.49k
      m_pcEncCfg->m_masteringDisplay[8] )
2725
0
  {
2726
0
    SEIMasteringDisplayColourVolume *sei = new SEIMasteringDisplayColourVolume;
2727
0
    m_seiEncoder.initSEIMasteringDisplayColourVolume(sei);
2728
0
    leadingSeiMessages.push_back(sei);
2729
0
  }
2730
2731
  // content light level
2732
1.49k
  if( m_pcEncCfg->m_contentLightLevel[0] != 0 && m_pcEncCfg->m_contentLightLevel[1] != 0 )
2733
0
  {
2734
0
    SEIContentLightLevelInfo *seiCLL = new SEIContentLightLevelInfo;
2735
0
    m_seiEncoder.initSEIContentLightLevel(seiCLL);
2736
0
    leadingSeiMessages.push_back(seiCLL);
2737
0
  }
2738
2739
2740
  // Note: using accessUnit.end() works only as long as this function is called after slice coding and before EOS/EOB NAL units
2741
1.49k
  AccessUnitList::iterator pos = accessUnit.end();
2742
1.49k
  xWriteSEISeparately( VVENC_NAL_UNIT_PREFIX_SEI, leadingSeiMessages, accessUnit, pos, slice->TLayer, slice->sps );
2743
2744
1.49k
  deleteSEIs( leadingSeiMessages );
2745
1.49k
}
2746
2747
void EncGOP::xWriteTrailingSEIs( const Picture& pic, AccessUnitList& accessUnit, std::string& digestStr )
2748
1.49k
{
2749
1.49k
  const Slice* slice = pic.slices[ 0 ];
2750
1.49k
  SEIMessages trailingSeiMessages;
2751
2752
1.49k
  if ( m_pcEncCfg->m_decodedPictureHashSEIType != VVENC_HASHTYPE_NONE )
2753
0
  {
2754
0
    SEIDecodedPictureHash *decodedPictureHashSei = new SEIDecodedPictureHash();
2755
0
    const CPelUnitBuf recoBuf = pic.cs->getRecoBuf();
2756
0
    m_seiEncoder.initDecodedPictureHashSEI( *decodedPictureHashSei, recoBuf, digestStr, slice->sps->bitDepths );
2757
0
    if ( m_pcEncCfg->m_decodedPictureHashSEIType < VVENC_HASHTYPE_MD5_LOG )
2758
0
    {
2759
0
    trailingSeiMessages.push_back( decodedPictureHashSei );
2760
0
  }
2761
0
    else
2762
0
    {
2763
0
      delete decodedPictureHashSei;
2764
0
    }
2765
0
  }
2766
2767
  // Note: using accessUnit.end() works only as long as this function is called after slice coding and before EOS/EOB NAL units
2768
1.49k
  AccessUnitList::iterator pos = accessUnit.end();
2769
1.49k
  xWriteSEISeparately( VVENC_NAL_UNIT_SUFFIX_SEI, trailingSeiMessages, accessUnit, pos, slice->TLayer, slice->sps );
2770
2771
1.49k
  deleteSEIs( trailingSeiMessages );
2772
1.49k
}
2773
2774
int EncGOP::xWriteVPS ( AccessUnitList &accessUnit, const VPS *vps, HLSWriter& hlsWriter )
2775
0
{
2776
0
  OutputNALUnit nalu(VVENC_NAL_UNIT_VPS);
2777
0
  hlsWriter.setBitstream( &nalu.m_Bitstream );
2778
0
  hlsWriter.codeVPS( vps );
2779
0
  accessUnit.push_back(new NALUnitEBSP(nalu));
2780
0
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
2781
0
}
2782
2783
int EncGOP::xWriteDCI ( AccessUnitList &accessUnit, const DCI *dci, HLSWriter& hlsWriter )
2784
1.49k
{
2785
1.49k
  if (dci->dciId ==0)
2786
1.49k
  {
2787
1.49k
    return 0;
2788
1.49k
  }
2789
2790
0
  OutputNALUnit nalu(VVENC_NAL_UNIT_DCI);
2791
0
  hlsWriter.setBitstream( &nalu.m_Bitstream );
2792
0
  hlsWriter.codeDCI( dci );
2793
0
  accessUnit.push_back(new NALUnitEBSP(nalu));
2794
0
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
2795
1.49k
}
2796
2797
int EncGOP::xWriteSPS ( AccessUnitList &accessUnit, const SPS *sps, HLSWriter& hlsWriter )
2798
1.49k
{
2799
1.49k
  OutputNALUnit nalu(VVENC_NAL_UNIT_SPS);
2800
1.49k
  hlsWriter.setBitstream( &nalu.m_Bitstream );
2801
1.49k
  hlsWriter.codeSPS( sps );
2802
1.49k
  accessUnit.push_back(new NALUnitEBSP(nalu));
2803
1.49k
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
2804
1.49k
}
2805
2806
int EncGOP::xWritePPS ( AccessUnitList &accessUnit, const PPS *pps, const SPS *sps, HLSWriter& hlsWriter )
2807
1.49k
{
2808
1.49k
  OutputNALUnit nalu(VVENC_NAL_UNIT_PPS);
2809
1.49k
  hlsWriter.setBitstream( &nalu.m_Bitstream );
2810
1.49k
  hlsWriter.codePPS( pps, sps );
2811
1.49k
  accessUnit.push_back(new NALUnitEBSP(nalu));
2812
1.49k
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
2813
1.49k
}
2814
2815
int EncGOP::xWriteAPS( AccessUnitList &accessUnit, const APS *aps, HLSWriter& hlsWriter, vvencNalUnitType eNalUnitType )
2816
0
{
2817
0
  OutputNALUnit nalu(eNalUnitType, aps->temporalId);
2818
0
  hlsWriter.setBitstream(&nalu.m_Bitstream);
2819
0
  hlsWriter.codeAPS(aps);
2820
0
  accessUnit.push_back(new NALUnitEBSP(nalu));
2821
0
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
2822
0
}
2823
2824
void EncGOP::xWriteAccessUnitDelimiter ( AccessUnitList &accessUnit, Slice* slice, bool IrapOrGdr, HLSWriter& hlsWriter )
2825
0
{
2826
0
  OutputNALUnit nalu(VVENC_NAL_UNIT_ACCESS_UNIT_DELIMITER, slice->TLayer);
2827
0
  hlsWriter.setBitstream(&nalu.m_Bitstream);
2828
0
  hlsWriter.codeAUD( IrapOrGdr, 2-slice->sliceType );
2829
0
  accessUnit.push_front(new NALUnitEBSP(nalu));
2830
0
}
2831
2832
void EncGOP::xWriteSEI (vvencNalUnitType naluType, SEIMessages& seiMessages, AccessUnitList &accessUnit, AccessUnitList::iterator &auPos, int temporalId, const SPS *sps)
2833
0
{
2834
0
  if (seiMessages.empty())
2835
0
  {
2836
0
    return;
2837
0
  }
2838
0
  OutputNALUnit nalu(naluType, temporalId);
2839
0
  m_seiWriter.writeSEImessages(nalu.m_Bitstream, seiMessages, m_EncHRD, false, temporalId);
2840
0
  auPos = accessUnit.insert(auPos, new NALUnitEBSP(nalu));
2841
0
  auPos++;
2842
0
}
2843
2844
void EncGOP::xWriteSEISeparately (vvencNalUnitType naluType, SEIMessages& seiMessages, AccessUnitList &accessUnit, AccessUnitList::iterator &auPos, int temporalId, const SPS *sps)
2845
2.98k
{
2846
2.98k
  if (seiMessages.empty())
2847
2.98k
  {
2848
2.98k
    return;
2849
2.98k
  }
2850
0
  for (SEIMessages::const_iterator sei = seiMessages.begin(); sei!=seiMessages.end(); sei++ )
2851
0
  {
2852
0
    SEIMessages tmpMessages;
2853
0
    tmpMessages.push_back(*sei);
2854
0
    OutputNALUnit nalu(naluType, temporalId);
2855
0
    m_seiWriter.writeSEImessages(nalu.m_Bitstream, tmpMessages, m_EncHRD, false, temporalId);
2856
0
    auPos = accessUnit.insert(auPos, new NALUnitEBSP(nalu));
2857
0
    auPos++;
2858
0
  }
2859
0
}
2860
2861
/** Attaches the input bitstream to the stream in the output NAL unit
2862
    Updates rNalu to contain concatenated bitstream. rpcBitstreamRedirect is cleared at the end of this function call.
2863
 *  \param codedSliceData contains the coded slice data (bitstream) to be concatenated to rNalu
2864
 *  \param rNalu          target NAL unit
2865
 */
2866
void EncGOP::xAttachSliceDataToNalUnit( OutputNALUnit& rNalu, const OutputBitstream* codedSliceData )
2867
1.49k
{
2868
  // Byte-align
2869
1.49k
  rNalu.m_Bitstream.writeByteAlignment();   // Slice header byte-alignment
2870
2871
  // Perform bitstream concatenation
2872
1.49k
  if (codedSliceData->getNumberOfWrittenBits() > 0)
2873
1.49k
  {
2874
1.49k
    rNalu.m_Bitstream.addSubstream(codedSliceData);
2875
1.49k
  }
2876
1.49k
}
2877
2878
void EncGOP::xCabacZeroWordPadding( const Picture& pic, const Slice* slice, uint32_t binCountsInNalUnits, uint32_t numBytesInVclNalUnits, std::ostringstream &nalUnitData )
2879
1.49k
{
2880
1.49k
  const PPS &pps                     = *(slice->pps);
2881
1.49k
  const SPS &sps                     = *(slice->sps);
2882
1.49k
  const ChromaFormat format          = sps.chromaFormatIdc;
2883
1.49k
  const int log2subWidthCxsubHeightC = getComponentScaleX( COMP_Cb, format ) + getComponentScaleY( COMP_Cb, format );
2884
1.49k
  const int minCUSize                = pic.cs->pcv->minCUSize;
2885
1.49k
  const int paddedWidth              = ( (pps.picWidthInLumaSamples  + minCUSize - 1) / minCUSize) * minCUSize;
2886
1.49k
  const int paddedHeight             = ( (pps.picHeightInLumaSamples + minCUSize - 1) / minCUSize) * minCUSize;
2887
1.49k
  const int rawBits                  = paddedWidth * paddedHeight * ( sps.bitDepths[ CH_L ] + 2 * ( sps.bitDepths[ CH_C ] >> log2subWidthCxsubHeightC ) );
2888
1.49k
  const uint32_t threshold           = ( 32/3 ) * numBytesInVclNalUnits + ( rawBits/32 );
2889
1.49k
  if ( binCountsInNalUnits >= threshold )
2890
0
  {
2891
    // need to add additional cabac zero words (each one accounts for 3 bytes (=00 00 03)) to increase numBytesInVclNalUnits
2892
0
    const uint32_t targetNumBytesInVclNalUnits = ( ( binCountsInNalUnits - ( rawBits/32 ) ) * 3 + 31 ) / 32;
2893
2894
0
    if ( targetNumBytesInVclNalUnits>numBytesInVclNalUnits ) // It should be!
2895
0
    {
2896
0
      const uint32_t numberOfAdditionalBytesNeeded    = targetNumBytesInVclNalUnits - numBytesInVclNalUnits;
2897
0
      const uint32_t numberOfAdditionalCabacZeroWords = ( numberOfAdditionalBytesNeeded + 2 ) / 3;
2898
0
      const uint32_t numberOfAdditionalCabacZeroBytes = numberOfAdditionalCabacZeroWords * 3;
2899
0
      if ( m_pcEncCfg->m_cabacZeroWordPaddingEnabled )
2900
0
      {
2901
0
        std::vector<uint8_t> zeroBytesPadding(numberOfAdditionalCabacZeroBytes, uint8_t(0));
2902
0
        for( uint32_t i = 0; i < numberOfAdditionalCabacZeroWords; i++ )
2903
0
        {
2904
0
          zeroBytesPadding[ i * 3 + 2 ] = 3;  // 00 00 03
2905
0
        }
2906
0
        nalUnitData.write( reinterpret_cast<const char*>(&(zeroBytesPadding[ 0 ])), numberOfAdditionalCabacZeroBytes );
2907
0
        msg.log( VVENC_NOTICE, "Adding %d bytes of padding\n", numberOfAdditionalCabacZeroWords * 3 );
2908
0
      }
2909
0
      else
2910
0
      {
2911
0
        msg.log( VVENC_NOTICE, "Standard would normally require adding %d bytes of padding\n", numberOfAdditionalCabacZeroWords * 3 );
2912
0
      }
2913
0
    }
2914
0
  }
2915
1.49k
}
2916
2917
void EncGOP::xAddPSNRStats( const Picture* pic, CPelUnitBuf cPicD, AccessUnitList& accessUnit, bool printFrameMSE, double* PSNR_Y, bool isEncodeLtRef )
2918
1.49k
{
2919
1.49k
  const Slice* slice         = pic->slices[0];
2920
2921
1.49k
  double dPSNR[MAX_NUM_COMP];
2922
1.49k
  double MSEyuvframe[MAX_NUM_COMP];
2923
5.97k
  for (int i = 0; i < MAX_NUM_COMP; i++)
2924
4.47k
  {
2925
4.47k
    dPSNR[i]       = pic->psnr[i];
2926
4.47k
    MSEyuvframe[i] = pic->mse[i];
2927
4.47k
  }
2928
2929
  /* calculate the size of the access unit, excluding:
2930
   *  - any AnnexB contributions (start_code_prefix, zero_byte, etc.,)
2931
   *  - SEI NAL units
2932
   */
2933
1.49k
  uint32_t numRBSPBytes = 0;
2934
5.97k
  for (AccessUnitList::const_iterator it = accessUnit.begin(); it != accessUnit.end(); it++)
2935
4.47k
  {
2936
4.47k
    uint32_t numRBSPBytes_nal = uint32_t((*it)->m_nalUnitData.str().size());
2937
4.47k
    if (m_pcEncCfg->m_summaryVerboseness > 0)
2938
0
    {
2939
0
      msg.log( VVENC_NOTICE, "*** %s numBytesInNALunit: %u\n", nalUnitTypeToString((*it)->m_nalUnitType), numRBSPBytes_nal);
2940
0
    }
2941
4.47k
    if( ( *it )->m_nalUnitType != VVENC_NAL_UNIT_PREFIX_SEI && ( *it )->m_nalUnitType != VVENC_NAL_UNIT_SUFFIX_SEI )
2942
4.47k
    {
2943
4.47k
      numRBSPBytes += numRBSPBytes_nal;
2944
4.47k
      if (it == accessUnit.begin() || (*it)->m_nalUnitType == VVENC_NAL_UNIT_VPS || (*it)->m_nalUnitType == VVENC_NAL_UNIT_DCI || (*it)->m_nalUnitType == VVENC_NAL_UNIT_SPS || (*it)->m_nalUnitType == VVENC_NAL_UNIT_PPS || (*it)->m_nalUnitType == VVENC_NAL_UNIT_PREFIX_APS || (*it)->m_nalUnitType == VVENC_NAL_UNIT_SUFFIX_APS)
2945
2.98k
      {
2946
2.98k
        numRBSPBytes += 4;
2947
2.98k
      }
2948
1.49k
      else
2949
1.49k
      {
2950
1.49k
        numRBSPBytes += 3;
2951
1.49k
      }
2952
4.47k
    }
2953
4.47k
  }
2954
1.49k
  const uint32_t uibits = numRBSPBytes * 8;
2955
2956
1.49k
  if (m_isPreAnalysis || !m_pcRateCtrl->rcIsFinalPass)
2957
0
  {
2958
0
    m_pcRateCtrl->addRCPassStats( slice->poc,
2959
0
                                  slice->sliceQp,
2960
0
                                  slice->getLambdas()[0],
2961
0
                                  pic->picVA.visAct,
2962
0
                                  uibits,
2963
0
                                  dPSNR[COMP_Y],
2964
0
                                  slice->isIntra(),
2965
0
                                  slice->TLayer,
2966
0
                                  pic->gopEntry->m_isStartOfIntra,
2967
0
                                  pic->gopEntry->m_isStartOfGop,
2968
0
                                  pic->gopEntry->m_gopNum,
2969
0
                                  pic->gopEntry->m_scType,
2970
0
                                  pic->picVA.spatAct[CH_L],
2971
0
                                  pic->m_picShared->m_picMotEstError,
2972
0
                                  pic->m_picShared->m_minNoiseLevels );
2973
0
  }
2974
2975
  //===== add PSNR =====
2976
1.49k
  m_AnalyzeAll.addResult(dPSNR, (double)uibits, MSEyuvframe
2977
1.49k
    , isEncodeLtRef
2978
1.49k
  );
2979
1.49k
  if ( slice->isIntra() )
2980
1.49k
  {
2981
1.49k
    m_AnalyzeI.addResult(dPSNR, (double)uibits, MSEyuvframe
2982
1.49k
      , isEncodeLtRef
2983
1.49k
    );
2984
1.49k
    *PSNR_Y = dPSNR[COMP_Y];
2985
1.49k
  }
2986
1.49k
  if ( slice->isInterP() )
2987
0
  {
2988
0
    m_AnalyzeP.addResult(dPSNR, (double)uibits, MSEyuvframe
2989
0
      , isEncodeLtRef
2990
0
    );
2991
0
    *PSNR_Y = dPSNR[COMP_Y];
2992
0
  }
2993
1.49k
  if ( slice->isInterB() )
2994
0
  {
2995
0
    m_AnalyzeB.addResult(dPSNR, (double)uibits, MSEyuvframe
2996
0
      , isEncodeLtRef
2997
0
    );
2998
0
    *PSNR_Y = dPSNR[COMP_Y];
2999
0
  }
3000
3001
1.49k
  char c = (slice->isIntra() ? 'I' : slice->isInterP() ? 'P' : 'B');
3002
1.49k
  if ( ! pic->isReferenced && pic->refCounter == 0 && ! m_pcEncCfg->m_maxParallelFrames )
3003
0
  {
3004
0
    c += 32;
3005
0
  }
3006
3007
  // create info string
3008
1.49k
  {
3009
1.49k
    if ((m_isPreAnalysis && m_pcRateCtrl->m_pcEncCfg->m_RCTargetBitrate > 0) || !m_pcRateCtrl->rcIsFinalPass)
3010
0
    {
3011
0
      std::string cInfo;
3012
0
      if( m_pcRateCtrl->rcIsFinalPass ) // single pass RC
3013
0
      {
3014
0
        cInfo = prnt("RC analyze poc %5d", slice->poc );
3015
0
      }
3016
0
      else
3017
0
      {
3018
0
        cInfo = prnt("RC pass %d/%d, analyze poc %5d",
3019
0
            m_pcRateCtrl->rcPass + 1,
3020
0
            m_pcEncCfg->m_RCNumPasses,
3021
0
            slice->poc );
3022
0
      }
3023
0
      accessUnit.InfoString.append( cInfo );
3024
0
    }
3025
1.49k
    else
3026
1.49k
    {
3027
1.49k
      std::stringstream sMctf;
3028
1.49k
      if( pic->gopEntry->m_mctfIndex >= 0 )
3029
0
        sMctf << ", TF " << pic->gopEntry->m_mctfIndex << ")";
3030
1.49k
      else
3031
1.49k
        sMctf << ")      ";
3032
3033
1.49k
      std::string cInfo = prnt("POC %5d TId: %1d (%10s, %c-SLICE, QP %d%s %10d bits",
3034
1.49k
          slice->poc,
3035
1.49k
          slice->TLayer,
3036
1.49k
          nalUnitTypeToString( slice->nalUnitType ),
3037
1.49k
          c,
3038
1.49k
          slice->sliceQp,
3039
1.49k
          sMctf.str().c_str(),
3040
1.49k
          uibits );
3041
3042
1.49k
      std::string yPSNR = dPSNR[COMP_Y]  == MAX_DOUBLE ? prnt(" [Y %7s dB    ", "inf" ) : prnt(" [Y %6.4lf dB    ", dPSNR[COMP_Y] );
3043
1.49k
      std::string uPSNR = dPSNR[COMP_Cb] == MAX_DOUBLE ? prnt("U %7s dB    ", "inf" ) : prnt("U %6.4lf dB    ", dPSNR[COMP_Cb] );
3044
1.49k
      std::string vPSNR = dPSNR[COMP_Cr] == MAX_DOUBLE ? prnt("V %7s dB]", "inf" ) : prnt("V %6.4lf dB]", dPSNR[COMP_Cr] );
3045
3046
1.49k
      accessUnit.InfoString.append( cInfo );
3047
1.49k
      accessUnit.InfoString.append( yPSNR );
3048
1.49k
      accessUnit.InfoString.append( uPSNR );
3049
1.49k
      accessUnit.InfoString.append( vPSNR );
3050
3051
1.49k
      if ( m_pcEncCfg->m_printHexPsnr )
3052
0
      {
3053
0
        uint64_t xPsnr[MAX_NUM_COMP];
3054
0
        for (int i = 0; i < MAX_NUM_COMP; i++)
3055
0
        {
3056
0
          std::copy(reinterpret_cast<uint8_t *>(&dPSNR[i]),
3057
0
              reinterpret_cast<uint8_t *>(&dPSNR[i]) + sizeof(dPSNR[i]),
3058
0
              reinterpret_cast<uint8_t *>(&xPsnr[i]));
3059
0
        }
3060
3061
0
        std::string yPSNRHex = dPSNR[COMP_Y]  == MAX_DOUBLE ? prnt(" [xY %16s", "inf") : prnt(" [xY %16" PRIx64,  xPsnr[COMP_Y] );
3062
0
        std::string uPSNRHex = dPSNR[COMP_Cb] == MAX_DOUBLE ? prnt(" xU %16s", "inf") : prnt(" xU %16" PRIx64, xPsnr[COMP_Cb] ) ;
3063
0
        std::string vPSNRHex = dPSNR[COMP_Cr] == MAX_DOUBLE ? prnt(" xV %16s]", "inf") : prnt(" xV %16" PRIx64 "]", xPsnr[COMP_Cr]);
3064
3065
0
        accessUnit.InfoString.append( yPSNRHex );
3066
0
        accessUnit.InfoString.append( uPSNRHex );
3067
0
        accessUnit.InfoString.append( vPSNRHex );
3068
0
      }
3069
3070
1.49k
      if( printFrameMSE )
3071
0
      {
3072
0
        std::string cFrameMSE = prnt( " [Y MSE %6.4lf  U MSE %6.4lf  V MSE %6.4lf]", MSEyuvframe[COMP_Y], MSEyuvframe[COMP_Cb], MSEyuvframe[COMP_Cr]);
3073
0
        accessUnit.InfoString.append( cFrameMSE );
3074
0
      }
3075
3076
1.49k
      std::string cEncTime = prnt(" [ET %5d ]", pic->encTime.getTimerInSec() );
3077
1.49k
      accessUnit.InfoString.append( cEncTime );
3078
3079
1.49k
      std::string cRefPics;
3080
4.47k
      for( int iRefList = 0; iRefList < 2; iRefList++ )
3081
2.98k
      {
3082
2.98k
        std::string tmp = prnt(" [L%d ", iRefList);
3083
2.98k
        cRefPics.append( tmp );
3084
2.98k
        for( int iRefIndex = 0; iRefIndex < slice->numRefIdx[ iRefList ]; iRefIndex++ )
3085
0
        {
3086
0
          tmp = prnt("%d ", slice->getRefPOC( RefPicList( iRefList ), iRefIndex));
3087
0
          cRefPics.append( tmp );
3088
0
        }
3089
2.98k
        cRefPics.append( "]" );
3090
2.98k
      }
3091
1.49k
      accessUnit.InfoString.append( cRefPics );
3092
1.49k
    }
3093
1.49k
  }
3094
1.49k
}
3095
3096
uint64_t EncGOP::xFindDistortionPlane( const CPelBuf& pic0, const CPelBuf& pic1, uint32_t rshift ) const
3097
0
{
3098
0
  uint64_t uiTotalDiff;
3099
0
  const  Pel*  pSrc0 = pic0.bufAt(0, 0);
3100
0
  const  Pel*  pSrc1 = pic1.bufAt(0, 0);
3101
3102
0
  CHECK(pic0.width  != pic1.width , "Unspecified error");
3103
0
  CHECK(pic0.height != pic1.height, "Unspecified error");
3104
3105
0
  if( rshift > 0 )
3106
0
  {
3107
0
    uiTotalDiff = 0;
3108
0
    for (int y = 0; y < pic0.height; y++)
3109
0
    {
3110
0
      for (int x = 0; x < pic0.width; x++)
3111
0
      {
3112
0
        Intermediate_Int iTemp = pSrc0[x] - pSrc1[x];
3113
0
        uiTotalDiff += uint64_t((iTemp * iTemp) >> rshift);
3114
0
      }
3115
0
      pSrc0 += pic0.stride;
3116
0
      pSrc1 += pic1.stride;
3117
0
    }
3118
0
  }
3119
0
  else
3120
0
  {
3121
0
    uiTotalDiff = 0;
3122
0
    for (int y = 0; y < pic0.height; y++)
3123
0
    {
3124
0
      for (int x = 0; x < pic0.width; x++)
3125
0
      {
3126
0
        Intermediate_Int iTemp = pSrc0[x] - pSrc1[x];
3127
0
        uiTotalDiff += uint64_t(iTemp * iTemp);
3128
0
      }
3129
0
      pSrc0 += pic0.stride;
3130
0
      pSrc1 += pic1.stride;
3131
0
    }
3132
0
  }
3133
3134
0
  return uiTotalDiff;
3135
0
}
3136
3137
void EncGOP::xPrintPictureInfo( const Picture& pic, AccessUnitList& accessUnit, const std::string& digestStr, bool printFrameMSE, bool isEncodeLtRef )
3138
1.49k
{
3139
1.49k
  double PSNR_Y;
3140
1.49k
  xAddPSNRStats( &pic, pic.getRecoBuf(), accessUnit, printFrameMSE, &PSNR_Y, isEncodeLtRef );
3141
3142
1.49k
  if( ! m_isPreAnalysis && m_pcRateCtrl->rcIsFinalPass )
3143
1.49k
  {
3144
1.49k
    std::string modeName;
3145
1.49k
    switch ( m_pcEncCfg->m_decodedPictureHashSEIType )
3146
1.49k
    {
3147
0
      case VVENC_HASHTYPE_MD5:
3148
0
      case VVENC_HASHTYPE_MD5_LOG:
3149
0
        modeName = "MD5";
3150
0
        break;
3151
0
      case VVENC_HASHTYPE_CRC:
3152
0
      case VVENC_HASHTYPE_CRC_LOG:
3153
0
        modeName = "CRC";
3154
0
        break;
3155
0
      case VVENC_HASHTYPE_CHECKSUM:
3156
0
      case VVENC_HASHTYPE_CHECKSUM_LOG:
3157
0
        modeName = "Checksum";
3158
0
        break;
3159
1.49k
      default:
3160
1.49k
        break;
3161
1.49k
    }
3162
3163
1.49k
    if ( modeName.length() )
3164
0
    {
3165
0
      std::string cDigist = prnt(" [%s:%s]", modeName.c_str(), digestStr.empty() ? "?" : digestStr.c_str() );
3166
0
      accessUnit.InfoString.append( cDigist );
3167
0
    }
3168
1.49k
  }
3169
3170
1.49k
  if( !accessUnit.InfoString.empty() && m_pcEncCfg->m_verbosity >= VVENC_NOTICE )
3171
0
  {
3172
0
    std::string cPicInfo = accessUnit.InfoString;
3173
0
    cPicInfo.append("\n");
3174
0
    const vvencMsgLevel msgLevel = m_isPreAnalysis ? VVENC_DETAILS : VVENC_NOTICE;
3175
0
    msg.log( msgLevel, cPicInfo.c_str() );
3176
0
    if( m_pcEncCfg->m_verbosity >= msgLevel ) fflush( stdout );
3177
0
  }
3178
1.49k
}
3179
3180
void EncGOP::xForceScc( Picture& pic )
3181
1.49k
{
3182
1.49k
  if( pic.gopEntry->m_isStartOfGop )
3183
1.49k
  {
3184
1.49k
    m_forceSCC = pic.m_picShared->m_forceSCC;
3185
1.49k
  }
3186
1.49k
  if( m_forceSCC && (!pic.isSccStrong || !pic.isSccWeak) )
3187
0
  {
3188
0
    pic.isSccStrong = true;
3189
0
    pic.isSccWeak = true;
3190
0
    pic.setSccFlags(m_pcEncCfg);
3191
0
  }
3192
1.49k
}
3193
3194
} // namespace vvenc
3195
3196
//! \}
3197