Coverage Report

Created: 2026-06-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vvdec/source/Lib/DecoderLib/DecLib.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) 2018-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVdeC 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
/** \file     DecLib.cpp
44
    \brief    decoder wrapper class
45
*/
46
47
#include "DecLib.h"
48
49
#include "CommonLib/dtrace_next.h"
50
#include "CommonLib/dtrace_buffer.h"
51
#include "CommonLib/TimeProfiler.h"
52
53
#include "CommonLib/arm/CommonDefARM.h"
54
#include "CommonLib/x86/CommonDefX86.h"
55
56
#include "NALread.h"
57
58
59
namespace vvdec
60
{
61
62
#ifdef TRACE_ENABLE_ITT
63
std::vector<__itt_domain*> itt_domain_decInst;
64
__itt_domain* itt_domain_dec           = __itt_domain_create( "Decode" );
65
__itt_domain* itt_domain_prs           = __itt_domain_create( "Parse" );
66
__itt_domain* itt_domain_oth           = __itt_domain_create( "Other" );
67
68
__itt_string_handle* itt_handle_alf    = __itt_string_handle_create( "ALF_CTU" );
69
__itt_string_handle* itt_handle_presao = __itt_string_handle_create( "PreSAO_Line" );
70
__itt_string_handle* itt_handle_sao    = __itt_string_handle_create( "SAO_CTU" );
71
__itt_string_handle* itt_handle_lfl    = __itt_string_handle_create( "LFL_CTU" );
72
__itt_string_handle* itt_handle_intra  = __itt_string_handle_create( "Intra_CTU" );
73
__itt_string_handle* itt_handle_inter  = __itt_string_handle_create( "Inter_CTU" );
74
__itt_string_handle* itt_handle_mider  = __itt_string_handle_create( "MI-Der_CTU" );
75
__itt_string_handle* itt_handle_lfcl   = __itt_string_handle_create( "Prep_ClearLF" );
76
__itt_string_handle* itt_handle_ext    = __itt_string_handle_create( "Prep_ExtBrdr" );
77
__itt_string_handle* itt_handle_dmvr   = __itt_string_handle_create( "MI-DMVR" );
78
__itt_string_handle* itt_handle_rsp    = __itt_string_handle_create( "Reshape_CTU" );
79
80
__itt_string_handle* itt_handle_parse  = __itt_string_handle_create( "Parse_Slice" );
81
82
__itt_string_handle* itt_handle_start  = __itt_string_handle_create( "Start_Pic" );
83
__itt_string_handle* itt_handle_done   = __itt_string_handle_create( "Pic_Done" );
84
__itt_string_handle* itt_handle_finish = __itt_string_handle_create( "Finish_Pic" );
85
86
__itt_string_handle* itt_handle_schedTasks = __itt_string_handle_create( "Scheduling_Tasks" );
87
__itt_string_handle* itt_handle_waitTasks  = __itt_string_handle_create( "Wait_for_Dec_Tasks" );
88
89
90
// create global domain for DecLib
91
__itt_domain* itt_domain_glb    = __itt_domain_create ( "Global" );
92
// create a global counter
93
__itt_counter itt_frame_counter = __itt_counter_create( "FrameNumber", "Global" );
94
95
#define ITT_TASKSTART( d, t ) __itt_task_begin( ( d ), __itt_null, __itt_null, ( t ) )
96
#define ITT_TASKEND( d, t )   __itt_task_end  ( ( d ) )
97
#else
98
#define ITT_TASKSTART( d, t )
99
#define ITT_TASKEND( d, t )
100
#endif
101
102
//! \ingroup DecoderLib
103
//! \{
104
105
DecLib::DecLib()
106
1.87k
{
107
#ifdef TRACE_ENABLE_ITT
108
  itt_domain_dec->flags = 1;
109
  itt_domain_prs->flags = 1;
110
  itt_domain_glb->flags = 1;
111
  itt_domain_oth->flags = 1;
112
#endif
113
1.87k
}
114
115
void DecLib::create( int numDecThreads, int parserFrameDelay, const UserAllocator& userAllocator, ErrHandlingFlags errHandlingFlags )
116
935
{
117
  // run constructor again to ensure all variables, especially in DecLibParser have been reset
118
935
  this->~DecLib();
119
935
  new( this ) DecLib;
120
121
935
  if( numDecThreads < 0 )
122
935
  {
123
935
    numDecThreads = std::thread::hardware_concurrency();
124
935
  }
125
126
935
  m_decodeThreadPool.reset( new ThreadPool( numDecThreads, "DecThread" ) );
127
128
935
  if( parserFrameDelay < 0 )
129
935
  {
130
935
    CHECK_FATAL( numDecThreads < 0, "invalid number of threads" );
131
935
    parserFrameDelay = std::min<int>( ( numDecThreads * DEFAULT_PARSE_DELAY_FACTOR ) >> 4, DEFAULT_PARSE_DELAY_MAX );
132
935
  }
133
935
  m_parseFrameDelay = parserFrameDelay;
134
135
935
  bool upscalingEnabled = false;
136
935
  m_picListManager.create( m_parseFrameDelay, (int) m_decLibRecon.size(), userAllocator );
137
935
  m_decLibParser.create  ( m_decodeThreadPool.get(), m_parseFrameDelay, (int) m_decLibRecon.size(), numDecThreads, errHandlingFlags );
138
139
935
  int id=0;
140
935
  for( auto &dec: m_decLibRecon )
141
1.87k
  {
142
1.87k
    dec.create( m_decodeThreadPool.get(), id++, upscalingEnabled );
143
1.87k
  }
144
145
935
  std::stringstream cssCap;
146
935
  cssCap << "THREADS="     << numDecThreads << "; "
147
935
         << "PARSE_DELAY=" << parserFrameDelay << "; ";
148
935
#if ENABLE_SIMD_OPT
149
#  if defined( TARGET_SIMD_ARM )
150
  cssCap << "SIMD=" << read_arm_extension_name();
151
#  elif defined( TARGET_SIMD_X86 )
152
  cssCap << "SIMD=" << read_x86_extension_name();
153
#  else
154
  cssCap << "SIMD=SCALAR";
155
#  endif
156
#else
157
  cssCap << "SIMD=NONE";
158
#endif
159
160
935
  m_sDecoderCapabilities = cssCap.str();
161
162
935
  DTRACE_UPDATE( g_trace_ctx, std::make_pair( "final", 1 ) );
163
935
}
164
165
void DecLib::destroy()
166
935
{
167
935
  if( m_decodeThreadPool )
168
935
  {
169
935
    m_decodeThreadPool->shutdown( true );
170
935
    m_decodeThreadPool.reset();
171
935
  }
172
173
935
  m_decLibParser.destroy();
174
935
  for( auto &dec: m_decLibRecon )
175
1.87k
  {
176
1.87k
    dec.destroy();
177
1.87k
  }
178
179
935
  m_picListManager.deleteBuffers();
180
935
}
181
182
Picture* DecLib::decode( InputNALUnit& nalu )
183
3.29k
{
184
3.29k
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_NALU_SLICE_PIC_HL );
185
186
3.29k
  bool newPic = false;
187
3.29k
  if( m_iMaxTemporalLayer < 0 || nalu.m_temporalId <= m_iMaxTemporalLayer )
188
3.29k
  {
189
3.29k
    newPic = m_decLibParser.parse( nalu );
190
3.29k
  }
191
192
3.29k
  if( newPic )
193
0
  {
194
0
    Picture* pcParsedPic = m_decLibParser.getNextDecodablePicture();
195
0
    if( pcParsedPic )
196
0
    {
197
0
      while( pcParsedPic->error || pcParsedPic->wasLost || pcParsedPic->parseDone.hasException() )
198
0
      {
199
0
        CHECK_FATAL( pcParsedPic->progress >= Picture::reconstructing, "The error picture shouldn't be in reconstructing state yet." );
200
201
0
        std::exception_ptr parsing_exception = pcParsedPic->parseDone.hasException() ? pcParsedPic->parseDone.getException() : nullptr;
202
0
        if( parsing_exception )
203
0
        {   // the exception has not been thrown out of the library. Do that after preparing this picture for referencing
204
0
          pcParsedPic->error = true;
205
0
          pcParsedPic->waitForAllTasks();
206
0
          pcParsedPic->parseDone.clearException();
207
0
        }
208
209
0
        pcParsedPic->waitForAllTasks();
210
211
0
        if( pcParsedPic->progress < Picture::parsing )
212
0
        {
213
          // we don't know if all structures are there yet, so we init them
214
0
          pcParsedPic->ensureUsableAsRef();
215
0
        }
216
0
        pcParsedPic->fillGrey( m_decLibParser.getParameterSetManager().getFirstSPS() );
217
218
        // need to finish picture here, because it won't go through declibRecon
219
0
        finishPicture( pcParsedPic );
220
221
        // this exception has not been thrown outside (error must have happened in slice parsing task)
222
0
        if( parsing_exception )
223
0
        {
224
0
          CHECK_FATAL( pcParsedPic->exceptionThrownOut, "The exception shouldn't have been thrown out already." );
225
0
          pcParsedPic->exceptionThrownOut = true;
226
0
          std::rethrow_exception( parsing_exception );
227
0
        }
228
229
        // try again to get a picture, that we can reconstruct now
230
0
        pcParsedPic = m_decLibParser.getNextDecodablePicture();
231
0
      }
232
233
0
      reconPicture( pcParsedPic );
234
0
    }
235
0
  }
236
237
3.29k
  if( newPic || nalu.m_nalUnitType == NAL_UNIT_EOS )
238
1
  {
239
1
    Picture* outPic = getNextOutputPic( false );
240
1
    if( outPic )
241
0
    {
242
0
      CHECK_WARN( outPic->progress < Picture::finished, "Picture should have been finished by now. Blocking and finishing..." );
243
0
      if( outPic->progress < Picture::finished )
244
0
      {
245
0
        blockAndFinishPictures( outPic );
246
247
0
        CHECK( outPic->progress < Picture::finished, "Picture still not finished. Something is really broken." );
248
0
      }
249
250
0
      m_checkMissingOutput = true;
251
0
    }
252
253
    // warn if we don't produce an output picture for every incoming picture
254
1
    CHECK_WARN( m_checkMissingOutput && !outPic, "missing output picture" ); // this CHECK_FATAL is not needed in flushPic(), because in flushPic() the nullptr signals the end of the bitstream
255
1
    return outPic;
256
1
  }
257
258
3.29k
  return nullptr;
259
3.29k
}
260
261
Picture* DecLib::flushPic()
262
916
{
263
916
  Picture* outPic = getNextOutputPic( false );
264
916
  try
265
916
  {
266
    // at end of file, fill the decompression queue and decode pictures until the next output-picture is finished
267
1.62k
    while( Picture* pcParsedPic = m_decLibParser.getNextDecodablePicture() )
268
713
    {
269
      // reconPicture() blocks and finishes one picture on each call
270
713
      reconPicture( pcParsedPic );
271
272
713
      if( !outPic )
273
713
      {
274
713
        outPic = getNextOutputPic( false );
275
713
      }
276
713
      if( outPic && outPic->progress == Picture::finished )
277
0
      {
278
0
        return outPic;
279
0
      }
280
713
    }
281
282
916
    if( outPic && outPic->progress == Picture::finished )
283
0
    {
284
0
      return outPic;
285
0
    }
286
287
    // if all pictures have been parsed, but not finished, iteratively wait for and finish next pictures
288
916
    blockAndFinishPictures( outPic );
289
916
    if( !outPic )
290
206
    {
291
206
      outPic = getNextOutputPic( false );
292
206
    }
293
916
    if( outPic && outPic->progress == Picture::finished )
294
0
    {
295
0
      return outPic;
296
0
    }
297
298
916
    CHECK( outPic, "we shouldn't be holding an output picture here" );
299
    // flush remaining pictures without considering num reorder pics
300
916
    outPic = getNextOutputPic( true );
301
916
    if( outPic )
302
3
    {
303
3
      CHECK( outPic->progress != Picture::finished, "all pictures should have been finished by now" );
304
      // outPic->referenced = false;
305
3
      return outPic;
306
3
    }
307
916
  }
308
916
  catch( ... )
309
916
  {
310
710
    m_picListManager.releasePicture(outPic);
311
710
    throw;
312
710
  }
313
314
  // At the very end reset parser state
315
203
  InputNALUnit eosNAL;
316
203
  eosNAL.m_nalUnitType = NAL_UNIT_EOS;
317
203
  m_decLibParser.parse( eosNAL );
318
203
  m_checkMissingOutput = false;
319
320
203
  return nullptr;
321
916
}
322
323
#if JVET_R0270
324
int DecLib::finishPicture( Picture* pcPic, MsgLevel msgl, bool associatedWithNewClvs )
325
#else
326
int DecLib::finishPicture( Picture* pcPic, MsgLevel msgl )
327
#endif
328
713
{
329
#ifdef TRACE_ENABLE_ITT
330
  // increment Framecounter
331
  __itt_counter_inc( itt_frame_counter );
332
#endif
333
334
713
  Slice*  pcSlice = pcPic->slices[0];
335
713
  if( pcPic->wasLost || pcPic->error || pcPic->reconDone.hasException() || pcPic->parseDone.hasException() )
336
710
  {
337
710
    msg( msgl, "POC %4d LId: %2d TId: %1d %s\n", pcPic->poc, pcPic->layerId, pcSlice->getTLayer(), pcPic->wasLost ? "LOST" : "ERROR" );
338
710
    pcPic->progress = Picture::finished;
339
340
    // if the picture has an exception set (originating from thread-pool tasks), don't return here, but rethrow the exception
341
710
    try
342
710
    {
343
710
      pcPic->parseDone.checkAndRethrowException();
344
710
      pcPic->reconDone.checkAndRethrowException();
345
710
    }
346
710
    catch( ... )
347
710
    {
348
710
      pcPic->waitForAllTasks();
349
350
      // need to clear exception so we can use it as reference picture
351
710
      pcPic->reconDone.clearException();
352
710
      pcPic->reconDone.unlock();
353
710
      pcPic->error = true;
354
710
      if( !pcPic->exceptionThrownOut )
355
710
      {
356
710
        pcPic->exceptionThrownOut = true;
357
710
        throw;
358
710
      }
359
710
    }
360
361
0
    return pcPic->poc;
362
710
  }
363
364
3
  ITT_TASKSTART( itt_domain_oth, itt_handle_finish );
365
366
3
  char c = ( pcSlice->isIntra() ? 'I' : pcSlice->isInterP() ? 'P' : 'B' );
367
3
  if( !pcPic->isReferencePic )
368
0
  {
369
0
    c += 32;  // tolower
370
0
  }
371
372
  //-- For time output for each slice
373
3
  msg( msgl, "POC %4d LId: %2d TId: %1d ( %c-SLICE, QP%3d ) ", pcPic->poc, pcPic->layerId,
374
3
         pcSlice->getTLayer(),
375
3
         c,
376
3
         pcSlice->getSliceQp() );
377
3
  msg( msgl, "[DT %6.3f] ", pcPic->getProcessingTime() );
378
379
9
  for (int iRefList = 0; iRefList < 2; iRefList++)
380
6
  {
381
6
    msg( msgl, "[L%d ", iRefList);
382
6
    for (int iRefIndex = 0; iRefIndex < pcSlice->getNumRefIdx(RefPicList(iRefList)); iRefIndex++)
383
0
    {
384
0
#if RPR_OUTPUT_MSG
385
0
      const std::pair<int, int>& scaleRatio = pcSlice->getScalingRatio( RefPicList( iRefList ), iRefIndex );
386
      
387
0
      if( pcSlice->getPicHeader()->getEnableTMVPFlag() && pcSlice->getColFromL0Flag() == bool(1 - iRefList) && pcSlice->getColRefIdx() == iRefIndex )
388
0
      {
389
0
        if( scaleRatio.first != 1 << SCALE_RATIO_BITS || scaleRatio.second != 1 << SCALE_RATIO_BITS )
390
0
        {
391
0
          msg( msgl, "%dc(%1.2lfx, %1.2lfx) ", pcSlice->getRefPOC( RefPicList( iRefList ), iRefIndex ), double( scaleRatio.first ) / ( 1 << SCALE_RATIO_BITS ), double( scaleRatio.second ) / ( 1 << SCALE_RATIO_BITS ) );
392
0
        }
393
0
        else
394
0
        {
395
0
          msg( msgl, "%dc ", pcSlice->getRefPOC( RefPicList( iRefList ), iRefIndex ) );
396
0
        }
397
0
      }
398
0
      else
399
0
      {
400
0
        if( scaleRatio.first != 1 << SCALE_RATIO_BITS || scaleRatio.second != 1 << SCALE_RATIO_BITS )
401
0
        {
402
0
          msg( msgl, "%d(%1.2lfx, %1.2lfx) ", pcSlice->getRefPOC( RefPicList( iRefList ), iRefIndex ), double( scaleRatio.first ) / ( 1 << SCALE_RATIO_BITS ), double( scaleRatio.second ) / ( 1 << SCALE_RATIO_BITS ) );
403
0
        }
404
0
        else
405
0
        {
406
0
      msg( msgl, "%d ", pcSlice->getRefPOC(RefPicList(iRefList), iRefIndex));
407
0
    }
408
0
      }
409
#else
410
      msg( msgl, "%d ", pcSlice->getRefPOC(RefPicList(iRefList), iRefIndex));
411
#endif
412
0
    }
413
6
    msg( msgl, "] ");
414
6
  }
415
416
3
  msg( msgl, "\n");
417
418
//  pcPic->neededForOutput = (pcSlice->getPicHeader()->getPicOutputFlag() ? true : false);
419
#if JVET_R0270
420
  if (associatedWithNewClvs && pcPic->neededForOutput)
421
  {
422
    if (!pcSlice->getPPS()->getMixedNaluTypesInPicFlag() && pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_RASL)
423
    {
424
      pcPic->neededForOutput = false;
425
    }
426
    else if (pcSlice->getPPS()->getMixedNaluTypesInPicFlag())
427
    {
428
      bool isRaslPic = true;
429
      for (int i = 0; isRaslPic && i < pcPic->numSlices; i++)
430
      {
431
        if (!(pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_RASL || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_RADL))
432
        {
433
          isRaslPic = false;
434
        }
435
      }
436
      if (isRaslPic)
437
      {
438
        pcPic->neededForOutput = false;
439
      }
440
    }
441
  }
442
#endif
443
444
3
  m_picListManager.markUnusedPicturesReusable();
445
446
3
  if( m_parseFrameDelay > 0 )
447
3
  {
448
3
    checkPictureHashSEI( pcPic );
449
3
  }
450
451
3
  ITT_TASKEND( itt_domain_oth, itt_handle_finish );
452
453
3
  pcPic->progress = Picture::finished;
454
455
3
  return pcSlice->getPOC();
456
713
}
457
458
void DecLib::checkPictureHashSEI( Picture* pcPic )
459
3
{
460
3
  if( !m_decodedPictureHashSEIEnabled )
461
3
  {
462
3
    return;
463
3
  }
464
0
  if( !pcPic->neededForOutput || pcPic->picCheckedDPH )
465
0
  {
466
0
    return;
467
0
  }
468
469
0
  CHECK( pcPic->progress < Picture::reconstructed, "picture not reconstructed" );
470
471
0
  seiMessages pictureHashes = SEI_internal::getSeisByType( pcPic->seiMessageList, VVDEC_DECODED_PICTURE_HASH );
472
0
  if( !pictureHashes.empty() )
473
0
  {
474
0
    if( pictureHashes.size() > 1 )
475
0
    {
476
0
      msg( WARNING, "Warning: Got multiple decoded picture hash SEI messages. Using first." );
477
0
    }
478
479
0
    const vvdecSEIDecodedPictureHash* hash = (vvdecSEIDecodedPictureHash*)pictureHashes.front()->payload;
480
481
0
    msg( INFO, "         " );
482
0
    const int hashErrors              = calcAndPrintHashStatus( pcPic->getRecoBuf(), hash, pcPic->cs->sps->getBitDepths(), INFO );
483
0
    m_numberOfChecksumErrorsDetected += hashErrors;
484
0
    pcPic->dphMismatch                = !!hashErrors;
485
0
    pcPic->picCheckedDPH              = true;
486
0
    msg( INFO, "\n" );
487
0
  }
488
0
  else
489
0
  {
490
0
    if( pcPic->subPictures.empty() )
491
0
    {
492
0
      msg( WARNING, "Warning: missing decoded picture hash SEI message.\n" );
493
0
      return;
494
0
    }
495
496
0
    seiMessages scalableNestingSeis = SEI_internal::getSeisByType( pcPic->seiMessageList, VVDEC_SCALABLE_NESTING );
497
0
    for( auto* seiIt: scalableNestingSeis )
498
0
    {
499
0
      CHECK( seiIt->payloadType != VVDEC_SCALABLE_NESTING, "expected nesting SEI" );
500
501
0
      const vvdecSEIScalableNesting* nestingSei = (vvdecSEIScalableNesting*) seiIt->payload;
502
0
      if( !nestingSei->snSubpicFlag )
503
0
      {
504
0
        continue;
505
0
      }
506
507
0
      for( int i = 0; i < nestingSei->snNumSEIs; ++i )
508
0
      {
509
0
        auto& nestedSei = nestingSei->nestedSEIs[i];
510
0
        CHECK( nestedSei == nullptr, "missing nested sei" );
511
0
        if( nestedSei && nestedSei->payloadType != VVDEC_DECODED_PICTURE_HASH )
512
0
          continue;
513
514
0
        const vvdecSEIDecodedPictureHash* hash = (vvdecSEIDecodedPictureHash*) nestedSei->payload;
515
516
0
        if( pcPic->subpicsCheckedDPH.empty() )
517
0
        {
518
0
          pcPic->subpicsCheckedDPH.resize( pcPic->subPictures.size(), false );
519
0
        }
520
0
        else
521
0
        {
522
0
          CHECK( pcPic->subpicsCheckedDPH.size() != pcPic->subPictures.size(), "Picture::subpicsCheckedDPH not properly initialized" );
523
0
        }
524
525
0
        for( int j = 0; j < nestingSei->snNumSubpics; ++j )
526
0
        {
527
0
          uint32_t subpicId = nestingSei->snSubpicId[j];
528
529
0
          for( auto& subPic: pcPic->subPictures )
530
0
          {
531
0
            if( subPic.getSubPicID() != subpicId )
532
0
              continue;
533
534
0
            auto subPicIdx = subPic.getSubPicIdx();
535
0
            if( pcPic->subpicsCheckedDPH[subPicIdx] )
536
0
              continue;
537
538
0
            const UnitArea area                 = UnitArea( pcPic->chromaFormat, subPic.getLumaArea() );
539
0
            const int      hashErrors           = calcAndPrintHashStatus( pcPic->cs->getRecoBuf( area ), hash, pcPic->cs->sps->getBitDepths(), INFO );
540
0
            m_numberOfChecksumErrorsDetected   += hashErrors;
541
0
            pcPic->dphMismatch                 |= !!hashErrors;
542
0
            pcPic->subpicsCheckedDPH[subPicIdx] = true;
543
0
            msg( INFO, "\n" );
544
0
          }
545
0
        }
546
0
      }
547
0
    }
548
549
0
    size_t checkedSubpicCount = std::count( pcPic->subpicsCheckedDPH.cbegin(), pcPic->subpicsCheckedDPH.cend(), true );
550
0
    pcPic->picCheckedDPH      = ( checkedSubpicCount == pcPic->subPictures.size() );   // mark when all subpics have been checked
551
552
0
    if( m_parseFrameDelay )
553
0
    {
554
      // this warning is only enabled, when running with parse delay enabled, because otherwise we don't know here if the last DPH Suffix-SEI has already been
555
      // parsed
556
0
      if( checkedSubpicCount != pcPic->subPictures.size() )
557
0
      {
558
0
        msg( WARNING, "Warning: missing decoded picture hash SEI message for SubPics (%u/%u).\n", checkedSubpicCount, pcPic->subPictures.size() );
559
0
      }
560
0
    }
561
0
  }
562
0
}
563
564
Picture* DecLib::getNextOutputPic( bool bFlush )
565
2.04k
{
566
2.04k
  if( m_picListManager.getFrontPic() == nullptr )
567
598
  {
568
598
    return nullptr;
569
598
  }
570
571
1.44k
  const SPS* activeSPS      = m_picListManager.getFrontPic()->cs->sps.get();
572
1.44k
  const int  maxNrSublayers = activeSPS->getMaxTLayers();
573
574
1.44k
  int numReorderPicsHighestTid;
575
1.44k
  int maxDecPicBufferingHighestTid;
576
1.44k
  if( m_iMaxTemporalLayer == -1 || m_iMaxTemporalLayer >= maxNrSublayers )
577
1.44k
  {
578
1.44k
    numReorderPicsHighestTid     = activeSPS->getNumReorderPics( maxNrSublayers - 1 );
579
1.44k
    maxDecPicBufferingHighestTid = activeSPS->getMaxDecPicBuffering( maxNrSublayers - 1 );
580
1.44k
  }
581
0
  else
582
0
  {
583
0
    numReorderPicsHighestTid     = activeSPS->getNumReorderPics( m_iMaxTemporalLayer );
584
0
    maxDecPicBufferingHighestTid = activeSPS->getMaxDecPicBuffering( m_iMaxTemporalLayer );
585
0
  }
586
587
1.44k
  return m_picListManager.getNextOutputPic( numReorderPicsHighestTid, maxDecPicBufferingHighestTid, bFlush );
588
2.04k
}
589
590
void DecLib::reconPicture( Picture* pcPic )
591
713
{
592
713
  CHECK_FATAL( std::any_of( m_decLibRecon.begin(), m_decLibRecon.end(), [=]( auto& rec ) { return rec.getCurrPic() == pcPic; } ),
593
713
         "(Reused) Picture structure is still in progress in decLibRecon." );
594
595
713
  DecLibRecon* reconInstance = &m_decLibRecon.front();
596
713
  move_to_end( m_decLibRecon.begin(), m_decLibRecon );
597
598
713
  Picture* donePic = reconInstance->waitForPrevDecompressedPic();
599
713
  try
600
713
  {
601
713
    reconInstance->decompressPicture( pcPic );
602
713
  }
603
713
  catch( ... )
604
713
  {
605
92
    pcPic->reconDone.setException( std::current_exception() );
606
92
    pcPic->error = true;
607
92
  }
608
609
713
  if( donePic )
610
0
  {
611
0
    finishPicture( donePic );
612
0
  }
613
713
}
614
615
void DecLib::blockAndFinishPictures( Picture* pcPic )
616
916
{
617
  // find Recon instance where current picture (if not null) is active and ensure the picture gets finished
618
  // otherwise all pictures get finished
619
916
  for( auto& recon: m_decLibRecon )
620
1.83k
  {
621
1.83k
    if( pcPic && recon.getCurrPic() != pcPic )
622
0
    {
623
0
      continue;
624
0
    }
625
626
1.83k
    if( Picture* donePic = recon.waitForPrevDecompressedPic() )
627
713
    {
628
713
      finishPicture( donePic );
629
713
    }
630
1.83k
  }
631
916
}
632
633
void DecLib::checkNalUnitConstraints( uint32_t naluType )
634
0
{
635
0
  const ConstraintInfo *cInfo = NULL;
636
0
  if (m_decLibParser.getParameterSetManager().getActiveSPS() != NULL && m_decLibParser.getParameterSetManager().getActiveSPS()->getProfileTierLevel() != NULL)
637
0
  {
638
0
    cInfo = m_decLibParser.getParameterSetManager().getActiveSPS()->getProfileTierLevel()->getConstraintInfo();
639
0
    if( cInfo != NULL )
640
0
    {
641
0
      xCheckNalUnitConstraintFlags( cInfo, naluType );
642
0
    }
643
0
  }
644
0
}
645
646
void DecLib::xCheckNalUnitConstraintFlags( const ConstraintInfo *cInfo, uint32_t naluType )
647
0
{
648
0
  if( cInfo != NULL )
649
0
  {
650
0
    CHECK( cInfo->getNoTrailConstraintFlag() && naluType == NAL_UNIT_CODED_SLICE_TRAIL,
651
0
           "Non-conforming bitstream. no_trail_constraint_flag is equal to 1 but bitstream contains NAL unit of type TRAIL_NUT." );
652
0
    CHECK( cInfo->getNoStsaConstraintFlag()  && naluType == NAL_UNIT_CODED_SLICE_STSA,
653
0
           "Non-conforming bitstream. no_stsa_constraint_flag is equal to 1 but bitstream contains NAL unit of type STSA_NUT." );
654
0
    CHECK( cInfo->getNoRaslConstraintFlag()  && naluType == NAL_UNIT_CODED_SLICE_RASL,
655
0
           "Non-conforming bitstream. no_rasl_constraint_flag is equal to 1 but bitstream contains NAL unit of type RASL_NUT." );
656
0
    CHECK( cInfo->getNoRadlConstraintFlag()  && naluType == NAL_UNIT_CODED_SLICE_RADL,
657
0
           "Non-conforming bitstream. no_radl_constraint_flag is equal to 1 but bitstream contains NAL unit of type RADL_NUT." );
658
0
    CHECK( cInfo->getNoIdrConstraintFlag()   && naluType == NAL_UNIT_CODED_SLICE_IDR_W_RADL,
659
0
           "Non-conforming bitstream. no_idr_constraint_flag is equal to 1 but bitstream contains NAL unit of type IDR_W_RADL." );
660
0
    CHECK( cInfo->getNoIdrConstraintFlag()   && naluType == NAL_UNIT_CODED_SLICE_IDR_N_LP,
661
0
           "Non-conforming bitstream. no_idr_constraint_flag is equal to 1 but bitstream contains NAL unit of type IDR_N_LP." );
662
0
    CHECK( cInfo->getNoCraConstraintFlag()   && naluType == NAL_UNIT_CODED_SLICE_CRA,
663
0
           "Non-conforming bitstream. no_cra_constraint_flag is equal to 1 but bitstream contains NAL unit of type CRA_NUT." );
664
0
    CHECK( cInfo->getNoGdrConstraintFlag()   && naluType == NAL_UNIT_CODED_SLICE_GDR,
665
0
           "Non-conforming bitstream. no_gdr_constraint_flag is equal to 1 but bitstream contains NAL unit of type GDR_NUT." );
666
0
    CHECK( cInfo->getNoApsConstraintFlag()   && naluType == NAL_UNIT_PREFIX_APS,
667
0
           "Non-conforming bitstream. no_aps_constraint_flag is equal to 1 but bitstream contains NAL unit of type APS_PREFIX_NUT." );
668
0
    CHECK( cInfo->getNoApsConstraintFlag()   && naluType == NAL_UNIT_SUFFIX_APS,
669
0
           "Non-conforming bitstream. no_aps_constraint_flag is equal to 1 but bitstream contains NAL unit of type APS_SUFFIX_NUT." );
670
0
  }
671
0
}
672
673
0
#define SEI_REPETITION_CONSTRAINT_LIST_SIZE  21
674
675
/**
676
 - Count the number of identical SEI messages in the current picture
677
 */
678
void DecLib::checkSeiInPictureUnit()
679
0
{
680
0
  std::vector<std::tuple<int, uint32_t, uint8_t*>> seiList;
681
682
  // payload types subject to constrained SEI repetition
683
0
  int picUnitRepConSeiList[SEI_REPETITION_CONSTRAINT_LIST_SIZE] = { 0, 1, 19, 45, 129, 132, 133, 137, 144, 145, 147, 148, 149, 150, 153, 154, 155, 156, 168, 203, 204};
684
  
685
  // extract SEI messages from NAL units
686
0
  for( auto &sei : m_pictureSeiNalus )
687
0
  {
688
0
    InputBitstream bs = sei.getBitstream();
689
690
0
    do
691
0
    {
692
0
      int payloadType = 0;
693
0
      uint32_t val = 0;
694
695
0
      do
696
0
      {
697
0
        val = bs.readByte();
698
0
        payloadType += val;
699
0
      } while (val==0xFF);
700
701
0
      uint32_t payloadSize = 0;
702
0
      do
703
0
      {
704
0
        val = bs.readByte();
705
0
        payloadSize += val;
706
0
      } while (val==0xFF);
707
708
0
      uint8_t *payload = new uint8_t[payloadSize];
709
0
      for( uint32_t i = 0; i < payloadSize; i++ )
710
0
      {
711
0
        val = bs.readByte();
712
0
        payload[i] = (uint8_t)val;
713
0
      }
714
0
      seiList.push_back(std::tuple<int, uint32_t, uint8_t*>(payloadType, payloadSize, payload));
715
0
    }
716
0
    while( bs.getNumBitsLeft() > 8 );
717
0
  }
718
719
  // count repeated messages in list
720
0
  for( uint32_t i = 0; i < seiList.size(); i++ )
721
0
  {
722
0
    int      k, count = 1;
723
0
    int      payloadType1 = std::get<0>(seiList[i]);
724
0
    uint32_t payloadSize1 = std::get<1>(seiList[i]);
725
0
    uint8_t  *payload1    = std::get<2>(seiList[i]);
726
727
    // only consider SEI payload types in the PicUnitRepConSeiList
728
0
    for( k=0; k<SEI_REPETITION_CONSTRAINT_LIST_SIZE; k++ )
729
0
    {
730
0
      if( payloadType1 == picUnitRepConSeiList[k] )
731
0
      {
732
0
        break;
733
0
      }
734
0
    }
735
0
    if( k >= SEI_REPETITION_CONSTRAINT_LIST_SIZE )
736
0
    {
737
0
      continue;
738
0
    }
739
740
    // compare current SEI message with remaining messages in the list
741
0
    for( uint32_t j = i+1; j < seiList.size(); j++ )
742
0
    {
743
0
      int      payloadType2 = std::get<0>(seiList[j]);
744
0
      uint32_t payloadSize2 = std::get<1>(seiList[j]);
745
0
      uint8_t  *payload2    = std::get<2>(seiList[j]);
746
      
747
      // check for identical SEI type, size, and payload
748
0
      if( payloadType1 == payloadType2 && payloadSize1 == payloadSize2 )
749
0
      {
750
0
        if( memcmp(payload1, payload2, payloadSize1*sizeof(uint8_t)) == 0 )
751
0
        {
752
0
          count++;
753
0
        }
754
0
      }
755
0
    }
756
0
    CHECK(count > 4, "There shall be less than or equal to 4 identical sei_payload( ) syntax structures within a picture unit.");
757
0
  }
758
759
  // free SEI message list memory
760
0
  for( uint32_t i = 0; i < seiList.size(); i++ )
761
0
  {
762
0
    uint8_t *payload = std::get<2>(seiList[i]);
763
0
    delete   payload;
764
0
  }
765
0
  seiList.clear();
766
0
}
767
768
/**
769
 - Reset list of SEI NAL units from the current picture
770
 */
771
void DecLib::resetPictureSeiNalus()
772
0
{
773
0
  m_pictureSeiNalus.clear();
774
0
}
775
776
void DecLib::checkAPSInPictureUnit()
777
0
{
778
0
  bool firstVCLFound = false;
779
0
  bool suffixAPSFound = false;
780
0
  for (auto &nalu : m_pictureUnitNals)
781
0
  {
782
0
    if (NALUnit::isVclNalUnitType(nalu))
783
0
    {
784
0
      firstVCLFound = true;
785
0
      CHECK( suffixAPSFound, "When any suffix APS NAL units are present in a PU, they shall follow the last VCL unit of the PU" );
786
0
    }
787
0
    else if (nalu == NAL_UNIT_PREFIX_APS)
788
0
    {
789
0
      CHECK( firstVCLFound, "When any prefix APS NAL units are present in a PU, they shall precede the first VCL unit of the PU");
790
0
    }
791
0
    else if (nalu == NAL_UNIT_SUFFIX_APS)
792
0
    {
793
0
      suffixAPSFound = true;
794
0
    }
795
0
  }
796
0
}
797
798
}