Coverage Report

Created: 2026-06-15 06:25

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