Coverage Report

Created: 2026-07-16 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/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
1.21k
{
60
1.21k
  CHECK( gopSize < 1, "gop size has to be greater than 0" );
61
62
1.21k
  m_mctfCfg            = &mctfCfg;
63
1.21k
  m_refreshType        = refreshType;
64
1.21k
  m_picReordering      = bPicReordering;
65
1.21k
  m_poc0idr            = poc0idr;
66
1.21k
  m_fixIntraPeriod     = intraPeriod;
67
1.21k
  m_firstPassMode      = firstPassMode;
68
1.21k
  m_maxGopSize         = gopSize;
69
1.21k
  m_defGopSize         = m_fixIntraPeriod > 0 ? std::min( m_maxGopSize, m_fixIntraPeriod ) : m_maxGopSize;
70
71
1.21k
  const int numGops    = m_fixIntraPeriod > 0 ? m_fixIntraPeriod / m_defGopSize               : 0;
72
1.21k
  const int remainSize = m_fixIntraPeriod > 0 ? m_fixIntraPeriod - ( m_defGopSize * numGops ) : 0;
73
1.21k
  const int minPrevPoc = xGetMinPoc( m_maxGopSize, cfgGopList );
74
1.21k
  const int numDefault = ( ( -1 * minPrevPoc ) + m_maxGopSize - 1 ) / m_maxGopSize + 1;
75
1.21k
  CHECK( numDefault <= 0, "number of gop lists to be created is 0" );
76
77
  // setup default gop lists
78
1.21k
  GOPEntryList* prevGopList = nullptr;
79
1.21k
  int pocOffset             = 0;
80
1.21k
  m_defaultGopLists.resize( numDefault );
81
3.64k
  for( int i = 0; i < numDefault; i++ )
82
2.43k
  {
83
2.43k
    xCreateGopList( m_maxGopSize, m_defGopSize, pocOffset, cfgGopList, prevGopList, m_defaultGopLists[ i ] );
84
2.43k
    prevGopList = &m_defaultGopLists[ i ];
85
2.43k
    pocOffset += m_defGopSize;
86
2.43k
  }
87
1.21k
  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
1.21k
  xSetDefaultRPL    ( m_defaultGopLists );
97
1.21k
  xSetDBPConstraints( m_defaultGopLists );
98
  // sanity check
99
4.86k
  for( int i = 0; i < (int)m_defaultGopLists.size() + 1; i++ )
100
3.64k
  {
101
3.64k
    GOPEntryList& gopList = i < (int)m_defaultGopLists.size() ? m_defaultGopLists[ i ] : m_remainGopList;
102
3.64k
    if( ! xCheckDBPConstraints( gopList ) )
103
0
    {
104
0
      THROW( "gop lists do not fullfill default DPB constraints" );
105
0
    }
106
3.64k
  }
107
108
1.21k
  CHECK( leadFrames < 0, "negative number of lead frames not supported" );
109
110
  // start with first gop list
111
1.21k
  m_gopList     = remainSize != 0 && !poc0idr ? &m_remainGopList : &m_defaultGopLists[ 0 ];
112
1.21k
  m_nextListIdx = remainSize != 0 && !poc0idr ? 0 : std::min( 1, (int)m_defaultGopLists.size() - 1 );
113
1.21k
  xCreatePocToGopIdx( *m_gopList, !m_poc0idr, m_pocToGopIdx );
114
115
  // lets start with poc 0
116
1.21k
  m_gopNum       = 0;
117
1.21k
  m_nextPoc      = -leadFrames;
118
1.21k
  m_pocOffset    = 0;
119
1.21k
  m_cnOffset     = 0;
120
1.21k
  m_numTillGop   = poc0idr ? 0 : (int)m_gopList->size() - 1;
121
1.21k
  m_numTillIntra = poc0idr ? 0 : (int)m_gopList->size() - 1;
122
1.21k
  m_minIntraDist = minIntraDist;
123
1.21k
  m_lastIntraPOC = -1;
124
1.21k
  m_adjustNoLPcodingOrder = m_refreshType == VVENC_DRT_IDR_NO_RADL && m_fixIntraPeriod <= m_maxGopSize;
125
1.21k
}
126
127
void GOPCfg::getNextGopEntry( GOPEntry& gopEntry )
128
1.21k
{
129
  // check for lead frames
130
1.21k
  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
1.21k
  const int  pocInGop   = m_nextPoc - m_pocOffset;
172
1.21k
  const int  gopId      = m_pocToGopIdx[ pocInGop % (int)m_pocToGopIdx.size() ];
173
1.21k
  const bool isLeading0 = m_poc0idr && m_nextPoc == 0 ? true : false; // for poc0idr, we have a leading poc 0
174
  
175
1.21k
  gopEntry             = (*m_gopList)[ gopId ];
176
1.21k
  gopEntry.m_POC       = m_nextPoc;
177
1.21k
  gopEntry.m_codingNum = isLeading0 ? 0 : m_cnOffset + gopId;
178
1.21k
  gopEntry.m_gopNum    = m_gopNum;
179
1.21k
  if( m_nextPoc != 0 && m_adjustNoLPcodingOrder )
180
0
  {
181
0
    xAdjustNoLPcodingOrder( gopEntry, gopId );
182
0
  }
183
1.21k
  gopEntry.m_isValid   = true;
184
185
  // mark start of intra
186
1.21k
  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
1.21k
  CHECK( m_numTillIntra == 0 && m_numTillGop != 0, "start of new intra period only at start of new gop expected" );
197
1.21k
  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
1.21k
  m_nextPoc      += 1;
236
1.21k
  m_numTillGop   -= 1;
237
1.21k
  if( m_numTillIntra > 0 )
238
1.21k
  {
239
1.21k
    m_numTillIntra -= 1;
240
1.21k
  }
241
1.21k
}
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
1.21k
{
271
1.21k
  gopEntry.m_isStartOfGop = true;
272
1.21k
  if( ! m_poc0idr && gopEntry.m_gopNum == 0 && ! gopEntry.m_isStartOfIntra )
273
1.21k
  {
274
1.21k
    gopEntry.m_isStartOfIntra = true;
275
1.21k
    gopEntry.m_sliceType      = 'I';
276
1.21k
    gopEntry.m_temporalId     = 0;
277
1.21k
    gopEntry.m_vtl            = 0;
278
1.21k
    m_lastIntraPOC            = gopEntry.m_POC;
279
1.21k
  }
280
1.21k
}
281
282
void GOPCfg::getDefaultRPLLists( RPLList& rpl0, RPLList& rpl1 ) const
283
1.21k
{
284
1.21k
  const int numRpl = (int)m_defaultRPLList.size();
285
  // TODO (jb): check size + 1, fix getNumRPL() as well
286
1.21k
  rpl0.resize( numRpl + 1 );
287
1.21k
  rpl1.resize( numRpl + 1 );
288
30.4k
  for( int i = 0; i < numRpl; i++ )
289
29.1k
  {
290
29.1k
    rpl0[ i ].initFromGopEntry( *m_defaultRPLList[ i ], 0 );
291
29.1k
    rpl1[ i ].initFromGopEntry( *m_defaultRPLList[ i ], 1 );
292
29.1k
  }
293
1.21k
}
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
1.21k
{
306
1.21k
  return m_maxTid > 0;
307
1.21k
}
308
309
bool GOPCfg::hasLeadingPictures() const
310
2.43k
{
311
2.43k
  for( const auto& gopEntry : m_defaultGopLists.back() )
312
4.86k
  {
313
4.86k
    if( ! gopEntry.m_useBckwdOnly )
314
2.43k
    {
315
2.43k
      return true;
316
2.43k
    }
317
4.86k
  }
318
0
  return false;
319
2.43k
}
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
1.21k
{
337
1.21k
  int minPoc = 0;
338
40.1k
  for( int i = 0; i < maxGopSize; i++ )
339
38.9k
  {
340
116k
    for( int l = 0; l < 2; l++ )
341
77.8k
    {
342
285k
      for( int j = 0; j < cfgGopList[ i ].m_numRefPics[ l ]; j++ )
343
207k
      {
344
207k
        const int refPoc = cfgGopList[ i ].m_POC - cfgGopList[ i ].m_deltaRefPics[ l ][ j ];
345
207k
        if( refPoc < minPoc )
346
1.21k
          minPoc = refPoc;
347
207k
      }
348
77.8k
    }
349
38.9k
  }
350
1.21k
  return minPoc;
351
1.21k
}
352
353
void GOPCfg::xCreateGopList( int maxGopSize, int gopSize, int pocOffset, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ], const GOPEntryList* prevGopList, GOPEntryList& gopList ) const
354
2.43k
{
355
2.43k
  const bool bSkipPrev = prevGopList == nullptr || (int)prevGopList->size() < maxGopSize;
356
2.43k
  std::vector< std::pair<int, int> > prevGopRefs;
357
2.43k
  if( ! bSkipPrev )
358
0
  {
359
0
    xGetPrevGopRefs( prevGopList, prevGopRefs );
360
0
  }
361
362
  //
363
  // copy given gop configuration
364
  //
365
366
2.43k
  gopList.clear();
367
2.43k
  gopList.resize( maxGopSize );
368
80.2k
  for( int i = 0; i < maxGopSize; i++ )
369
77.8k
  {
370
77.8k
    gopList[ i ].setDefaultGOPEntry();
371
77.8k
    gopList[ i ].copyFromGopCfg( cfgGopList[ i ] );
372
77.8k
  }
373
374
  // find max temporal id
375
2.43k
  const int maxTid = xGetMaxTid( gopList );
376
377
  // prune gop list
378
2.43k
  CHECK( gopSize > maxGopSize, "only pruning of gop list supported" );
379
2.43k
  if( gopSize < maxGopSize )
380
2.43k
  {
381
2.43k
    xPruneGopList( gopSize, gopList );
382
2.43k
  }
383
384
  // 
385
  // fix prediction pattern
386
  //
387
388
  // use list of available entries for prediction
389
2.43k
  std::vector<GOPEntry*> availList;
390
2.43k
  availList.resize( gopSize + 1, nullptr );
391
2.43k
  GOPEntry e1;
392
2.43k
  if( prevGopRefs.size() == 0 || prevGopRefs[ 0 ].first == 0 )
393
2.43k
  {
394
    // insert leading poc 0 to be available
395
2.43k
    CHECK( prevGopRefs.size() && prevGopRefs[ 0 ].first == 0 && prevGopRefs[ 0 ].second != 0, "gop structure should start and end with temporalId 0" );
396
2.43k
    e1.setDefaultGOPEntry();
397
2.43k
    e1.m_POC = 0;
398
2.43k
    availList[ 0 ] = &e1;
399
    // remove leading poc 0 from prev 
400
2.43k
    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
2.43k
  }
409
410
  // loop over gop list
411
2.43k
  std::vector<int> newFwd;
412
2.43k
  std::vector<int> newBckwd;
413
2.43k
  std::vector<int> availFwd;
414
2.43k
  std::vector<int> availBckwd;
415
2.43k
  newFwd.reserve( MAX_REF_PICS );
416
2.43k
  newBckwd.reserve( MAX_REF_PICS );
417
2.43k
  availFwd.reserve( MAX_REF_PICS );
418
2.43k
  availBckwd.reserve( MAX_REF_PICS );
419
60.8k
  for( int i = 0; i < gopList.size(); i++ )
420
58.3k
  {
421
58.3k
    GOPEntry* gopEntry = &gopList[ i ];
422
423
    // loop over both reference lists
424
175k
    for( int l = 0; l < 2; l++ )
425
116k
    {
426
116k
      newFwd.clear();
427
116k
      newBckwd.clear();
428
116k
      availFwd.clear();
429
116k
      availBckwd.clear();
430
431
      // fill list of possible reference pictures in forward / backward direction
432
116k
      xAddRefPicsFwd( availFwd, gopEntry, availList );
433
116k
      xAddRefPicsBckwd( availBckwd, gopEntry, availList );
434
      // access to previous gop not used/allowed for non-ref pics at highest temporal layer
435
116k
      if( gopEntry->m_temporalId <= 1 || gopEntry->m_temporalId < maxTid )
436
58.3k
      {
437
58.3k
        xAddRefPicsPrevGOP( availBckwd, gopEntry, prevGopRefs );
438
58.3k
      }
439
440
      // check / fix all active references
441
350k
      for( int j = 0; j < gopEntry->m_numRefPicsActive[ l ]; j++ )
442
233k
      {
443
233k
        const int delta = gopEntry->m_deltaRefPics[ l ][ j ];
444
233k
        CHECK( delta == 0, "error in gop configuration: try to reference own picture" );
445
        // start with forward or backward prediction
446
233k
        std::vector<int>& sameDir   = delta < 0 ? availFwd   : availBckwd;
447
233k
        std::vector<int>& invertDir = delta < 0 ? availBckwd : availFwd;
448
        // check if candidates are available in same direction list
449
233k
        if( sameDir.size() )
450
211k
        {
451
          // try to find correct match
452
211k
          auto itr = find( sameDir.begin(), sameDir.end(), delta );
453
211k
          if( itr == sameDir.end() )
454
12.1k
          {
455
            // no match found, choose replacement
456
12.1k
            itr = sameDir.begin();
457
12.1k
          }
458
          // use delta candidate and remove candidate to prevent doublets
459
211k
          std::vector<int>& newDst = *itr < 0 ? newFwd : newBckwd;
460
211k
          newDst.push_back( *itr );
461
211k
          sameDir.erase( itr );
462
211k
        }
463
21.8k
        else if( invertDir.size() )
464
17.0k
        {
465
          // corret match not possible, we are inverting the prediction direction
466
17.0k
          auto itr = invertDir.begin();
467
          // use delta candidate and remove candidate to prevent doublets
468
17.0k
          std::vector<int>& newDst = *itr < 0 ? newFwd : newBckwd;
469
17.0k
          newDst.push_back( *itr );
470
17.0k
          invertDir.erase( itr );
471
17.0k
        }
472
233k
      }
473
474
      // clear old delta list
475
116k
      std::memset( gopEntry->m_deltaRefPics[ l ], 0, sizeof( gopEntry->m_deltaRefPics[ l ] ) );
476
477
      // ensure new delta lists are sorted
478
116k
      std::sort( newFwd.begin(), newFwd.end(),     []( auto& a, auto& b ){ return a > b; } );
479
116k
      std::sort( newBckwd.begin(), newBckwd.end(), []( auto& a, auto& b ){ return a < b; } );
480
481
      // copy new delta poc list
482
116k
      CHECK( (int)newFwd.size() + (int)newBckwd.size() >= VVENC_MAX_NUM_REF_PICS, "number of delta ref pic entries exceeds array bounds" );
483
116k
      gopEntry->m_numRefPics[ l ]       = (int)newFwd.size() + (int)newBckwd.size();
484
116k
      gopEntry->m_numRefPicsActive[ l ] = (int)newFwd.size() + (int)newBckwd.size();
485
116k
      std::vector<int>& src1 = l == 0 ? newBckwd : newFwd;
486
116k
      std::vector<int>& src2 = l == 0 ? newFwd   : newBckwd;
487
116k
      int dstIdx = 0;
488
321k
      for( int j = 0; j < (int)src1.size(); j++ )
489
204k
      {
490
204k
        gopEntry->m_deltaRefPics[ l ][ dstIdx ] = src1[ j ];
491
204k
        dstIdx += 1;
492
204k
      }
493
141k
      for( int j = 0; j < (int)src2.size(); j++ )
494
24.3k
      {
495
24.3k
        gopEntry->m_deltaRefPics[ l ][ dstIdx ] = src2[ j ];
496
24.3k
        dstIdx += 1;
497
24.3k
      }
498
116k
    }
499
500
    // available for later use as ref
501
58.3k
    availList[ gopEntry->m_POC ] = gopEntry;
502
58.3k
  }
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
2.43k
  GOPEntry e2;
511
2.43k
  e2.setDefaultGOPEntry();
512
2.43k
  e2.m_POC           = 2 * maxGopSize;
513
2.43k
  e2.m_temporalId    = 0;
514
2.43k
  const bool bIsLast = m_maxGopSize != gopSize;
515
2.43k
  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
2.43k
  const GOPEntry* prevEntry = &e2;
528
529
  // traverse gop list from back to front
530
60.8k
  for( int i = gopSize - 1; i >= 0; i-- )
531
58.3k
  {
532
58.3k
    GOPEntry* gopEntry = &gopList[ i ];
533
    // loop over all ref lists
534
175k
    for( int j = 0; j < 2; j++ )
535
116k
    {
536
      // loop over all delta POCs in previous entry
537
396k
      for( int k = 0; k < prevEntry->m_numRefPics[ j ]; k++ )
538
279k
      {
539
279k
        const int reqPoc = prevEntry->m_POC - prevEntry->m_deltaRefPics[ j ][ k ];
540
279k
        if( reqPoc == gopEntry->m_POC )
541
41.3k
        {
542
41.3k
          continue;
543
41.3k
        }
544
238k
        bool bAvail = false;
545
        // search if required poc is also part of the ref lists of current gop entry
546
445k
        for( int l = 0; l < 2; l++ )
547
389k
        {
548
931k
          for( int m = 0; m < gopEntry->m_numRefPics[ l ]; m++ )
549
724k
          {
550
724k
            const int availPoc = gopEntry->m_POC - gopEntry->m_deltaRefPics[ l ][ m ];
551
724k
            if( reqPoc == availPoc )
552
182k
            {
553
182k
              bAvail = true;
554
182k
              break;
555
182k
            }
556
724k
          }
557
389k
          if( bAvail )
558
182k
          {
559
182k
            break;
560
182k
          }
561
389k
        }
562
        // if required poc not found, add this poc to current entry lists
563
238k
        if( ! bAvail )
564
55.9k
        {
565
55.9k
          const int delta   = gopEntry->m_POC - reqPoc;
566
55.9k
          const int addList = delta > 0 ? 0 : 1; // add to backward L0 or forward L1 list
567
55.9k
          CHECK( gopEntry->m_numRefPics[ addList ] >= VVENC_MAX_NUM_REF_PICS, "out of array bounds" );
568
55.9k
          gopEntry->m_deltaRefPics[ addList ][ gopEntry->m_numRefPics[ addList ] ] = delta;
569
55.9k
          gopEntry->m_numRefPics[ addList ] += 1;
570
          // small optimization: sort additional poc by value to decrease required bits for encoding the list
571
55.9k
          const int sign = delta > 0 ? 1 : -1;
572
55.9k
          for( int n = gopEntry->m_numRefPics[ addList ] - 1; n > gopEntry->m_numRefPicsActive[ addList ]; n-- )
573
14.5k
          {
574
14.5k
            if( gopEntry->m_deltaRefPics[ addList ][ n ] * sign > gopEntry->m_deltaRefPics[ addList ][ n - 1 ] * sign )
575
14.5k
              break;
576
0
            std::swap( gopEntry->m_deltaRefPics[ addList ][ n ], gopEntry->m_deltaRefPics[ addList ][ n - 1 ] );
577
0
          }
578
55.9k
        }
579
238k
      }
580
116k
    }
581
58.3k
    prevEntry = gopEntry;
582
58.3k
  }
583
584
  // poc to gop map for fastwr access
585
2.43k
  std::vector<int> pocToGopIdx;
586
2.43k
  xCreatePocToGopIdx( gopList, false, pocToGopIdx );
587
588
  // mark first gop entry
589
2.43k
  gopList[ pocToGopIdx[ 0 ] ].m_isStartOfGop = true;
590
591
  // STSA, forward B flags, MCTF index, virtual TLayer
592
2.43k
  xSetSTSA     ( gopList, pocToGopIdx );
593
2.43k
  xSetBckwdOnly( gopList );
594
2.43k
  xSetMctfIndex( maxGopSize, gopList );
595
2.43k
  xSetVTL      ( gopList );
596
2.43k
  if( m_firstPassMode == 2 || m_firstPassMode == 4 )
597
0
  {
598
0
    xSetSkipFirstPass( gopList );
599
0
  }
600
2.43k
}
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
2.43k
{
673
2.43k
  const int oldSize = (int)gopList.size();
674
2.43k
  CHECK( oldSize <= gopSize, "gop list to short for prunning" );
675
676
2.43k
  std::vector<GOPEntry> prunedList;
677
2.43k
  prunedList.reserve( gopSize );
678
80.2k
  for( int i = 0; i < (int)gopList.size(); i++ )
679
77.8k
  {
680
77.8k
    if( gopList[ i ].m_POC == oldSize )
681
2.43k
    {
682
      // always include end of old gop, which is the start of following gop
683
2.43k
      prunedList.push_back( gopList[ i ] );
684
      // fix poc and delta lists of new end of pruned gop
685
2.43k
      GOPEntry& gopEntry = prunedList.back();
686
2.43k
      const int sizeDiff = oldSize - gopSize;
687
7.29k
      for( int l = 0; l < 2; l++ )
688
4.86k
      {
689
17.0k
        for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j++ )
690
12.1k
        {
691
12.1k
          if( gopEntry.m_deltaRefPics[ l ][ j ] > sizeDiff )
692
12.1k
            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
12.1k
        }
696
4.86k
      }
697
2.43k
      gopEntry.m_POC -= sizeDiff;
698
2.43k
    }
699
75.3k
    else if( gopList[ i ].m_POC < gopSize )
700
55.9k
    {
701
55.9k
      prunedList.push_back( gopList[ i ] );
702
55.9k
    }
703
77.8k
  }
704
705
2.43k
  gopList.clear();
706
2.43k
  gopList.resize( (int)prunedList.size() );
707
60.8k
  for( int i = 0; i < (int)prunedList.size(); i++ )
708
58.3k
  {
709
58.3k
    gopList[ i ] = prunedList[ i ];
710
58.3k
  }
711
712
2.43k
  CHECK( gopList.size() != gopSize, "pruned gop list incomplete" );
713
2.43k
}
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
2.43k
{
739
2.43k
  for( auto& gopEntry : gopList )
740
58.3k
  {
741
58.3k
    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
58.3k
    if( (int)gopList.size() < maxGopSize && poc == (int)gopList.size() )
745
2.43k
    {
746
2.43k
      poc = maxGopSize;
747
2.43k
    }
748
    // find mctf index
749
204k
    for( int i = m_mctfCfg->numFrames - 1; i >= 0; i-- )
750
154k
    {
751
154k
      if( ( poc % m_mctfCfg->MCTFFrames[ i ] ) == 0 )
752
8.47k
      {
753
8.47k
        gopEntry.m_mctfIndex = i;
754
8.47k
        break;
755
8.47k
      }
756
154k
    }
757
58.3k
  }
758
2.43k
}
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
3.64k
{
779
3.64k
  const int gopSize   = (int)gopList.size();
780
3.64k
  const int pocOffset = bShift ? -1 : 0;
781
782
3.64k
  pocToGopIdx.clear();
783
3.64k
  pocToGopIdx.resize( gopSize, -1 );
784
785
91.2k
  for( int i = 0; i < gopSize; i++ )
786
87.5k
  {
787
87.5k
    const GOPEntry& gopEntry = gopList[ i ];
788
87.5k
    const int poc            = ( gopEntry.m_POC + pocOffset ) % gopSize;
789
87.5k
    CHECK( gopEntry.m_POC > gopSize || gopEntry.m_POC < 1, "error: poc out of range" );
790
87.5k
    CHECK( pocToGopIdx[ poc ] != -1,                       "error: multiple entries in gop list map to same poc" );
791
87.5k
    pocToGopIdx[ poc ] = i;
792
87.5k
  }
793
91.2k
  for( int i = 0; i < gopSize; i++ )
794
87.5k
  {
795
87.5k
    CHECK( pocToGopIdx[ i ] < 0, "error: poc not found in gop list" );
796
87.5k
  }
797
3.64k
}
798
799
void GOPCfg::xSetSTSA( GOPEntryList& gopList, const std::vector<int>& pocToGopIdx ) const
800
2.43k
{
801
  // check STSA condition satisfied for each GOPEntry
802
2.43k
  std::vector<bool> isLocalSTSA;
803
2.43k
  isLocalSTSA.resize( gopList.size(), true );
804
60.8k
  for( int i = 0; i < (int)gopList.size(); i++ )
805
58.3k
  {
806
58.3k
    const GOPEntry& gopEntry = gopList[ i ];
807
    // check all ref pics have higher temporal level
808
172k
    for( int l = 0; l < 2 && isLocalSTSA[ i ]; l++ )
809
114k
    {
810
393k
      for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ )
811
282k
      {
812
282k
        const int refPoc = gopEntry.m_POC - gopEntry.m_deltaRefPics[ l ][ j ];
813
282k
        if( refPoc < 0 )
814
0
        {
815
0
          continue;
816
0
        }
817
282k
        const int gopId  = pocToGopIdx[ refPoc % (int)pocToGopIdx.size() ];
818
282k
        const GOPEntry& refEntry = gopList[ gopId ];
819
282k
        if( refEntry.m_temporalId >= gopEntry.m_temporalId )
820
2.43k
        {
821
2.43k
          isLocalSTSA[ i ] = false;
822
2.43k
          break;
823
2.43k
        }
824
282k
      }
825
114k
    }
826
58.3k
  }
827
828
  // accumulate STSA condition for all pics of same temporal level
829
2.43k
  const int maxTid = xGetMaxTid( gopList );
830
2.43k
  std::vector<bool> isTlSTSA;
831
2.43k
  isTlSTSA.resize( maxTid + 1, true );
832
60.8k
  for( int i = 0; i < (int)gopList.size(); i++ )
833
58.3k
  {
834
58.3k
    const GOPEntry& gopEntry = gopList[ i ];
835
58.3k
    const int tid            = gopEntry.m_temporalId;
836
58.3k
    const bool isSTSA        = isTlSTSA[ tid ] && isLocalSTSA[ i ];
837
58.3k
    isTlSTSA[ tid ]          = isSTSA;
838
58.3k
  }
839
840
  // set STSA flag for each GOPEntry
841
2.43k
  for( auto& gopEntry : gopList )
842
58.3k
  {
843
58.3k
    const int tid = gopEntry.m_temporalId;
844
58.3k
    gopEntry.m_isSTSA = isTlSTSA[ tid ];
845
58.3k
  }
846
2.43k
}
847
848
void GOPCfg::xSetBckwdOnly( GOPEntryList& gopList ) const
849
2.43k
{
850
2.43k
  for( auto& gopEntry : gopList )
851
58.3k
  {
852
58.3k
    bool useBckwdOnly = true;
853
162k
    for( int l = 0; l < 2 && useBckwdOnly; l++ )
854
104k
    {
855
221k
      for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ )
856
172k
      {
857
172k
        if( gopEntry.m_deltaRefPics[ l ][ j ] < 0 )
858
55.9k
        {
859
55.9k
          useBckwdOnly = false;
860
55.9k
          break;
861
55.9k
        }
862
172k
      }
863
104k
    }
864
58.3k
    gopEntry.m_useBckwdOnly = useBckwdOnly;
865
58.3k
  }
866
2.43k
}
867
868
void GOPCfg::xSetVTL( GOPEntryList& gopList ) const
869
2.43k
{
870
2.43k
  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
2.43k
  else
883
2.43k
  {
884
2.43k
    for( auto& gopEntry : gopList )
885
58.3k
    {
886
58.3k
      gopEntry.m_vtl = gopEntry.m_temporalId;
887
58.3k
    }
888
2.43k
  }
889
2.43k
}
890
891
void GOPCfg::xSetDefaultRPL( std::vector<GOPEntryList>& defaultLists )
892
1.21k
{
893
1.21k
  m_defaultRPLList.clear();
894
895
  // default RPL index is entry position in gop
896
1.21k
  GOPEntryList& gopList = defaultLists.back();
897
1.21k
  for( auto& gopEntry : gopList )
898
29.1k
  {
899
29.1k
    gopEntry.m_defaultRPLIdx = (int)m_defaultRPLList.size();
900
29.1k
    m_defaultRPLList.push_back( &gopEntry );
901
29.1k
  }
902
903
  // in prev lists set extra index if RPL list deviates from default list
904
2.43k
  for( int i = 0; i < (int)defaultLists.size() - 1; i++ )
905
1.21k
  {
906
1.21k
    GOPEntryList& prevList = defaultLists[ i ];
907
1.21k
    CHECK( gopList.size() != prevList.size(), "default gop list and first gop list have to have same size" );
908
30.4k
    for( int i = 0; i < (int)prevList.size(); i++ )
909
29.1k
    {
910
29.1k
      CHECK( prevList[ i ].m_POC != gopList[ i ].m_POC, "default gop list and first gop List have to have same poc layout" );
911
29.1k
      GOPEntry& gopEntry = prevList[ i ];
912
29.1k
      GOPEntry& cmpEntry = gopList[ i ];
913
29.1k
      bool haveSameList  = true;
914
87.5k
      for( int l = 0; l < 2 && haveSameList; l++ )
915
58.3k
      {
916
58.3k
        if( gopEntry.m_numRefPics[ l ] != cmpEntry.m_numRefPics[ l ] )
917
0
        {
918
0
          haveSameList = false;
919
0
          break;
920
0
        }
921
200k
        for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ )
922
142k
        {
923
142k
          if( gopEntry.m_deltaRefPics[ l ][ j ] != cmpEntry.m_deltaRefPics[ l ][ j ] )
924
0
          {
925
0
            haveSameList = false;
926
0
            break;
927
0
          }
928
142k
        }
929
58.3k
      }
930
29.1k
      if( haveSameList )
931
29.1k
      {
932
29.1k
        gopEntry.m_defaultRPLIdx = cmpEntry.m_defaultRPLIdx;
933
29.1k
      }
934
0
      else
935
0
      {
936
0
        gopEntry.m_defaultRPLIdx = (int)m_defaultRPLList.size();
937
0
        m_defaultRPLList.push_back( &gopEntry );
938
0
      }
939
29.1k
    }
940
1.21k
  }
941
942
1.21k
  std::vector<int> hist[ 2 ];
943
1.21k
  hist[ 0 ].resize( MAX_NUM_REF, 0 );
944
1.21k
  hist[ 1 ].resize( MAX_NUM_REF, 0 );
945
1.21k
  for( auto gopEntry : m_defaultRPLList )
946
29.1k
  {
947
87.5k
    for( int l = 0; l < 2; l++ )
948
58.3k
    {
949
58.3k
      CHECK( gopEntry->m_numRefPicsActive[ l ] < 0 || gopEntry->m_numRefPicsActive[ l ] >= MAX_NUM_REF, "array index out of bounds");
950
58.3k
      hist[ l ][ gopEntry->m_numRefPicsActive[ l ] ] += 1;
951
58.3k
    }
952
29.1k
  }
953
3.64k
  for( int l = 0; l < 2; l++ )
954
2.43k
  {
955
2.43k
    int bestIdx = 0;
956
2.43k
    int maxVal  = -1;
957
41.3k
    for( int j = 0; j < (int)hist[ l ].size(); j++ )
958
38.9k
    {
959
38.9k
      if( hist[ l ][ j ] > maxVal )
960
7.29k
      {
961
7.29k
        bestIdx = j;
962
7.29k
        maxVal  = hist[ l ][ j ];
963
7.29k
      }
964
38.9k
    }
965
2.43k
    m_defaultNumActive[ l ] = bestIdx;
966
2.43k
  }
967
1.21k
}
968
969
void GOPCfg::xSetDBPConstraints( std::vector<GOPEntryList>& defaultLists )
970
1.21k
{
971
1.21k
  const GOPEntryList& gopList = defaultLists.back();
972
973
1.21k
  m_maxTid = xGetMaxTid( gopList );
974
975
1.21k
  m_maxDecPicBuffering.resize( m_maxTid + 1, 1 );
976
1.21k
  m_numReorderPics.resize    ( m_maxTid + 1, 0 );
977
978
  // max DPB size and number reorder pics per temporal level
979
1.21k
  for( const auto& gopEntry : gopList )
980
29.1k
  {
981
29.1k
    const int numRefPics = xGetMaxRefPics   ( gopEntry );
982
29.1k
    const int numReorder = xGetMaxNumReorder( gopEntry, gopList );
983
29.1k
    const int tid        = gopEntry.m_temporalId;
984
29.1k
    m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], numRefPics + 1 );
985
29.1k
    m_numReorderPics    [ tid ] = std::max( m_numReorderPics[ tid ], numReorder );
986
29.1k
  }
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
1.21k
  m_maxDecPicBuffering[ 0 ] = std::max( m_maxDecPicBuffering[ 0 ], m_numReorderPics[ 0 ] +1 );
990
6.08k
  for( int tid = 1; tid < m_maxTid; tid++ )
991
4.86k
  {
992
    // a lower layer can not have higher reorder value than a higher layer
993
4.86k
    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
4.86k
    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
4.86k
    m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], m_maxDecPicBuffering[ tid - 1 ] );
998
4.86k
  }
999
1.21k
}
1000
1001
bool GOPCfg::xCheckDBPConstraints( const GOPEntryList& gopList ) const
1002
3.64k
{
1003
3.64k
  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
3.64k
  const int maxTid = xGetMaxTid( gopList );
1009
3.64k
  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
3.64k
  for( const auto& gopEntry : gopList )
1015
58.3k
  {
1016
58.3k
    const int numRefPics = xGetMaxRefPics   ( gopEntry );
1017
58.3k
    const int numReorder = xGetMaxNumReorder( gopEntry, gopList );
1018
58.3k
    const int tid        = gopEntry.m_temporalId;
1019
58.3k
    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
58.3k
    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
58.3k
  }
1030
3.64k
  return true;
1031
3.64k
}
1032
1033
void GOPCfg::xAddRefPicsBckwd( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector<GOPEntry*>& availList ) const
1034
116k
{
1035
1.57M
  for( int i = ( gopEntry->m_POC - 1 ); i >= 0; i-- )
1036
1.45M
  {
1037
1.45M
    const GOPEntry* refEntry = availList[ i ];
1038
1.45M
    if( refEntry )
1039
1.16M
    {
1040
1.16M
      if( refEntry->m_temporalId <= gopEntry->m_temporalId )
1041
933k
      {
1042
933k
        deltaList.push_back( gopEntry->m_POC - refEntry->m_POC );
1043
933k
        CHECK( deltaList.back() <= 0, "error in backward list: try to access future ref" );
1044
933k
      }
1045
1.16M
    }
1046
1.45M
  }
1047
116k
}
1048
1049
void GOPCfg::xAddRefPicsFwd( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector<GOPEntry*>& availList ) const
1050
116k
{
1051
1.45M
  for( int i = ( gopEntry->m_POC + 1 ); i < (int)availList.size(); i++ )
1052
1.34M
  {
1053
1.34M
    const GOPEntry* refEntry = availList[ i ];
1054
1.34M
    if( refEntry )
1055
291k
    {
1056
291k
      if( refEntry->m_temporalId <= gopEntry->m_temporalId )
1057
291k
      {
1058
291k
        deltaList.push_back( gopEntry->m_POC - refEntry->m_POC );
1059
291k
        CHECK( deltaList.back() >= 0, "error in forward list: try to access past ref" );
1060
291k
      }
1061
291k
    }
1062
1.34M
  }
1063
116k
}
1064
1065
void GOPCfg::xAddRefPicsPrevGOP( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector< std::pair<int, int> >& prevGopRefs ) const
1066
58.3k
{
1067
58.3k
  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
58.3k
}
1076
1077
int GOPCfg::xGetMaxTid( const GOPEntryList& gopList ) const
1078
9.72k
{
1079
9.72k
  int maxTid = 0;
1080
9.72k
  for( auto& gopEntry : gopList )
1081
223k
  {
1082
223k
    if( gopEntry.m_temporalId > maxTid )
1083
42.5k
    {
1084
42.5k
      maxTid = gopEntry.m_temporalId;
1085
42.5k
    }
1086
223k
  }
1087
9.72k
  return maxTid;
1088
9.72k
}
1089
1090
int GOPCfg::xGetMaxRefPics( const GOPEntry& gopEntry ) const
1091
87.5k
{
1092
87.5k
  int numRefPics = gopEntry.m_numRefPics[ 0 ];
1093
324k
  for( int j = 0; j < gopEntry.m_numRefPics[ 1 ]; j++ )
1094
237k
  {
1095
237k
    bool inL0 = false;
1096
704k
    for( int k = 0; k < gopEntry.m_numRefPics[ 0 ]; k++ )
1097
503k
    {
1098
503k
      if( gopEntry.m_deltaRefPics[ 1 ][ j ] == gopEntry.m_deltaRefPics[ 0 ][ k ] )
1099
36.4k
      {
1100
36.4k
        inL0 = true;
1101
36.4k
        break;
1102
36.4k
      }
1103
503k
    }
1104
237k
    if( ! inL0 )
1105
200k
    {
1106
200k
      numRefPics += 1;
1107
200k
    }
1108
237k
  }
1109
87.5k
  return numRefPics;
1110
87.5k
}
1111
1112
int GOPCfg::xGetMaxNumReorder( const GOPEntry& gopEntry, const GOPEntryList& gopList ) const
1113
87.5k
{
1114
87.5k
  int maxCodingNum = 0;
1115
2.18M
  for( int j = 0; j < (int)gopList.size(); j++ )
1116
2.10M
  {
1117
2.10M
    if( gopList[ j ].m_POC <= gopEntry.m_POC )
1118
1.09M
    {
1119
1.09M
      maxCodingNum = j;
1120
1.09M
    }
1121
2.10M
  }
1122
87.5k
  int numReorder = 0;
1123
1.31M
  for( int j = 0; j < maxCodingNum; j++ )
1124
1.22M
  {
1125
1.22M
    const GOPEntry& cmpEntry = gopList[ j ];
1126
1.22M
    if( cmpEntry.m_POC > gopEntry.m_POC && cmpEntry.m_temporalId <= gopEntry.m_temporalId )
1127
218k
    {
1128
218k
      numReorder += 1;
1129
218k
    }
1130
1.22M
  }
1131
87.5k
  return numReorder;
1132
87.5k
}
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