Coverage Report

Created: 2026-07-25 07:06

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
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
3.29k
  if( newPic || nalu.m_nalUnitType == NAL_UNIT_EOS )
213
1
  {
214
1
    Picture* outPic = getNextOutputPic( false );
215
1
    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
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
230
1
    return outPic;
231
1
  }
232
233
3.29k
  return nullptr;
234
3.29k
}
235
236
Picture* DecLib::flushPic()
237
916
{
238
916
  Picture* outPic = nullptr;
239
916
  try
240
916
  {
241
    // at end of file, fill the decompression queue and decode pictures until the next output-picture is finished
242
1.62k
    while( Picture* pcParsedPic = m_decLibParser.getNextDecodablePicture() )
243
713
    {
244
713
      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
713
      reconPicture( pcParsedPic );
256
257
713
      if( !outPic )
258
713
      {
259
713
        outPic = getNextOutputPic( false );
260
713
      }
261
713
      if( outPic && outPic->progress == Picture::finished )
262
0
      {
263
0
        return outPic;
264
0
      }
265
713
    }
266
267
    // no more pictures to finish decoding, but we might still have finished pictures in the recon queue
268
916
    if( !outPic )
269
916
    {
270
916
      outPic = getNextOutputPic( false );
271
916
    }
272
916
    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
916
    blockAndFinishPictures( outPic );
279
916
    if( !outPic )
280
206
    {
281
206
      outPic = getNextOutputPic( false );
282
206
    }
283
916
    if( outPic && outPic->progress == Picture::finished )
284
0
    {
285
0
      return outPic;
286
0
    }
287
288
916
    CHECK( outPic, "we shouldn't be holding an output picture here" );
289
    // flush remaining pictures without considering num reorder pics
290
916
    outPic = getNextOutputPic( true );
291
916
    if( outPic )
292
3
    {
293
3
      CHECK( outPic->progress != Picture::finished, "all pictures should have been finished by now" );
294
      // outPic->referenced = false;
295
3
      return outPic;
296
3
    }
297
916
  }
298
916
  catch( ... )
299
916
  {
300
710
    m_picListManager.releasePicture( outPic );
301
710
    throw;
302
710
  }
303
304
  // At the very end reset parser state
305
203
  InputNALUnit eosNAL;
306
203
  eosNAL.m_nalUnitType = NAL_UNIT_EOS;
307
203
  m_decLibParser.parse( eosNAL );
308
203
  m_checkMissingOutput = false;
309
310
203
  return nullptr;
311
916
}
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
713
{
350
#ifdef TRACE_ENABLE_ITT
351
  // increment Framecounter
352
  __itt_counter_inc( itt_frame_counter );
353
#endif
354
355
713
  Slice*  pcSlice = pcPic->slices[0];
356
713
  if( pcPic->wasLost || pcPic->error || pcPic->reconDone.hasException() || pcPic->parseDone.hasException() )
357
710
  {
358
710
    msg( msgl, "POC %4d LId: %2d TId: %1d %s\n", pcPic->poc, pcPic->layerId, pcSlice->getTLayer(), pcPic->wasLost ? "LOST" : "ERROR" );
359
710
    pcPic->fillGrey( pcPic->cs->sps.get() );
360
710
    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
710
    try
364
710
    {
365
710
      pcPic->parseDone.checkAndRethrowException();
366
710
      pcPic->reconDone.checkAndRethrowException();
367
710
    }
368
710
    catch( ... )
369
710
    {
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
710
  }
385
386
3
  ITT_TASKSTART( itt_domain_oth, itt_handle_finish );
387
388
3
  char c = ( pcSlice->isIntra() ? 'I' : pcSlice->isInterP() ? 'P' : 'B' );
389
3
  if( !pcPic->isReferencePic )
390
0
  {
391
0
    c += 32;  // tolower
392
0
  }
393
394
  //-- For time output for each slice
395
3
  msg( msgl, "POC %4d LId: %2d TId: %1d ( %c-SLICE, QP%3d ) ", pcPic->poc, pcPic->layerId,
396
3
         pcSlice->getTLayer(),
397
3
         c,
398
3
         pcSlice->getSliceQp() );
399
3
  msg( msgl, "[DT %6.3f] ", pcPic->getProcessingTime() );
400
401
9
  for (int iRefList = 0; iRefList < 2; iRefList++)
402
6
  {
403
6
    msg( msgl, "[L%d ", iRefList);
404
6
    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
6
    msg( msgl, "] ");
436
6
  }
437
438
3
  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
3
  m_picListManager.markUnusedPicturesReusable();
467
468
3
  if( m_parseFrameDelay > 0 )
469
3
  {
470
3
    checkPictureHashSEI( pcPic );
471
3
  }
472
473
3
  ITT_TASKEND( itt_domain_oth, itt_handle_finish );
474
475
3
  pcPic->progress = Picture::finished;
476
477
3
  return pcSlice->getPOC();
478
713
}
479
480
void DecLib::checkPictureHashSEI( Picture* pcPic )
481
3
{
482
3
  if( !m_decodedPictureHashSEIEnabled )
483
3
  {
484
3
    return;
485
3
  }
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
2.04k
{
588
2.04k
  if( m_picListManager.getFrontPic() == nullptr )
589
598
  {
590
598
    return nullptr;
591
598
  }
592
593
1.44k
  const SPS* activeSPS      = m_picListManager.getFrontPic()->cs->sps.get();
594
1.44k
  const int  maxNrSublayers = activeSPS->getMaxTLayers();
595
596
1.44k
  int numReorderPicsHighestTid;
597
1.44k
  int maxDecPicBufferingHighestTid;
598
1.44k
  if( m_iMaxTemporalLayer == -1 || m_iMaxTemporalLayer >= maxNrSublayers )
599
1.44k
  {
600
1.44k
    numReorderPicsHighestTid     = activeSPS->getNumReorderPics( maxNrSublayers - 1 );
601
1.44k
    maxDecPicBufferingHighestTid = activeSPS->getMaxDecPicBuffering( maxNrSublayers - 1 );
602
1.44k
  }
603
0
  else
604
0
  {
605
0
    numReorderPicsHighestTid     = activeSPS->getNumReorderPics( m_iMaxTemporalLayer );
606
0
    maxDecPicBufferingHighestTid = activeSPS->getMaxDecPicBuffering( m_iMaxTemporalLayer );
607
0
  }
608
609
1.44k
  return m_picListManager.getNextOutputPic( numReorderPicsHighestTid, maxDecPicBufferingHighestTid, bFlush );
610
2.04k
}
611
612
void DecLib::reconPicture( Picture* pcPic )
613
713
{
614
713
  CHECK_FATAL( std::any_of( m_decLibRecon.begin(), m_decLibRecon.end(), [=]( auto& rec ) { return rec.getCurrPic() == pcPic; } ),
615
713
         "(Reused) Picture structure is still in progress in decLibRecon." );
616
617
713
  DecLibRecon* reconInstance = &m_decLibRecon.front();
618
713
  move_to_end( m_decLibRecon.begin(), m_decLibRecon );
619
620
713
  Picture* donePic = reconInstance->waitForPrevDecompressedPic();
621
713
  try
622
713
  {
623
713
    reconInstance->decompressPicture( pcPic );
624
713
  }
625
713
  catch( ... )
626
713
  {
627
27
    pcPic->reconDone.setException( std::current_exception() );
628
27
    pcPic->error = true;
629
27
  }
630
631
713
  if( donePic )
632
0
  {
633
0
    finishPicture( donePic );
634
0
  }
635
713
}
636
637
void DecLib::blockAndFinishPictures( Picture* pcPic )
638
916
{
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
916
  for( auto& recon: m_decLibRecon )
642
1.83k
  {
643
1.83k
    if( pcPic && recon.getCurrPic() != pcPic )
644
0
    {
645
0
      continue;
646
0
    }
647
648
1.83k
    if( Picture* donePic = recon.waitForPrevDecompressedPic() )
649
713
    {
650
713
      finishPicture( donePic );
651
713
    }
652
1.83k
  }
653
916
}
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
}