Coverage Report

Created: 2026-05-30 06:10

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