Coverage Report

Created: 2026-10-17 06:56

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