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/GOPCfg.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     GOPCfg.cpp
45
    \brief    
46
*/
47
48
#include "GOPCfg.h"
49
#include "EncStage.h"
50
51
//! \ingroup EncoderLib
52
//! \{
53
54
namespace vvenc {
55
56
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
57
58
void GOPCfg::initGopList( int refreshType, bool poc0idr, int intraPeriod, int gopSize, int leadFrames, bool bPicReordering, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ], const vvencMCTF& mctfCfg, int firstPassMode, int minIntraDist )
59
0
{
60
0
  CHECK( gopSize < 1, "gop size has to be greater than 0" );
61
62
0
  m_mctfCfg            = &mctfCfg;
63
0
  m_refreshType        = refreshType;
64
0
  m_picReordering      = bPicReordering;
65
0
  m_poc0idr            = poc0idr;
66
0
  m_fixIntraPeriod     = intraPeriod;
67
0
  m_firstPassMode      = firstPassMode;
68
0
  m_maxGopSize         = gopSize;
69
0
  m_defGopSize         = m_fixIntraPeriod > 0 ? std::min( m_maxGopSize, m_fixIntraPeriod ) : m_maxGopSize;
70
71
0
  const int numGops    = m_fixIntraPeriod > 0 ? m_fixIntraPeriod / m_defGopSize               : 0;
72
0
  const int remainSize = m_fixIntraPeriod > 0 ? m_fixIntraPeriod - ( m_defGopSize * numGops ) : 0;
73
0
  const int minPrevPoc = xGetMinPoc( m_maxGopSize, cfgGopList );
74
0
  const int numDefault = ( ( -1 * minPrevPoc ) + m_maxGopSize - 1 ) / m_maxGopSize + 1;
75
0
  CHECK( numDefault <= 0, "number of gop lists to be created is 0" );
76
77
  // setup default gop lists
78
0
  GOPEntryList* prevGopList = nullptr;
79
0
  int pocOffset             = 0;
80
0
  m_defaultGopLists.resize( numDefault );
81
0
  for( int i = 0; i < numDefault; i++ )
82
0
  {
83
0
    xCreateGopList( m_maxGopSize, m_defGopSize, pocOffset, cfgGopList, prevGopList, m_defaultGopLists[ i ] );
84
0
    prevGopList = &m_defaultGopLists[ i ];
85
0
    pocOffset += m_defGopSize;
86
0
  }
87
0
  if( remainSize )
88
0
  {
89
0
    const int prevGopIdx = Clip3<int>( 0, (int)m_defaultGopLists.size() - 1, numGops - 1 );
90
0
    prevGopList = &m_defaultGopLists[ prevGopIdx ];
91
0
    pocOffset   = numGops * m_defGopSize;
92
0
    xCreateGopList( m_maxGopSize, remainSize, pocOffset, cfgGopList, prevGopList, m_remainGopList );
93
0
  }
94
95
  // set some defaults based on gop list layouts
96
0
  xSetDefaultRPL    ( m_defaultGopLists );
97
0
  xSetDBPConstraints( m_defaultGopLists );
98
  // sanity check
99
0
  for( int i = 0; i < (int)m_defaultGopLists.size() + 1; i++ )
100
0
  {
101
0
    GOPEntryList& gopList = i < (int)m_defaultGopLists.size() ? m_defaultGopLists[ i ] : m_remainGopList;
102
0
    if( ! xCheckDBPConstraints( gopList ) )
103
0
    {
104
0
      THROW( "gop lists do not fullfill default DPB constraints" );
105
0
    }
106
0
  }
107
108
0
  CHECK( leadFrames < 0, "negative number of lead frames not supported" );
109
110
  // start with first gop list
111
0
  m_gopList     = remainSize != 0 && !poc0idr ? &m_remainGopList : &m_defaultGopLists[ 0 ];
112
0
  m_nextListIdx = remainSize != 0 && !poc0idr ? 0 : std::min( 1, (int)m_defaultGopLists.size() - 1 );
113
0
  xCreatePocToGopIdx( *m_gopList, !m_poc0idr, m_pocToGopIdx );
114
115
  // lets start with poc 0
116
0
  m_gopNum       = 0;
117
0
  m_nextPoc      = -leadFrames;
118
0
  m_pocOffset    = 0;
119
0
  m_cnOffset     = 0;
120
0
  m_numTillGop   = poc0idr ? 0 : (int)m_gopList->size() - 1;
121
0
  m_numTillIntra = poc0idr ? 0 : (int)m_gopList->size() - 1;
122
0
  m_minIntraDist = minIntraDist;
123
0
  m_lastIntraPOC = -1;
124
0
  m_adjustNoLPcodingOrder = m_refreshType == VVENC_DRT_IDR_NO_RADL && m_fixIntraPeriod <= m_maxGopSize;
125
0
}
126
127
void GOPCfg::getNextGopEntry( GOPEntry& gopEntry )
128
0
{
129
  // check for lead frames
130
0
  if( m_nextPoc < 0 )
131
0
  {
132
0
    const int pocAdj          = m_poc0idr ? 0 : 1;
133
0
    const bool isStartOfIntra = m_fixIntraPeriod > 0 ? ( ( m_nextPoc + pocAdj ) % m_fixIntraPeriod == 0 ) : false; ;
134
135
    // try to estimate temporal layer 0 leading frames
136
0
    bool isTl0 = false;
137
0
    if( m_fixIntraPeriod == 1 || m_picReordering == false || m_defGopSize <= 1 || isStartOfIntra )
138
0
    {
139
      // for AI or LD every frame is temporal layer 0
140
0
      isTl0 = true;
141
0
    }
142
0
    else
143
0
    {
144
      // dyadic gop with or without intra period
145
0
      if( m_fixIntraPeriod > 0 )
146
0
      {
147
0
        const int ipOffset  = -m_fixIntraPeriod * ( ( m_nextPoc - ( m_fixIntraPeriod - 1 ) ) / m_fixIntraPeriod );
148
0
        const int pocInIp   = m_nextPoc + ipOffset;
149
0
        isTl0 = ( pocInIp + pocAdj ) % m_defGopSize == 0;
150
0
      }
151
0
      else
152
0
      {
153
0
        isTl0 = ( ( m_nextPoc + pocAdj ) % m_defGopSize ) == 0;
154
0
      }
155
0
    }
156
157
    // create some fake gop entry for leading frames
158
0
    gopEntry.m_POC            = m_nextPoc;
159
0
    gopEntry.m_codingNum      = m_nextPoc;
160
0
    gopEntry.m_sliceType      = isStartOfIntra ? 'I' : 'B';
161
0
    gopEntry.m_temporalId     = isTl0 ? 0 : 1;
162
0
    gopEntry.m_isStartOfIntra = isStartOfIntra;
163
0
    gopEntry.m_isValid        = true;
164
0
    if( isStartOfIntra ) m_lastIntraPOC = m_nextPoc;
165
166
    // continue with next frame
167
0
    m_nextPoc += 1;
168
0
    return;
169
0
  }
170
171
0
  const int  pocInGop   = m_nextPoc - m_pocOffset;
172
0
  const int  gopId      = m_pocToGopIdx[ pocInGop % (int)m_pocToGopIdx.size() ];
173
0
  const bool isLeading0 = m_poc0idr && m_nextPoc == 0 ? true : false; // for poc0idr, we have a leading poc 0
174
  
175
0
  gopEntry             = (*m_gopList)[ gopId ];
176
0
  gopEntry.m_POC       = m_nextPoc;
177
0
  gopEntry.m_codingNum = isLeading0 ? 0 : m_cnOffset + gopId;
178
0
  gopEntry.m_gopNum    = m_gopNum;
179
0
  if( m_nextPoc != 0 && m_adjustNoLPcodingOrder )
180
0
  {
181
0
    xAdjustNoLPcodingOrder( gopEntry, gopId );
182
0
  }
183
0
  gopEntry.m_isValid   = true;
184
185
  // mark start of intra
186
0
  if( m_numTillIntra == 0 )
187
0
  {
188
0
    gopEntry.m_sliceType      = 'I';
189
0
    gopEntry.m_isStartOfIntra = true;
190
0
    gopEntry.m_temporalId     = 0;
191
0
    gopEntry.m_vtl            = 0;
192
0
    m_lastIntraPOC            = m_nextPoc;
193
0
  }
194
195
  // check for end of current gop
196
0
  CHECK( m_numTillIntra == 0 && m_numTillGop != 0, "start of new intra period only at start of new gop expected" );
197
0
  if( m_numTillGop == 0 )
198
0
  {
199
0
    const int prevGopSize = (int)m_gopList->size();
200
    // check for start of new intra period
201
0
    if( m_numTillIntra == 0 )
202
0
    {
203
0
      m_gopList      = &m_defaultGopLists[ 0 ];
204
0
      m_nextListIdx  = std::min( 1, (int)m_defaultGopLists.size() - 1 );
205
0
      m_numTillIntra = m_fixIntraPeriod;
206
0
      m_adjustNoLPcodingOrder = m_refreshType == VVENC_DRT_IDR_NO_RADL && m_fixIntraPeriod <= m_maxGopSize;
207
0
    }
208
0
    else
209
0
    {
210
0
      const int remainSize = m_numTillIntra > 0 ? std::min( m_defGopSize, m_numTillIntra ) : m_defGopSize;
211
0
      if( remainSize == (int)m_defaultGopLists[ m_nextListIdx ].size() && prevGopSize == m_defGopSize )
212
0
      {
213
0
        m_gopList     = &m_defaultGopLists[ m_nextListIdx ];
214
0
        m_nextListIdx = std::min( m_nextListIdx + 1, (int)m_defaultGopLists.size() - 1 );
215
0
      }
216
0
      else
217
0
      {
218
0
        CHECK( remainSize != (int)m_remainGopList.size() || prevGopSize != m_defGopSize, "remaining size does not match size of pre-calculated gop list" );
219
0
        m_gopList = &m_remainGopList;
220
0
      }
221
      //if we have a full gop, we don't need to change the coding order...
222
0
      m_adjustNoLPcodingOrder = m_refreshType == VVENC_DRT_IDR_NO_RADL && m_numTillIntra <= m_maxGopSize;
223
0
    }
224
0
    xCreatePocToGopIdx( *m_gopList, !m_poc0idr, m_pocToGopIdx );
225
0
    m_numTillGop  = (int)m_gopList->size();
226
0
    m_cnOffset   += isLeading0 ? 1 : prevGopSize;
227
0
    if( ! isLeading0 )
228
0
    {
229
0
      m_pocOffset += prevGopSize;
230
0
      m_gopNum    += 1;
231
0
    }
232
0
  }
233
234
  // continue with next pic
235
0
  m_nextPoc      += 1;
236
0
  m_numTillGop   -= 1;
237
0
  if( m_numTillIntra > 0 )
238
0
  {
239
0
    m_numTillIntra -= 1;
240
0
  }
241
0
}
242
243
void GOPCfg::startIntraPeriod( GOPEntry& gopEntry )
244
0
{
245
  // set gop entry
246
0
  gopEntry.m_sliceType      = 'I';
247
0
  gopEntry.m_isStartOfIntra = true;
248
0
  gopEntry.m_isStartOfGop   = true;
249
0
  gopEntry.m_temporalId     = 0;
250
0
  gopEntry.m_vtl            = 0;
251
0
  m_lastIntraPOC            = gopEntry.m_POC;
252
253
  // start with first gop list
254
0
  m_gopList      = &m_defaultGopLists[ 0 ];
255
0
  m_nextListIdx  = std::min( 1, (int)m_defaultGopLists.size() - 1 );
256
0
  m_numTillIntra = m_fixIntraPeriod;
257
0
  m_numTillGop   = (int)m_gopList->size();
258
259
0
  xCreatePocToGopIdx( *m_gopList, !m_poc0idr, m_pocToGopIdx );
260
261
  // process current / first I picture
262
0
  m_numTillGop  -= 1;
263
0
  if( m_numTillIntra > 0 )
264
0
  {
265
0
    m_numTillIntra -= 1;
266
0
  }
267
0
}
268
269
void GOPCfg::fixStartOfLastGop( GOPEntry& gopEntry )
270
0
{
271
0
  gopEntry.m_isStartOfGop = true;
272
0
  if( ! m_poc0idr && gopEntry.m_gopNum == 0 && ! gopEntry.m_isStartOfIntra )
273
0
  {
274
0
    gopEntry.m_isStartOfIntra = true;
275
0
    gopEntry.m_sliceType      = 'I';
276
0
    gopEntry.m_temporalId     = 0;
277
0
    gopEntry.m_vtl            = 0;
278
0
    m_lastIntraPOC            = gopEntry.m_POC;
279
0
  }
280
0
}
281
282
void GOPCfg::getDefaultRPLLists( RPLList& rpl0, RPLList& rpl1 ) const
283
0
{
284
0
  const int numRpl = (int)m_defaultRPLList.size();
285
  // TODO (jb): check size + 1, fix getNumRPL() as well
286
0
  rpl0.resize( numRpl + 1 );
287
0
  rpl1.resize( numRpl + 1 );
288
0
  for( int i = 0; i < numRpl; i++ )
289
0
  {
290
0
    rpl0[ i ].initFromGopEntry( *m_defaultRPLList[ i ], 0 );
291
0
    rpl1[ i ].initFromGopEntry( *m_defaultRPLList[ i ], 1 );
292
0
  }
293
0
}
294
295
bool GOPCfg::isSTAallowed( int poc ) const
296
0
{
297
0
  int intraDistBack    = poc - m_lastIntraPOC;
298
0
  int intraDistForward = m_numTillIntra + 1;
299
  
300
  //if intraDistBack == 0 we have a regular I-Slice and there we need to do the STA analysis as well
301
0
  return ( ( intraDistBack >= m_minIntraDist && intraDistForward >= m_minIntraDist ) || intraDistBack == 0 );
302
0
}
303
304
bool GOPCfg::hasNonZeroTemporalId() const
305
0
{
306
0
  return m_maxTid > 0;
307
0
}
308
309
bool GOPCfg::hasLeadingPictures() const
310
0
{
311
0
  for( const auto& gopEntry : m_defaultGopLists.back() )
312
0
  {
313
0
    if( ! gopEntry.m_useBckwdOnly )
314
0
    {
315
0
      return true;
316
0
    }
317
0
  }
318
0
  return false;
319
0
}
320
321
bool GOPCfg::isChromaDeltaQPEnabled() const
322
0
{
323
0
  for( const auto& gopEntry : m_defaultGopLists.back() )
324
0
  {
325
0
    if( gopEntry.m_CbQPoffset || gopEntry.m_CrQPoffset )
326
0
    {
327
0
      return true;
328
0
    }
329
0
  }
330
0
  return false;
331
0
}
332
333
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
334
335
int GOPCfg::xGetMinPoc( int maxGopSize, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ] ) const
336
0
{
337
0
  int minPoc = 0;
338
0
  for( int i = 0; i < maxGopSize; i++ )
339
0
  {
340
0
    for( int l = 0; l < 2; l++ )
341
0
    {
342
0
      for( int j = 0; j < cfgGopList[ i ].m_numRefPics[ l ]; j++ )
343
0
      {
344
0
        const int refPoc = cfgGopList[ i ].m_POC - cfgGopList[ i ].m_deltaRefPics[ l ][ j ];
345
0
        if( refPoc < minPoc )
346
0
          minPoc = refPoc;
347
0
      }
348
0
    }
349
0
  }
350
0
  return minPoc;
351
0
}
352
353
void GOPCfg::xCreateGopList( int maxGopSize, int gopSize, int pocOffset, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ], const GOPEntryList* prevGopList, GOPEntryList& gopList ) const
354
0
{
355
0
  const bool bSkipPrev = prevGopList == nullptr || (int)prevGopList->size() < maxGopSize;
356
0
  std::vector< std::pair<int, int> > prevGopRefs;
357
0
  if( ! bSkipPrev )
358
0
  {
359
0
    xGetPrevGopRefs( prevGopList, prevGopRefs );
360
0
  }
361
362
  //
363
  // copy given gop configuration
364
  //
365
366
0
  gopList.clear();
367
0
  gopList.resize( maxGopSize );
368
0
  for( int i = 0; i < maxGopSize; i++ )
369
0
  {
370
0
    gopList[ i ].setDefaultGOPEntry();
371
0
    gopList[ i ].copyFromGopCfg( cfgGopList[ i ] );
372
0
  }
373
374
  // find max temporal id
375
0
  const int maxTid = xGetMaxTid( gopList );
376
377
  // prune gop list
378
0
  CHECK( gopSize > maxGopSize, "only pruning of gop list supported" );
379
0
  if( gopSize < maxGopSize )
380
0
  {
381
0
    xPruneGopList( gopSize, gopList );
382
0
  }
383
384
  // 
385
  // fix prediction pattern
386
  //
387
388
  // use list of available entries for prediction
389
0
  std::vector<GOPEntry*> availList;
390
0
  availList.resize( gopSize + 1, nullptr );
391
0
  GOPEntry e1;
392
0
  if( prevGopRefs.size() == 0 || prevGopRefs[ 0 ].first == 0 )
393
0
  {
394
    // insert leading poc 0 to be available
395
0
    CHECK( prevGopRefs.size() && prevGopRefs[ 0 ].first == 0 && prevGopRefs[ 0 ].second != 0, "gop structure should start and end with temporalId 0" );
396
0
    e1.setDefaultGOPEntry();
397
0
    e1.m_POC = 0;
398
0
    availList[ 0 ] = &e1;
399
    // remove leading poc 0 from prev 
400
0
    if( prevGopRefs.size() && prevGopRefs[ 0 ].first == 0 )
401
0
    {
402
0
      for( int i = 0; i < (int)prevGopRefs.size() - 1; i++ )
403
0
      {
404
0
        prevGopRefs[ i ] = prevGopRefs[ i + 1 ];
405
0
      }
406
0
      prevGopRefs.pop_back();
407
0
    }
408
0
  }
409
410
  // loop over gop list
411
0
  std::vector<int> newFwd;
412
0
  std::vector<int> newBckwd;
413
0
  std::vector<int> availFwd;
414
0
  std::vector<int> availBckwd;
415
0
  newFwd.reserve( MAX_REF_PICS );
416
0
  newBckwd.reserve( MAX_REF_PICS );
417
0
  availFwd.reserve( MAX_REF_PICS );
418
0
  availBckwd.reserve( MAX_REF_PICS );
419
0
  for( int i = 0; i < gopList.size(); i++ )
420
0
  {
421
0
    GOPEntry* gopEntry = &gopList[ i ];
422
423
    // loop over both reference lists
424
0
    for( int l = 0; l < 2; l++ )
425
0
    {
426
0
      newFwd.clear();
427
0
      newBckwd.clear();
428
0
      availFwd.clear();
429
0
      availBckwd.clear();
430
431
      // fill list of possible reference pictures in forward / backward direction
432
0
      xAddRefPicsFwd( availFwd, gopEntry, availList );
433
0
      xAddRefPicsBckwd( availBckwd, gopEntry, availList );
434
      // access to previous gop not used/allowed for non-ref pics at highest temporal layer
435
0
      if( gopEntry->m_temporalId <= 1 || gopEntry->m_temporalId < maxTid )
436
0
      {
437
0
        xAddRefPicsPrevGOP( availBckwd, gopEntry, prevGopRefs );
438
0
      }
439
440
      // check / fix all active references
441
0
      for( int j = 0; j < gopEntry->m_numRefPicsActive[ l ]; j++ )
442
0
      {
443
0
        const int delta = gopEntry->m_deltaRefPics[ l ][ j ];
444
0
        CHECK( delta == 0, "error in gop configuration: try to reference own picture" );
445
        // start with forward or backward prediction
446
0
        std::vector<int>& sameDir   = delta < 0 ? availFwd   : availBckwd;
447
0
        std::vector<int>& invertDir = delta < 0 ? availBckwd : availFwd;
448
        // check if candidates are available in same direction list
449
0
        if( sameDir.size() )
450
0
        {
451
          // try to find correct match
452
0
          auto itr = find( sameDir.begin(), sameDir.end(), delta );
453
0
          if( itr == sameDir.end() )
454
0
          {
455
            // no match found, choose replacement
456
0
            itr = sameDir.begin();
457
0
          }
458
          // use delta candidate and remove candidate to prevent doublets
459
0
          std::vector<int>& newDst = *itr < 0 ? newFwd : newBckwd;
460
0
          newDst.push_back( *itr );
461
0
          sameDir.erase( itr );
462
0
        }
463
0
        else if( invertDir.size() )
464
0
        {
465
          // corret match not possible, we are inverting the prediction direction
466
0
          auto itr = invertDir.begin();
467
          // use delta candidate and remove candidate to prevent doublets
468
0
          std::vector<int>& newDst = *itr < 0 ? newFwd : newBckwd;
469
0
          newDst.push_back( *itr );
470
0
          invertDir.erase( itr );
471
0
        }
472
0
      }
473
474
      // clear old delta list
475
0
      std::memset( gopEntry->m_deltaRefPics[ l ], 0, sizeof( gopEntry->m_deltaRefPics[ l ] ) );
476
477
      // ensure new delta lists are sorted
478
0
      std::sort( newFwd.begin(), newFwd.end(),     []( auto& a, auto& b ){ return a > b; } );
479
0
      std::sort( newBckwd.begin(), newBckwd.end(), []( auto& a, auto& b ){ return a < b; } );
480
481
      // copy new delta poc list
482
0
      CHECK( (int)newFwd.size() + (int)newBckwd.size() >= VVENC_MAX_NUM_REF_PICS, "number of delta ref pic entries exceeds array bounds" );
483
0
      gopEntry->m_numRefPics[ l ]       = (int)newFwd.size() + (int)newBckwd.size();
484
0
      gopEntry->m_numRefPicsActive[ l ] = (int)newFwd.size() + (int)newBckwd.size();
485
0
      std::vector<int>& src1 = l == 0 ? newBckwd : newFwd;
486
0
      std::vector<int>& src2 = l == 0 ? newFwd   : newBckwd;
487
0
      int dstIdx = 0;
488
0
      for( int j = 0; j < (int)src1.size(); j++ )
489
0
      {
490
0
        gopEntry->m_deltaRefPics[ l ][ dstIdx ] = src1[ j ];
491
0
        dstIdx += 1;
492
0
      }
493
0
      for( int j = 0; j < (int)src2.size(); j++ )
494
0
      {
495
0
        gopEntry->m_deltaRefPics[ l ][ dstIdx ] = src2[ j ];
496
0
        dstIdx += 1;
497
0
      }
498
0
    }
499
500
    // available for later use as ref
501
0
    availList[ gopEntry->m_POC ] = gopEntry;
502
0
  }
503
504
  //
505
  // add entries to ref lists, which are required as reference for later pictures in coding order
506
  //
507
508
  // create fake entry as start of potential next gop, 
509
  // to ensure pictures referenced from next gop are keept alive
510
0
  GOPEntry e2;
511
0
  e2.setDefaultGOPEntry();
512
0
  e2.m_POC           = 2 * maxGopSize;
513
0
  e2.m_temporalId    = 0;
514
0
  const bool bIsLast = m_maxGopSize != gopSize;
515
0
  if( ! bIsLast )
516
0
  {
517
0
    std::vector<int> pocList;
518
0
    pocList.reserve( gopSize );
519
0
    xGetRefsOfNextGop( gopSize, cfgGopList, pocOffset, pocList );
520
0
    e2.m_numRefPics[ 0 ] = (int)pocList.size();
521
0
    for( int i = 0; i < (int)pocList.size(); i++ )
522
0
    {
523
0
      const int reqPoc = pocList[ i ] + maxGopSize; // shift poc from next to current gop
524
0
      e2.m_deltaRefPics[ 0 ][ i ] = e2.m_POC - reqPoc;
525
0
    }
526
0
  }
527
0
  const GOPEntry* prevEntry = &e2;
528
529
  // traverse gop list from back to front
530
0
  for( int i = gopSize - 1; i >= 0; i-- )
531
0
  {
532
0
    GOPEntry* gopEntry = &gopList[ i ];
533
    // loop over all ref lists
534
0
    for( int j = 0; j < 2; j++ )
535
0
    {
536
      // loop over all delta POCs in previous entry
537
0
      for( int k = 0; k < prevEntry->m_numRefPics[ j ]; k++ )
538
0
      {
539
0
        const int reqPoc = prevEntry->m_POC - prevEntry->m_deltaRefPics[ j ][ k ];
540
0
        if( reqPoc == gopEntry->m_POC )
541
0
        {
542
0
          continue;
543
0
        }
544
0
        bool bAvail = false;
545
        // search if required poc is also part of the ref lists of current gop entry
546
0
        for( int l = 0; l < 2; l++ )
547
0
        {
548
0
          for( int m = 0; m < gopEntry->m_numRefPics[ l ]; m++ )
549
0
          {
550
0
            const int availPoc = gopEntry->m_POC - gopEntry->m_deltaRefPics[ l ][ m ];
551
0
            if( reqPoc == availPoc )
552
0
            {
553
0
              bAvail = true;
554
0
              break;
555
0
            }
556
0
          }
557
0
          if( bAvail )
558
0
          {
559
0
            break;
560
0
          }
561
0
        }
562
        // if required poc not found, add this poc to current entry lists
563
0
        if( ! bAvail )
564
0
        {
565
0
          const int delta   = gopEntry->m_POC - reqPoc;
566
0
          const int addList = delta > 0 ? 0 : 1; // add to backward L0 or forward L1 list
567
0
          CHECK( gopEntry->m_numRefPics[ addList ] >= VVENC_MAX_NUM_REF_PICS, "out of array bounds" );
568
0
          gopEntry->m_deltaRefPics[ addList ][ gopEntry->m_numRefPics[ addList ] ] = delta;
569
0
          gopEntry->m_numRefPics[ addList ] += 1;
570
          // small optimization: sort additional poc by value to decrease required bits for encoding the list
571
0
          const int sign = delta > 0 ? 1 : -1;
572
0
          for( int n = gopEntry->m_numRefPics[ addList ] - 1; n > gopEntry->m_numRefPicsActive[ addList ]; n-- )
573
0
          {
574
0
            if( gopEntry->m_deltaRefPics[ addList ][ n ] * sign > gopEntry->m_deltaRefPics[ addList ][ n - 1 ] * sign )
575
0
              break;
576
0
            std::swap( gopEntry->m_deltaRefPics[ addList ][ n ], gopEntry->m_deltaRefPics[ addList ][ n - 1 ] );
577
0
          }
578
0
        }
579
0
      }
580
0
    }
581
0
    prevEntry = gopEntry;
582
0
  }
583
584
  // poc to gop map for fastwr access
585
0
  std::vector<int> pocToGopIdx;
586
0
  xCreatePocToGopIdx( gopList, false, pocToGopIdx );
587
588
  // mark first gop entry
589
0
  gopList[ pocToGopIdx[ 0 ] ].m_isStartOfGop = true;
590
591
  // STSA, forward B flags, MCTF index, virtual TLayer
592
0
  xSetSTSA     ( gopList, pocToGopIdx );
593
0
  xSetBckwdOnly( gopList );
594
0
  xSetMctfIndex( maxGopSize, gopList );
595
0
  xSetVTL      ( gopList );
596
0
  if( m_firstPassMode == 2 || m_firstPassMode == 4 )
597
0
  {
598
0
    xSetSkipFirstPass( gopList );
599
0
  }
600
0
}
601
602
void GOPCfg::xGetPrevGopRefs( const GOPEntryList* prevGopList, std::vector< std::pair<int, int> >& prevGopRefs ) const
603
0
{
604
0
  if( ! prevGopList )
605
0
  {
606
0
    return;
607
0
  }
608
609
0
  prevGopRefs.reserve( MAX_REF_PICS );
610
611
0
  const int gopSize         = (int)prevGopList->size();
612
0
  const GOPEntry& lastEntry = prevGopList->back();
613
614
  // last encoded picture available for reference
615
0
  prevGopRefs.push_back( std::make_pair( lastEntry.m_POC, lastEntry.m_temporalId ) );
616
617
  // insert all available reference pictures
618
0
  for( int l = 0; l < 2; l++ )
619
0
  {
620
0
    for( int i = 0; i < lastEntry.m_numRefPics[ l ]; i++ )
621
0
    {
622
      // check referenced poc already found
623
0
      const int refPoc = lastEntry.m_POC - lastEntry.m_deltaRefPics[ l ][ i ];
624
0
      bool bFound = false;
625
0
      for( int j = 0; j < (int)prevGopRefs.size(); j++ )
626
0
      {
627
0
        if( prevGopRefs[ j ].first == refPoc )
628
0
        {
629
0
          bFound = true;
630
0
          break;
631
0
        }
632
0
      }
633
0
      if( bFound )
634
0
      {
635
0
        continue;
636
0
      }
637
      // find temporal layer of referenced poc
638
0
      int refTid = -1;
639
0
      int cmpPoc = refPoc;
640
0
      if( cmpPoc < 0 )
641
0
      {
642
        // ensure value positive
643
0
        const int numGops = -1 * cmpPoc / gopSize + 1;
644
0
        cmpPoc += numGops * gopSize;
645
0
      }
646
0
      cmpPoc = cmpPoc % gopSize;
647
0
      for( int j = 0; j < gopSize; j++ )
648
0
      {
649
0
        if( ( (*prevGopList)[ j ].m_POC % gopSize ) == cmpPoc )
650
0
        {
651
0
          refTid = (*prevGopList)[ j ].m_temporalId;
652
0
          break;
653
0
        }
654
0
      }
655
0
      CHECK( refTid < 0, "error in gop configuration: gop entry not found or temporalId negative" );
656
      // store referenced poc
657
0
      prevGopRefs.push_back( std::make_pair( refPoc, refTid ) );
658
0
    }
659
0
  }
660
661
  // correct poc of referenced pictures by previous gop size
662
0
  for( int i = 0; i < (int)prevGopRefs.size(); i++ )
663
0
  {
664
0
    prevGopRefs[ i ].first -= gopSize;
665
0
    CHECK( prevGopRefs[ i ].first > 0, "error in gop configuration: reference picture points to future gop" );
666
0
  }
667
668
0
  std::sort( prevGopRefs.begin(), prevGopRefs.end(), []( auto& a, auto& b ){ return a.first > b.first; } );
669
0
}
670
671
void GOPCfg::xPruneGopList( int gopSize, GOPEntryList& gopList ) const
672
0
{
673
0
  const int oldSize = (int)gopList.size();
674
0
  CHECK( oldSize <= gopSize, "gop list to short for prunning" );
675
676
0
  std::vector<GOPEntry> prunedList;
677
0
  prunedList.reserve( gopSize );
678
0
  for( int i = 0; i < (int)gopList.size(); i++ )
679
0
  {
680
0
    if( gopList[ i ].m_POC == oldSize )
681
0
    {
682
      // always include end of old gop, which is the start of following gop
683
0
      prunedList.push_back( gopList[ i ] );
684
      // fix poc and delta lists of new end of pruned gop
685
0
      GOPEntry& gopEntry = prunedList.back();
686
0
      const int sizeDiff = oldSize - gopSize;
687
0
      for( int l = 0; l < 2; l++ )
688
0
      {
689
0
        for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j++ )
690
0
        {
691
0
          if( gopEntry.m_deltaRefPics[ l ][ j ] > sizeDiff )
692
0
            gopEntry.m_deltaRefPics[ l ][ j ] -= sizeDiff;
693
0
          else
694
0
            gopEntry.m_deltaRefPics[ l ][ j ] = gopSize; // ref points inside removed pics, lets point to start of gop (poc 0) as default
695
0
        }
696
0
      }
697
0
      gopEntry.m_POC -= sizeDiff;
698
0
    }
699
0
    else if( gopList[ i ].m_POC < gopSize )
700
0
    {
701
0
      prunedList.push_back( gopList[ i ] );
702
0
    }
703
0
  }
704
705
0
  gopList.clear();
706
0
  gopList.resize( (int)prunedList.size() );
707
0
  for( int i = 0; i < (int)prunedList.size(); i++ )
708
0
  {
709
0
    gopList[ i ] = prunedList[ i ];
710
0
  }
711
712
0
  CHECK( gopList.size() != gopSize, "pruned gop list incomplete" );
713
0
}
714
715
void GOPCfg::xGetRefsOfNextGop( int gopSize, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ], int pocOffset, std::vector<int>& pocList ) const
716
0
{
717
0
  const int minPoc = -1 * ( pocOffset + gopSize );
718
0
  for( int i = 0; i < gopSize; i++ )
719
0
  {
720
0
    for( int l = 0; l < 2; l++ )
721
0
    {
722
0
      for( int j = 0; j < cfgGopList[ i ].m_numRefPics[ l ]; j++ )
723
0
      {
724
0
        const int refPoc = cfgGopList[ i ].m_POC - cfgGopList[ i ].m_deltaRefPics[ l ][ j ];
725
0
        if( refPoc >= minPoc && refPoc < 0 )
726
0
        {
727
0
          auto itr = find( pocList.begin(), pocList.end(), refPoc );
728
0
          if( itr == pocList.end() )
729
0
            pocList.push_back( refPoc );
730
0
        }
731
0
      }
732
0
    }
733
0
  }
734
0
  std::sort( pocList.begin(), pocList.end() );
735
0
}
736
737
void GOPCfg::xSetMctfIndex( int maxGopSize, GOPEntryList& gopList ) const
738
0
{
739
0
  for( auto& gopEntry : gopList )
740
0
  {
741
0
    int poc = gopEntry.m_POC;
742
    // for pruned gops max poc is start / end of gop and should be filtered, but the used poc has been changed 
743
    // to pruned gop size and does not fit the original config anymore => set back original max gop size poc
744
0
    if( (int)gopList.size() < maxGopSize && poc == (int)gopList.size() )
745
0
    {
746
0
      poc = maxGopSize;
747
0
    }
748
    // find mctf index
749
0
    for( int i = m_mctfCfg->numFrames - 1; i >= 0; i-- )
750
0
    {
751
0
      if( ( poc % m_mctfCfg->MCTFFrames[ i ] ) == 0 )
752
0
      {
753
0
        gopEntry.m_mctfIndex = i;
754
0
        break;
755
0
      }
756
0
    }
757
0
  }
758
0
}
759
760
void GOPCfg::xSetSkipFirstPass( GOPEntryList& gopList ) const
761
0
{
762
  // find max temporal id
763
0
  const int maxTid = xGetMaxTid( gopList );
764
765
  // skip first pass encoding after first frame at max temporal id has been encoded
766
0
  if( maxTid > 1 )
767
0
  {
768
0
    bool bSkipRem = false;
769
0
    for( auto& gopEntry : gopList )
770
0
    {
771
0
      gopEntry.m_skipFirstPass = bSkipRem;
772
0
      bSkipRem |= gopEntry.m_temporalId == maxTid;
773
0
    }
774
0
  }
775
0
}
776
777
void GOPCfg::xCreatePocToGopIdx( const GOPEntryList& gopList, bool bShift, std::vector<int>& pocToGopIdx ) const
778
0
{
779
0
  const int gopSize   = (int)gopList.size();
780
0
  const int pocOffset = bShift ? -1 : 0;
781
782
0
  pocToGopIdx.clear();
783
0
  pocToGopIdx.resize( gopSize, -1 );
784
785
0
  for( int i = 0; i < gopSize; i++ )
786
0
  {
787
0
    const GOPEntry& gopEntry = gopList[ i ];
788
0
    const int poc            = ( gopEntry.m_POC + pocOffset ) % gopSize;
789
0
    CHECK( gopEntry.m_POC > gopSize || gopEntry.m_POC < 1, "error: poc out of range" );
790
0
    CHECK( pocToGopIdx[ poc ] != -1,                       "error: multiple entries in gop list map to same poc" );
791
0
    pocToGopIdx[ poc ] = i;
792
0
  }
793
0
  for( int i = 0; i < gopSize; i++ )
794
0
  {
795
0
    CHECK( pocToGopIdx[ i ] < 0, "error: poc not found in gop list" );
796
0
  }
797
0
}
798
799
void GOPCfg::xSetSTSA( GOPEntryList& gopList, const std::vector<int>& pocToGopIdx ) const
800
0
{
801
  // check STSA condition satisfied for each GOPEntry
802
0
  std::vector<bool> isLocalSTSA;
803
0
  isLocalSTSA.resize( gopList.size(), true );
804
0
  for( int i = 0; i < (int)gopList.size(); i++ )
805
0
  {
806
0
    const GOPEntry& gopEntry = gopList[ i ];
807
    // check all ref pics have higher temporal level
808
0
    for( int l = 0; l < 2 && isLocalSTSA[ i ]; l++ )
809
0
    {
810
0
      for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ )
811
0
      {
812
0
        const int refPoc = gopEntry.m_POC - gopEntry.m_deltaRefPics[ l ][ j ];
813
0
        if( refPoc < 0 )
814
0
        {
815
0
          continue;
816
0
        }
817
0
        const int gopId  = pocToGopIdx[ refPoc % (int)pocToGopIdx.size() ];
818
0
        const GOPEntry& refEntry = gopList[ gopId ];
819
0
        if( refEntry.m_temporalId >= gopEntry.m_temporalId )
820
0
        {
821
0
          isLocalSTSA[ i ] = false;
822
0
          break;
823
0
        }
824
0
      }
825
0
    }
826
0
  }
827
828
  // accumulate STSA condition for all pics of same temporal level
829
0
  const int maxTid = xGetMaxTid( gopList );
830
0
  std::vector<bool> isTlSTSA;
831
0
  isTlSTSA.resize( maxTid + 1, true );
832
0
  for( int i = 0; i < (int)gopList.size(); i++ )
833
0
  {
834
0
    const GOPEntry& gopEntry = gopList[ i ];
835
0
    const int tid            = gopEntry.m_temporalId;
836
0
    const bool isSTSA        = isTlSTSA[ tid ] && isLocalSTSA[ i ];
837
0
    isTlSTSA[ tid ]          = isSTSA;
838
0
  }
839
840
  // set STSA flag for each GOPEntry
841
0
  for( auto& gopEntry : gopList )
842
0
  {
843
0
    const int tid = gopEntry.m_temporalId;
844
0
    gopEntry.m_isSTSA = isTlSTSA[ tid ];
845
0
  }
846
0
}
847
848
void GOPCfg::xSetBckwdOnly( GOPEntryList& gopList ) const
849
0
{
850
0
  for( auto& gopEntry : gopList )
851
0
  {
852
0
    bool useBckwdOnly = true;
853
0
    for( int l = 0; l < 2 && useBckwdOnly; l++ )
854
0
    {
855
0
      for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ )
856
0
      {
857
0
        if( gopEntry.m_deltaRefPics[ l ][ j ] < 0 )
858
0
        {
859
0
          useBckwdOnly = false;
860
0
          break;
861
0
        }
862
0
      }
863
0
    }
864
0
    gopEntry.m_useBckwdOnly = useBckwdOnly;
865
0
  }
866
0
}
867
868
void GOPCfg::xSetVTL( GOPEntryList& gopList ) const
869
0
{
870
0
  if( m_picReordering == 0 )
871
0
  {
872
0
    const int vtl = std::min( m_maxGopSize >> 2, 3 );
873
0
    for( auto& gopEntry : gopList )
874
0
    {
875
0
      CHECK( gopEntry.m_temporalId != 0, "unexpected low delay temporal id" );
876
0
      if( !gopEntry.m_isStartOfGop && !gopEntry.m_isStartOfIntra )
877
0
      {
878
0
        gopEntry.m_vtl = vtl;
879
0
      }
880
0
    }
881
0
  }
882
0
  else
883
0
  {
884
0
    for( auto& gopEntry : gopList )
885
0
    {
886
0
      gopEntry.m_vtl = gopEntry.m_temporalId;
887
0
    }
888
0
  }
889
0
}
890
891
void GOPCfg::xSetDefaultRPL( std::vector<GOPEntryList>& defaultLists )
892
0
{
893
0
  m_defaultRPLList.clear();
894
895
  // default RPL index is entry position in gop
896
0
  GOPEntryList& gopList = defaultLists.back();
897
0
  for( auto& gopEntry : gopList )
898
0
  {
899
0
    gopEntry.m_defaultRPLIdx = (int)m_defaultRPLList.size();
900
0
    m_defaultRPLList.push_back( &gopEntry );
901
0
  }
902
903
  // in prev lists set extra index if RPL list deviates from default list
904
0
  for( int i = 0; i < (int)defaultLists.size() - 1; i++ )
905
0
  {
906
0
    GOPEntryList& prevList = defaultLists[ i ];
907
0
    CHECK( gopList.size() != prevList.size(), "default gop list and first gop list have to have same size" );
908
0
    for( int i = 0; i < (int)prevList.size(); i++ )
909
0
    {
910
0
      CHECK( prevList[ i ].m_POC != gopList[ i ].m_POC, "default gop list and first gop List have to have same poc layout" );
911
0
      GOPEntry& gopEntry = prevList[ i ];
912
0
      GOPEntry& cmpEntry = gopList[ i ];
913
0
      bool haveSameList  = true;
914
0
      for( int l = 0; l < 2 && haveSameList; l++ )
915
0
      {
916
0
        if( gopEntry.m_numRefPics[ l ] != cmpEntry.m_numRefPics[ l ] )
917
0
        {
918
0
          haveSameList = false;
919
0
          break;
920
0
        }
921
0
        for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ )
922
0
        {
923
0
          if( gopEntry.m_deltaRefPics[ l ][ j ] != cmpEntry.m_deltaRefPics[ l ][ j ] )
924
0
          {
925
0
            haveSameList = false;
926
0
            break;
927
0
          }
928
0
        }
929
0
      }
930
0
      if( haveSameList )
931
0
      {
932
0
        gopEntry.m_defaultRPLIdx = cmpEntry.m_defaultRPLIdx;
933
0
      }
934
0
      else
935
0
      {
936
0
        gopEntry.m_defaultRPLIdx = (int)m_defaultRPLList.size();
937
0
        m_defaultRPLList.push_back( &gopEntry );
938
0
      }
939
0
    }
940
0
  }
941
942
0
  std::vector<int> hist[ 2 ];
943
0
  hist[ 0 ].resize( MAX_NUM_REF, 0 );
944
0
  hist[ 1 ].resize( MAX_NUM_REF, 0 );
945
0
  for( auto gopEntry : m_defaultRPLList )
946
0
  {
947
0
    for( int l = 0; l < 2; l++ )
948
0
    {
949
0
      CHECK( gopEntry->m_numRefPicsActive[ l ] < 0 || gopEntry->m_numRefPicsActive[ l ] >= MAX_NUM_REF, "array index out of bounds");
950
0
      hist[ l ][ gopEntry->m_numRefPicsActive[ l ] ] += 1;
951
0
    }
952
0
  }
953
0
  for( int l = 0; l < 2; l++ )
954
0
  {
955
0
    int bestIdx = 0;
956
0
    int maxVal  = -1;
957
0
    for( int j = 0; j < (int)hist[ l ].size(); j++ )
958
0
    {
959
0
      if( hist[ l ][ j ] > maxVal )
960
0
      {
961
0
        bestIdx = j;
962
0
        maxVal  = hist[ l ][ j ];
963
0
      }
964
0
    }
965
0
    m_defaultNumActive[ l ] = bestIdx;
966
0
  }
967
0
}
968
969
void GOPCfg::xSetDBPConstraints( std::vector<GOPEntryList>& defaultLists )
970
0
{
971
0
  const GOPEntryList& gopList = defaultLists.back();
972
973
0
  m_maxTid = xGetMaxTid( gopList );
974
975
0
  m_maxDecPicBuffering.resize( m_maxTid + 1, 1 );
976
0
  m_numReorderPics.resize    ( m_maxTid + 1, 0 );
977
978
  // max DPB size and number reorder pics per temporal level
979
0
  for( const auto& gopEntry : gopList )
980
0
  {
981
0
    const int numRefPics = xGetMaxRefPics   ( gopEntry );
982
0
    const int numReorder = xGetMaxNumReorder( gopEntry, gopList );
983
0
    const int tid        = gopEntry.m_temporalId;
984
0
    m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], numRefPics + 1 );
985
0
    m_numReorderPics    [ tid ] = std::max( m_numReorderPics[ tid ], numReorder );
986
0
  }
987
988
  // the value of num_reorder_pics[ i ] shall be in the range of 0 to max_dec_pic_buffering[ i ] - 1, inclusive
989
0
  m_maxDecPicBuffering[ 0 ] = std::max( m_maxDecPicBuffering[ 0 ], m_numReorderPics[ 0 ] +1 );
990
0
  for( int tid = 1; tid < m_maxTid; tid++ )
991
0
  {
992
    // a lower layer can not have higher reorder value than a higher layer
993
0
    m_numReorderPics[ tid ] = std::max( m_numReorderPics[ tid ], m_numReorderPics[ tid - 1 ] );
994
    // the value of num_reorder_pics[ i ] shall be in the range of 0 to max_dec_pic_buffering[ i ] - 1, inclusive
995
0
    m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], m_numReorderPics[ tid ] +1 );
996
    // a lower layer can not have higher DPB size value than a higher layer
997
0
    m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], m_maxDecPicBuffering[ tid - 1 ] );
998
0
  }
999
0
}
1000
1001
bool GOPCfg::xCheckDBPConstraints( const GOPEntryList& gopList ) const
1002
0
{
1003
0
  if( gopList.size() && gopList[ 0 ].m_temporalId != 0 )
1004
0
  {
1005
0
    msg.log( VVENC_ERROR, "first entry in gop list must have temporal id 0" );
1006
0
    return false;
1007
0
  }
1008
0
  const int maxTid = xGetMaxTid( gopList );
1009
0
  if( maxTid > m_maxTid )
1010
0
  {
1011
0
    msg.log( VVENC_ERROR, "max temporal level exceeds overall maximum value" );
1012
0
    return false;
1013
0
  }
1014
0
  for( const auto& gopEntry : gopList )
1015
0
  {
1016
0
    const int numRefPics = xGetMaxRefPics   ( gopEntry );
1017
0
    const int numReorder = xGetMaxNumReorder( gopEntry, gopList );
1018
0
    const int tid        = gopEntry.m_temporalId;
1019
0
    if( numRefPics + 1 > m_maxDecPicBuffering[ tid ] )
1020
0
    {
1021
0
      msg.log( VVENC_ERROR, "max DPB size exceeds overall maximum value" );
1022
0
      return false;
1023
0
    }
1024
0
    if( numReorder > m_numReorderPics[ tid ] )
1025
0
    {
1026
0
      msg.log( VVENC_ERROR, "max number reorder pics exceeds overall maximum value" );
1027
0
      return false;
1028
0
    }
1029
0
  }
1030
0
  return true;
1031
0
}
1032
1033
void GOPCfg::xAddRefPicsBckwd( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector<GOPEntry*>& availList ) const
1034
0
{
1035
0
  for( int i = ( gopEntry->m_POC - 1 ); i >= 0; i-- )
1036
0
  {
1037
0
    const GOPEntry* refEntry = availList[ i ];
1038
0
    if( refEntry )
1039
0
    {
1040
0
      if( refEntry->m_temporalId <= gopEntry->m_temporalId )
1041
0
      {
1042
0
        deltaList.push_back( gopEntry->m_POC - refEntry->m_POC );
1043
0
        CHECK( deltaList.back() <= 0, "error in backward list: try to access future ref" );
1044
0
      }
1045
0
    }
1046
0
  }
1047
0
}
1048
1049
void GOPCfg::xAddRefPicsFwd( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector<GOPEntry*>& availList ) const
1050
0
{
1051
0
  for( int i = ( gopEntry->m_POC + 1 ); i < (int)availList.size(); i++ )
1052
0
  {
1053
0
    const GOPEntry* refEntry = availList[ i ];
1054
0
    if( refEntry )
1055
0
    {
1056
0
      if( refEntry->m_temporalId <= gopEntry->m_temporalId )
1057
0
      {
1058
0
        deltaList.push_back( gopEntry->m_POC - refEntry->m_POC );
1059
0
        CHECK( deltaList.back() >= 0, "error in forward list: try to access past ref" );
1060
0
      }
1061
0
    }
1062
0
  }
1063
0
}
1064
1065
void GOPCfg::xAddRefPicsPrevGOP( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector< std::pair<int, int> >& prevGopRefs ) const
1066
0
{
1067
0
  for( int i = 0; i < (int)prevGopRefs.size(); i++ )
1068
0
  {
1069
0
    if( prevGopRefs[ i ].second <= gopEntry->m_temporalId )
1070
0
    {
1071
0
      deltaList.push_back( gopEntry->m_POC - prevGopRefs[ i ].first );
1072
0
      CHECK( deltaList.back() <= 0, "error in backward list: try to access future ref" );
1073
0
    }
1074
0
  }
1075
0
}
1076
1077
int GOPCfg::xGetMaxTid( const GOPEntryList& gopList ) const
1078
0
{
1079
0
  int maxTid = 0;
1080
0
  for( auto& gopEntry : gopList )
1081
0
  {
1082
0
    if( gopEntry.m_temporalId > maxTid )
1083
0
    {
1084
0
      maxTid = gopEntry.m_temporalId;
1085
0
    }
1086
0
  }
1087
0
  return maxTid;
1088
0
}
1089
1090
int GOPCfg::xGetMaxRefPics( const GOPEntry& gopEntry ) const
1091
0
{
1092
0
  int numRefPics = gopEntry.m_numRefPics[ 0 ];
1093
0
  for( int j = 0; j < gopEntry.m_numRefPics[ 1 ]; j++ )
1094
0
  {
1095
0
    bool inL0 = false;
1096
0
    for( int k = 0; k < gopEntry.m_numRefPics[ 0 ]; k++ )
1097
0
    {
1098
0
      if( gopEntry.m_deltaRefPics[ 1 ][ j ] == gopEntry.m_deltaRefPics[ 0 ][ k ] )
1099
0
      {
1100
0
        inL0 = true;
1101
0
        break;
1102
0
      }
1103
0
    }
1104
0
    if( ! inL0 )
1105
0
    {
1106
0
      numRefPics += 1;
1107
0
    }
1108
0
  }
1109
0
  return numRefPics;
1110
0
}
1111
1112
int GOPCfg::xGetMaxNumReorder( const GOPEntry& gopEntry, const GOPEntryList& gopList ) const
1113
0
{
1114
0
  int maxCodingNum = 0;
1115
0
  for( int j = 0; j < (int)gopList.size(); j++ )
1116
0
  {
1117
0
    if( gopList[ j ].m_POC <= gopEntry.m_POC )
1118
0
    {
1119
0
      maxCodingNum = j;
1120
0
    }
1121
0
  }
1122
0
  int numReorder = 0;
1123
0
  for( int j = 0; j < maxCodingNum; j++ )
1124
0
  {
1125
0
    const GOPEntry& cmpEntry = gopList[ j ];
1126
0
    if( cmpEntry.m_POC > gopEntry.m_POC && cmpEntry.m_temporalId <= gopEntry.m_temporalId )
1127
0
    {
1128
0
      numReorder += 1;
1129
0
    }
1130
0
  }
1131
0
  return numReorder;
1132
0
}
1133
1134
void GOPCfg::xAdjustNoLPcodingOrder( GOPEntry& gopEntry, const int orgGopId ) const
1135
0
{
1136
0
  if( orgGopId == 0 )
1137
0
  {
1138
    //postpone the intra frame and put it in a new gop
1139
0
    gopEntry.m_codingNum += (int)m_pocToGopIdx.size() - 1;
1140
0
    gopEntry.m_gopNum    += 1;
1141
0
  }
1142
0
  else
1143
0
  {
1144
0
    gopEntry.m_codingNum -= 1;
1145
0
    if( orgGopId == 1 && m_fixIntraPeriod > m_maxGopSize )
1146
0
    {
1147
0
      gopEntry.m_isStartOfGop = true;
1148
0
    }
1149
0
  }
1150
1151
0
  return;
1152
0
}
1153
1154
} // namespace vvenc
1155
1156
//! \}
1157