Coverage Report

Created: 2026-04-01 07:49

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