Coverage Report

Created: 2026-10-28 06:18

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