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/EncLib.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     EncLib.cpp
45
    \brief    encoder class
46
*/
47
48
#include "EncLib.h"
49
#include "CommonLib/Picture.h"
50
#include "CommonLib/CommonDef.h"
51
#include "CommonLib/TimeProfiler.h"
52
#include "CommonLib/Rom.h"
53
#include "CommonLib/MCTF.h"
54
#include "Utilities/NoMallocThreadPool.h"
55
#include "Utilities/MsgLog.h"
56
#include "EncStage.h"
57
#include "PreProcess.h"
58
#include "CuTree.h"
59
#include "EncGOP.h"
60
#include "CommonLib/x86/CommonDefX86.h"
61
62
//! \ingroup EncoderLib
63
//! \{
64
65
namespace vvenc {
66
67
68
// ====================================================================================================================
69
// Constructor / destructor / create / destroy
70
// ====================================================================================================================
71
72
EncLib::EncLib( MsgLog& logger )
73
1.49k
  : msg             ( logger )
74
1.49k
  , m_recYuvBufFunc  ( nullptr )
75
1.49k
  , m_recYuvBufCtx   ( nullptr )
76
1.49k
  , m_encCfg         ()
77
1.49k
  , m_orgCfg         ()
78
1.49k
  , m_firstPassCfg   ()
79
1.49k
  , m_rateCtrl       ( nullptr )
80
1.49k
  , m_preProcess     ( nullptr )
81
1.49k
  , m_MCTF           ( nullptr )
82
1.49k
  , m_cuTree         ( nullptr )
83
1.49k
  , m_preEncoder     ( nullptr )
84
1.49k
  , m_gopEncoder     ( nullptr )
85
1.49k
  , m_threadPool     ( nullptr )
86
1.49k
  , m_picsRcvd       ( 0 )
87
1.49k
  , m_passInitialized( -1 )
88
1.49k
  , m_maxNumPicShared( MAX_INT )
89
1.49k
  , m_accessUnitOutputStarted( false )
90
1.49k
{
91
1.49k
}
92
93
EncLib::~EncLib()
94
1.49k
{
95
1.49k
  delete m_rateCtrl;
96
1.49k
  m_rateCtrl = nullptr;
97
1.49k
}
98
99
void EncLib::setRecYUVBufferCallback( void* ctx, vvencRecYUVBufferCallback func )
100
0
{
101
0
  m_recYuvBufCtx  = ctx;
102
0
  m_recYuvBufFunc = func;
103
0
  if( m_rateCtrl && m_rateCtrl->rcIsFinalPass && m_gopEncoder )
104
0
  {
105
0
    m_gopEncoder->setRecYUVBufferCallback( m_recYuvBufCtx, m_recYuvBufFunc );
106
0
  }
107
0
}
108
109
void EncLib::initEncoderLib( const vvenc_config& encCfg )
110
1.49k
{
111
  // copy config parameter
112
1.49k
  const_cast<VVEncCfg&>(m_encCfg) = encCfg;
113
114
#if defined( REAL_TARGET_X86 ) && defined( _MSC_VER ) && _MSC_VER >= 1938 && _MSC_VER < 1939
115
  if( read_x86_extension_flags() >= x86_simd::AVX2 )
116
  {
117
    msg.log( VVENC_WARNING, "WARNING: MSVC version 17.8 produces invalid AVX2 code, partially disabling AVX2!\n" );
118
  }
119
120
#endif
121
  // setup modified configs for rate control
122
1.49k
  if( m_encCfg.m_RCNumPasses > 1 || m_encCfg.m_LookAhead )
123
0
  {
124
0
    xInitRCCfg();
125
0
  }
126
127
  // initialize pass
128
1.49k
  initPass( 0, nullptr );
129
130
#if ENABLE_TRACING
131
  g_trace_ctx = tracing_init( m_encCfg.m_traceFile, m_encCfg.m_traceRule, msg );
132
  if( g_trace_ctx && m_encCfg.m_listTracingChannels )
133
  {
134
    std::string sChannelsList;
135
    g_trace_ctx->getChannelsList( sChannelsList );
136
    msg.log( VVENC_INFO, "\n Using tracing channels:\n\n%s\n", sChannelsList.c_str() );
137
  }
138
#endif
139
140
#if ENABLE_TIME_PROFILING
141
  if( g_timeProfiler )
142
  {
143
    delete g_timeProfiler;
144
  }
145
  g_timeProfiler = timeProfilerCreate( encCfg );
146
#endif
147
1.49k
}
148
149
void EncLib::uninitEncoderLib()
150
1.49k
{
151
#if ENABLE_TRACING
152
  if( g_trace_ctx )
153
  {
154
    tracing_uninit( g_trace_ctx );
155
  }
156
#endif
157
158
#if ENABLE_CU_MODE_COUNTERS
159
  std::cout << std::endl;
160
  std::cout << "CU Modes statistic across picture types and temporal levels (0:Intra, >0:Inter, luma only)" << std::endl;
161
  for( size_t j = 0; j < g_cuCounters1D.getNumCntTypes(); j++ )
162
  {
163
    for( size_t i = 0; i < g_cuCounters1D.getDimHor() - 1; i++ )
164
    {
165
      g_cuCounters1D[j][0][g_cuCounters1D.getDimHor() - 1] += g_cuCounters1D[j][0][i];
166
    }
167
  }
168
  StatCounters::report2D( std::cout, g_cuCounters1D, false, true, false, true, true, CU_MODES_TESTED );
169
170
  std::cout << std::endl;
171
  std::cout << "CU Modes statistic across block-shapes (Non-I-Slices, luma only)" << std::endl;
172
  StatCounters::report2D( std::cout, g_cuCounters2D, true, true, false, true, true, CU_MODES_TESTED );
173
#endif
174
175
#if ENABLE_TIME_PROFILING
176
#if ENABLE_TIME_PROFILING_MT_MODE
177
  if( m_threadPool )
178
  {
179
    for(auto& p : m_threadPool->getProfilers())
180
    {
181
      *g_timeProfiler += *p;
182
    }
183
  }
184
#endif
185
  timeProfilerResults( g_timeProfiler );
186
  delete g_timeProfiler;
187
  g_timeProfiler = nullptr;
188
#endif
189
1.49k
  xUninitLib();
190
1.49k
}
191
192
void EncLib::initPass( int pass, const char* statsFName )
193
1.49k
{
194
1.49k
  CHECK( m_passInitialized != pass && m_passInitialized + 1 != pass, "initialization of passes only in successive order possible" );
195
196
1.49k
  if( m_rateCtrl == nullptr )
197
1.49k
  {
198
1.49k
    m_rateCtrl = new RateCtrl(msg);
199
1.49k
  }
200
201
1.49k
  m_rateCtrl->setRCPass( m_encCfg, pass, statsFName );
202
203
1.49k
  if( m_passInitialized + 1 != pass )
204
0
  {
205
0
    return;
206
0
  }
207
208
  // reset
209
1.49k
  xUninitLib();
210
211
  // enable encoder config based on rate control pass
212
1.49k
  if( m_encCfg.m_RCNumPasses > 1 || ( m_encCfg.m_LookAhead && m_orgCfg.m_RCTargetBitrate > 0 ) )
213
0
  {
214
0
    if( !m_rateCtrl->rcIsFinalPass )
215
0
    {
216
      // set encoder config for 1st rate control pass
217
0
      const_cast<VVEncCfg&>(m_encCfg) = m_firstPassCfg;
218
0
    }
219
0
    else
220
0
    {
221
      // restore encoder config for final 2nd RC pass
222
0
      const_cast<VVEncCfg&>(m_encCfg) = m_orgCfg;
223
0
      m_rateCtrl->init( m_encCfg );
224
0
      const_cast<VVEncCfg&>(m_encCfg).m_QP = m_rateCtrl->getBaseQP();
225
0
    }
226
0
    if( m_encCfg.m_RCTargetBitrate > 0 && !m_encCfg.m_LookAhead )
227
0
    {
228
0
      m_rateCtrl->processFirstPassData( false );
229
0
    }
230
0
  }
231
1.49k
  else if( m_encCfg.m_usePerceptQPA && m_encCfg.m_LookAhead )
232
0
  {
233
0
    m_rateCtrl->init( m_encCfg );
234
0
  }
235
236
  // thread pool
237
1.49k
  if( m_encCfg.m_numThreads > 0 )
238
1.49k
  {
239
1.49k
    m_threadPool = new NoMallocThreadPool( m_encCfg.m_numThreads, "EncSliceThreadPool", &m_encCfg );
240
1.49k
  }
241
1.49k
  m_maxNumPicShared = 0;
242
243
  // pre processing
244
1.49k
  m_preProcess = new PreProcess( msg );
245
1.49k
  m_preProcess->initStage( m_encCfg, 1, -m_encCfg.m_leadFrames, true, true, false );
246
1.49k
  m_preProcess->init( m_encCfg, m_rateCtrl->rcIsFinalPass );
247
1.49k
  m_encStages.push_back( m_preProcess );
248
1.49k
  m_maxNumPicShared += 1;
249
250
  // MCTF
251
1.49k
  if( m_encCfg.m_vvencMCTF.MCTF || m_encCfg.m_usePerceptQPA )
252
1.49k
  {
253
1.49k
    m_MCTF = new MCTF();
254
1.49k
    const int leadFrames   = std::min( VVENC_MCTF_RANGE, m_encCfg.m_leadFrames );
255
1.49k
    const int minQueueSize = m_encCfg.m_vvencMCTF.MCTFFutureReference ? ( leadFrames + 1 + VVENC_MCTF_RANGE ) : ( leadFrames + 1 );
256
1.49k
    m_MCTF->initStage( m_encCfg, minQueueSize, -leadFrames, true, true, false );
257
1.49k
    m_MCTF->init( m_encCfg, m_rateCtrl->rcIsFinalPass, m_threadPool );
258
1.49k
    m_encStages.push_back( m_MCTF );
259
1.49k
    m_maxNumPicShared += minQueueSize - leadFrames;
260
1.49k
  }
261
262
  // CU-tree look-ahead, holds the pictures of a GOP until the next GOP has arrived
263
1.49k
  if( m_encCfg.useCuTree() )
264
0
  {
265
0
    m_cuTree = new CuTree();
266
0
    m_cuTree->initStage( m_encCfg, 1, 0, false, true, false );
267
0
    m_cuTree->init( m_encCfg, m_threadPool );
268
0
    m_encStages.push_back( m_cuTree );
269
    // a GOP and its look-ahead wait here, while the GOP encoder holds the previous GOP
270
0
    m_maxNumPicShared += m_encCfg.m_GOPSize + CuTree::getNumLookAheadPics( m_encCfg );
271
0
  }
272
273
  // pre analysis encoder
274
1.49k
  if( m_encCfg.m_LookAhead )
275
0
  {
276
0
    m_preEncoder = new EncGOP( msg );
277
0
    const int minQueueSize = m_firstPassCfg.m_GOPSize + 1;
278
0
    m_preEncoder->initStage( m_firstPassCfg, minQueueSize, 0, false, false, false );
279
0
    m_preEncoder->init( m_firstPassCfg, m_preProcess->getGOPCfg(), *m_rateCtrl, m_threadPool, true );
280
0
    m_encStages.push_back( m_preEncoder );
281
0
    m_maxNumPicShared += minQueueSize;
282
0
  }
283
284
  // gop encoder
285
1.49k
  m_gopEncoder = new EncGOP( msg );
286
1.49k
  const int minQueueSize = m_encCfg.m_GOPSize + 1;
287
1.49k
  m_gopEncoder->initStage( m_encCfg, minQueueSize, 0, false, false, m_encCfg.m_stageParallelProc );
288
1.49k
  m_gopEncoder->init( m_encCfg, m_preProcess->getGOPCfg(), *m_rateCtrl, m_threadPool, false );
289
1.49k
  m_encStages.push_back( m_gopEncoder );
290
1.49k
  m_maxNumPicShared += minQueueSize;
291
292
  // additional pictures due to structural delay
293
1.49k
  m_maxNumPicShared += m_preProcess->getGOPCfg()->getNumReorderPics()[ m_encCfg.m_maxTLayer ];
294
1.49k
  m_maxNumPicShared += 3;
295
  // increase number of picture buffers for GOP parallel processing (with IFP), or read GOPs ahead
296
  // so that frame parallel encoding can start pictures of later GOPs early (without IFP)
297
1.49k
  m_maxNumPicShared += m_encCfg.m_numParallelGOPs ? (m_encCfg.m_GOPSize + 1) * m_encCfg.m_numParallelGOPs: 0;
298
299
1.49k
  if( m_rateCtrl->rcIsFinalPass )
300
1.49k
  {
301
1.49k
    m_gopEncoder->setRecYUVBufferCallback( m_recYuvBufCtx, m_recYuvBufFunc );
302
1.49k
  }
303
304
  // link encoder stages
305
4.47k
  for( int i = 0; i < (int)m_encStages.size() - 1; i++ )
306
2.98k
  {
307
2.98k
    m_encStages[ i ]->linkNextStage( m_encStages[ i + 1 ] );
308
2.98k
  }
309
310
1.49k
  m_picsRcvd                = -m_encCfg.m_leadFrames;
311
1.49k
  m_accessUnitOutputStarted = false;
312
1.49k
  m_passInitialized         = pass;
313
1.49k
}
314
315
void EncLib::xUninitLib()
316
2.98k
{
317
  // make sure all processing threads are stopped before releasing data
318
2.98k
  if( m_threadPool )
319
1.49k
  {
320
1.49k
    m_threadPool->shutdown( true );
321
1.49k
  }
322
323
  // sub modules
324
2.98k
  if( m_rateCtrl != nullptr )
325
2.98k
  {
326
2.98k
    m_rateCtrl->destroy();
327
2.98k
  }
328
2.98k
  if( m_preProcess )
329
1.49k
  {
330
1.49k
    delete m_preProcess;
331
1.49k
    m_preProcess = nullptr;
332
1.49k
  }
333
2.98k
  if( m_MCTF )
334
1.49k
  {
335
1.49k
    delete m_MCTF;
336
1.49k
    m_MCTF = nullptr;
337
1.49k
  }
338
2.98k
  if( m_cuTree )
339
0
  {
340
0
    delete m_cuTree;
341
0
    m_cuTree = nullptr;
342
0
  }
343
2.98k
  if( m_preEncoder )
344
0
  {
345
0
    delete m_preEncoder;
346
0
    m_preEncoder = nullptr;
347
0
  }
348
2.98k
  if( m_gopEncoder )
349
1.49k
  {
350
1.49k
    delete m_gopEncoder;
351
1.49k
    m_gopEncoder = nullptr;
352
1.49k
  }
353
2.98k
  m_encStages.clear();
354
355
2.98k
  for( auto picShared : m_picSharedList )
356
1.49k
  {
357
1.49k
    delete picShared;
358
1.49k
  }
359
2.98k
  m_picSharedList.clear();
360
361
  // thread pool
362
2.98k
  if( m_threadPool )
363
1.49k
  {
364
1.49k
    delete m_threadPool;
365
1.49k
    m_threadPool = nullptr;
366
1.49k
  }
367
2.98k
}
368
369
void EncLib::xInitRCCfg()
370
0
{
371
  // backup original configuration
372
0
  const_cast<VVEncCfg&>(m_orgCfg) = m_encCfg;
373
374
  // initialize first pass configuration
375
0
  m_firstPassCfg = m_encCfg;
376
0
  vvenc_init_preset( &m_firstPassCfg, vvencPresetMode::VVENC_FIRSTPASS );
377
378
  // fixed-QP encoding in first rate control pass
379
0
  m_firstPassCfg.m_RCTargetBitrate = 0;
380
0
  if( m_firstPassCfg.m_FirstPassMode > 2 )
381
0
  {
382
0
    m_firstPassCfg.m_SourceWidth  = ( m_encCfg.m_SourceWidth  >> 1 ) & ( ~7 );
383
0
    m_firstPassCfg.m_SourceHeight = ( m_encCfg.m_SourceHeight >> 1 ) & ( ~7 );
384
0
    m_firstPassCfg.m_PadSourceWidth  = m_firstPassCfg.m_SourceWidth;
385
0
    m_firstPassCfg.m_PadSourceHeight = m_firstPassCfg.m_SourceHeight;
386
0
#if TILES_IFP_2PRC_HOTFIX
387
0
    if( m_firstPassCfg.m_ifp && (m_firstPassCfg.m_numTileCols > 1 || m_firstPassCfg.m_numTileRows > 1) )
388
0
    {
389
      // due to sub-sampling, expected different tile configuration in cfg (m_tileRowHeight[], m_tileColumnWidth[])
390
      // currently, disable auto tiles for the first pass
391
0
      m_firstPassCfg.m_numTileCols = 1;
392
0
      m_firstPassCfg.m_numTileRows = 1;
393
0
      m_firstPassCfg.m_picPartitionFlag = false;
394
0
    }
395
0
#endif
396
0
  }
397
398
  // preserve some settings
399
0
  m_firstPassCfg.m_intraQPOffset   = m_encCfg.m_intraQPOffset;
400
0
  m_firstPassCfg.m_GOPQPA          = m_encCfg.m_GOPQPA;
401
0
  if( m_firstPassCfg.m_usePerceptQPA && ( m_firstPassCfg.m_QP <= MAX_QP_PERCEPT_QPA || m_firstPassCfg.m_framesToBeEncoded == 1 ) )
402
0
  {
403
0
    m_firstPassCfg.m_CTUSize       = m_encCfg.m_CTUSize;
404
0
  }
405
0
  m_firstPassCfg.m_vvencMCTF.MCTF  = m_encCfg.m_vvencMCTF.MCTF;
406
  // the first pass measures the rates with the final pass's block importance mapping; the firstpass preset also
407
  // leaves the CU-tree block size of the final pass
408
0
  m_firstPassCfg.m_blockImportanceMapping = m_encCfg.m_blockImportanceMapping;
409
0
  m_firstPassCfg.m_IBCMode         = m_encCfg.m_IBCMode;
410
0
  m_firstPassCfg.m_bimCtuSize      = m_encCfg.m_CTUSize;
411
0
  m_firstPassCfg.m_log2MinCodingBlockSize = m_encCfg.m_log2MinCodingBlockSize;
412
413
  // set Inter block size
414
0
  if( m_firstPassCfg.m_FirstPassMode > 0 )
415
0
  {
416
0
    m_firstPassCfg.m_MinQT[ 1 ] = m_firstPassCfg.m_MaxQT[ 1 ] = ( std::min( m_firstPassCfg.m_SourceWidth, m_firstPassCfg.m_SourceHeight ) < 720 ? 32 : 64 );
417
0
  }
418
419
  // clear MaxCuDQPSubdiv
420
0
  if( m_firstPassCfg.m_CTUSize < 128 && std::min( m_firstPassCfg.m_SourceWidth, m_firstPassCfg.m_SourceHeight ) >= 720 )
421
0
  {
422
0
    m_firstPassCfg.m_cuQpDeltaSubdiv = 0;
423
0
  }
424
0
}
425
426
// ====================================================================================================================
427
// Public member functions
428
// ====================================================================================================================
429
430
// The current I/O work flow consists of three main parts:
431
//   1. At the beginning, the encoder can consume input YUV buffers without an output.
432
//   2. After the first encoded picture/access unit is output, on each next call: one yuv-buffer IN / one encoded access unit (AU) OUT.
433
//      Hence, in stage-parallel mode, we must wait until next encoded picture (AU) is going to be output
434
//   3. When no more input is available, the top level goes into flushing mode: nullptr IN / one encoded access unit (AU) OUT. 
435
//      The encoder flushes the encoded AUs until the queues are empty.
436
437
void EncLib::encodePicture( bool flush, const vvencYUVBuffer* yuvInBuf, AccessUnitList& au, bool& isQueueEmpty )
438
2.98k
{
439
2.98k
  PROFILER_ACCUM_AND_START_NEW_SET( 1, g_timeProfiler, P_TOP_LEVEL );
440
441
2.98k
  CHECK( yuvInBuf == nullptr && ! flush, "no input picture given" );
442
443
  // clear output access unit
444
2.98k
  au.clearAu();
445
446
  // NOTE regarding the stage parallel processing
447
  // The next input yuv-buffer must be passed to the encoding process (1.Stage).
448
  // The following should be considered:
449
  //   1. The final stage is non-blocking, so it doesn't wait until picture is reconstructed.
450
  //   2. Generally, the stages have different throughput; last stage is the slowest.
451
  //   3. The number of picture-units required for the input yuv-buffers is limited.
452
  //   4. Due to chunk-mode and non-blockiness, it's possible that we can run out of picture-units.
453
  //   5. Then we have to wait for the next available picture-unit, so the input frame can be passed to the 1.stage.
454
455
  // CU-tree passes its pictures on a GOP at a time, and the GOP encoder only starts with a GOP and one picture queued.
456
  // When the intra period cuts a GOP short, the GOP encoder waits for CU-tree's next batch, at most a GOP of input.
457
  // Without stage parallel processing, a buffer of that many access units covers the wait, so every call still
458
  // returns one; with it, the delayed output below already does.
459
2.98k
  const size_t auBuffer = ( m_encCfg.useCuTree() && ! m_encCfg.m_stageParallelProc ) ? m_encCfg.m_GOPSize + 1 : 0;
460
461
2.98k
  PicShared* picShared = nullptr;
462
2.98k
  bool inputPending    = ( yuvInBuf != nullptr );
463
4.47k
  while( true )
464
4.47k
  {
465
    // send new YUV input buffer to first encoder stage
466
4.47k
    if( inputPending )
467
1.49k
    {
468
1.49k
      picShared = xGetFreePicShared();
469
1.49k
      if( picShared )
470
1.49k
      {
471
1.49k
        picShared->reuse( m_picsRcvd, yuvInBuf );
472
1.49k
        m_encStages[ 0 ]->addPicSorted( picShared, flush );
473
1.49k
        m_picsRcvd  += 1;
474
1.49k
        inputPending = false;
475
1.49k
      }
476
1.49k
    }
477
478
4.47k
    PROFILER_EXT_UPDATE( g_timeProfiler, P_TOP_LEVEL, 0 );
479
480
    // trigger stages
481
4.47k
    isQueueEmpty = m_picsRcvd > 0 || ( m_picsRcvd <= 0 && flush );
482
4.47k
    bool progress = false;
483
4.47k
    for( auto encStage : m_encStages )
484
13.4k
    {
485
13.4k
      progress     |= encStage->runStage( flush, au );
486
13.4k
      isQueueEmpty &= encStage->isStageDone();
487
13.4k
    }
488
489
4.47k
    if( !au.empty() )
490
1.49k
    {
491
1.49k
      m_AuList.push_back( au );
492
493
1.49k
      au.detachNalUnitList();
494
1.49k
      au.clearAu();
495
      // NOTE: delay AU output in stage parallel mode, and by the CU-tree buffer without it
496
1.49k
      if( !m_accessUnitOutputStarted )
497
1.49k
        m_accessUnitOutputStarted = ( !m_encCfg.m_stageParallelProc && m_AuList.size() > auBuffer ) || m_AuList.size() > 4 || flush;
498
1.49k
    }
499
500
    // wait if input picture hasn't been stored yet or if encoding is running and no new output access unit has been encoded
501
    // (or, with the CU-tree buffer, until the buffer is full again)
502
4.47k
    bool waitAndStay = inputPending || ( m_rateCtrl->rcIsFinalPass && m_AuList.size() <= auBuffer && ! isQueueEmpty && ( m_accessUnitOutputStarted || flush ) );
503
4.47k
    if( ! waitAndStay )
504
2.98k
    {
505
2.98k
      break;
506
2.98k
    }
507
    // With the CU-tree buffer (no stage parallel processing), nothing changes until a stage passes a picture on:
508
    // return for more input rather than wait for an access unit that cannot come.
509
1.49k
    if( auBuffer && ! inputPending && ! progress )
510
0
    {
511
0
      break;
512
0
    }
513
514
1.49k
    if( m_encCfg.m_stageParallelProc )
515
1.49k
    {
516
1.49k
      for( auto encStage : m_encStages )
517
4.47k
      {
518
4.47k
        if( encStage->isNonBlocking() )
519
1.49k
          encStage->waitForFreeEncoders();
520
4.47k
      }
521
1.49k
    }
522
1.49k
  }
523
524
  // check if we have an AU to output
525
2.98k
  if( !m_AuList.empty() && m_accessUnitOutputStarted )
526
1.49k
  {
527
1.49k
    au = m_AuList.front();
528
1.49k
    m_AuList.front().detachNalUnitList();
529
1.49k
    m_AuList.pop_front();
530
1.49k
  }
531
532
  // reset output access unit, if not final pass
533
2.98k
  if( ! m_rateCtrl->rcIsFinalPass )
534
0
  {
535
0
    au.clearAu();
536
0
  }
537
538
  // finally, ensure that the whole queue is empty
539
2.98k
  isQueueEmpty &= m_AuList.empty();
540
2.98k
}
541
542
void EncLib::printSummary()
543
0
{
544
0
  if( m_gopEncoder )
545
0
  {
546
0
    m_gopEncoder->printOutSummary( m_encCfg.m_printMSEBasedSequencePSNR, m_encCfg.m_printSequenceMSE, m_encCfg.m_printHexPsnr );
547
0
  }
548
0
}
549
550
void EncLib::getParameterSets( AccessUnitList& au )
551
0
{
552
0
  if( m_gopEncoder )
553
0
  {
554
0
    m_gopEncoder->getParameterSets( au );
555
0
  }
556
0
}
557
558
int EncLib::getCurPass() const
559
0
{
560
0
  return m_passInitialized;
561
0
}
562
563
// ====================================================================================================================
564
// Protected member functions
565
// ====================================================================================================================
566
567
PicShared* EncLib::xGetFreePicShared()
568
1.49k
{
569
1.49k
  PicShared* picShared = nullptr;
570
1.49k
  for( auto itr : m_picSharedList )
571
0
  {
572
0
    if( ! itr->isUsed() )
573
0
    {
574
0
      picShared = itr;
575
0
      break;
576
0
    }
577
0
  }
578
579
1.49k
  if( ! picShared )
580
1.49k
  {
581
1.49k
    if( m_encCfg.m_stageParallelProc && ( m_picSharedList.size() >= m_maxNumPicShared ) )
582
0
      return nullptr;
583
584
1.49k
    picShared = new PicShared();
585
1.49k
    picShared->create( m_encCfg.m_framesToBeEncoded, m_encCfg.m_internChromaFormat, Size( m_encCfg.m_PadSourceWidth, m_encCfg.m_PadSourceHeight ), m_encCfg.m_vvencMCTF.MCTF || m_encCfg.m_usePerceptQPA );
586
1.49k
    m_picSharedList.push_back( picShared );
587
1.49k
  }
588
1.49k
  CHECK( picShared == nullptr, "out of memory" );
589
590
1.49k
  return picShared;
591
1.49k
}
592
593
} // namespace vvenc
594
595
//! \}