Coverage Report

Created: 2026-07-16 06:32

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