Coverage Report

Created: 2026-04-01 07:49

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
0
{
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
0
}
114
115
void DecLib::create( int numDecThreads, int parserFrameDelay, const UserAllocator& userAllocator, ErrHandlingFlags errHandlingFlags )
116
0
{
117
  // run constructor again to ensure all variables, especially in DecLibParser have been reset
118
0
  this->~DecLib();
119
0
  new( this ) DecLib;
120
121
0
  if( numDecThreads < 0 )
122
0
  {
123
0
    numDecThreads = std::thread::hardware_concurrency();
124
0
  }
125
126
0
  m_decodeThreadPool.reset( new ThreadPool( numDecThreads, "DecThread" ) );
127
128
0
  if( parserFrameDelay < 0 )
129
0
  {
130
0
    CHECK_FATAL( numDecThreads < 0, "invalid number of threads" );
131
0
    parserFrameDelay = std::min<int>( ( numDecThreads * DEFAULT_PARSE_DELAY_FACTOR ) >> 4, DEFAULT_PARSE_DELAY_MAX );
132
0
  }
133
0
  m_parseFrameDelay = parserFrameDelay;
134
135
0
  bool upscalingEnabled = false;
136
0
  m_picListManager.create( m_parseFrameDelay, (int) m_decLibRecon.size(), userAllocator );
137
0
  m_decLibParser.create  ( m_decodeThreadPool.get(), m_parseFrameDelay, (int) m_decLibRecon.size(), numDecThreads, errHandlingFlags );
138
139
0
  int id=0;
140
0
  for( auto &dec: m_decLibRecon )
141
0
  {
142
0
    dec.create( m_decodeThreadPool.get(), id++, upscalingEnabled );
143
0
  }
144
145
0
  std::stringstream cssCap;
146
0
  cssCap << "THREADS="     << numDecThreads << "; "
147
0
         << "PARSE_DELAY=" << parserFrameDelay << "; ";
148
0
#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
0
  m_sDecoderCapabilities = cssCap.str();
161
162
0
  DTRACE_UPDATE( g_trace_ctx, std::make_pair( "final", 1 ) );
163
0
}
164
165
void DecLib::destroy()
166
0
{
167
0
  if( m_decodeThreadPool )
168
0
  {
169
0
    m_decodeThreadPool->shutdown( true );
170
0
    m_decodeThreadPool.reset();
171
0
  }
172
173
0
  m_decLibParser.destroy();
174
0
  for( auto &dec: m_decLibRecon )
175
0
  {
176
0
    dec.destroy();
177
0
  }
178
179
0
  m_picListManager.deleteBuffers();
180
0
}
181
182
Picture* DecLib::decode( InputNALUnit& nalu )
183
0
{
184
0
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_NALU_SLICE_PIC_HL );
185
186
0
  bool newPic = false;
187
0
  if( m_iMaxTemporalLayer < 0 || nalu.m_temporalId <= m_iMaxTemporalLayer )
188
0
  {
189
0
    newPic = m_decLibParser.parse( nalu );
190
0
  }
191
192
0
  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
0
  if( newPic || nalu.m_nalUnitType == NAL_UNIT_EOS )
238
0
  {
239
0
    Picture* outPic = getNextOutputPic( false );
240
0
    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
0
    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
0
    return outPic;
256
0
  }
257
258
0
  return nullptr;
259
0
}
260
261
Picture* DecLib::flushPic()
262
0
{
263
0
  Picture* outPic = getNextOutputPic( false );
264
0
  try
265
0
  {
266
    // at end of file, fill the decompression queue and decode pictures until the next output-picture is finished
267
0
    while( Picture* pcParsedPic = m_decLibParser.getNextDecodablePicture() )
268
0
    {
269
      // reconPicture() blocks and finishes one picture on each call
270
0
      reconPicture( pcParsedPic );
271
272
0
      if( !outPic )
273
0
      {
274
0
        outPic = getNextOutputPic( false );
275
0
      }
276
0
      if( outPic && outPic->progress == Picture::finished )
277
0
      {
278
0
        return outPic;
279
0
      }
280
0
    }
281
282
0
    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
0
    blockAndFinishPictures( outPic );
289
0
    if( !outPic )
290
0
    {
291
0
      outPic = getNextOutputPic( false );
292
0
    }
293
0
    if( outPic && outPic->progress == Picture::finished )
294
0
    {
295
0
      return outPic;
296
0
    }
297
298
0
    CHECK( outPic, "we shouldn't be holding an output picture here" );
299
    // flush remaining pictures without considering num reorder pics
300
0
    outPic = getNextOutputPic( true );
301
0
    if( outPic )
302
0
    {
303
0
      CHECK( outPic->progress != Picture::finished, "all pictures should have been finished by now" );
304
      // outPic->referenced = false;
305
0
      return outPic;
306
0
    }
307
0
  }
308
0
  catch( ... )
309
0
  {
310
0
    m_picListManager.releasePicture(outPic);
311
0
    throw;
312
0
  }
313
314
  // At the very end reset parser state
315
0
  InputNALUnit eosNAL;
316
0
  eosNAL.m_nalUnitType = NAL_UNIT_EOS;
317
0
  m_decLibParser.parse( eosNAL );
318
0
  m_checkMissingOutput = false;
319
320
0
  return nullptr;
321
0
}
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
0
{
329
#ifdef TRACE_ENABLE_ITT
330
  // increment Framecounter
331
  __itt_counter_inc( itt_frame_counter );
332
#endif
333
334
0
  Slice*  pcSlice = pcPic->slices[0];
335
0
  if( pcPic->wasLost || pcPic->error || pcPic->reconDone.hasException() )
336
0
  {
337
0
    msg( msgl, "POC %4d LId: %2d TId: %1d %s\n", pcPic->poc, pcPic->layerId, pcSlice->getTLayer(), pcPic->wasLost ? "LOST" : "ERROR" );
338
0
    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
0
    try
342
0
    {
343
0
      pcPic->parseDone.checkAndRethrowException();
344
0
      pcPic->reconDone.checkAndRethrowException();
345
0
    }
346
0
    catch( ... )
347
0
    {
348
0
      pcPic->waitForAllTasks();
349
350
      // need to clear exception so we can use it as reference picture
351
0
      pcPic->reconDone.clearException();
352
0
      pcPic->reconDone.unlock();
353
0
      pcPic->error = true;
354
0
      if( !pcPic->exceptionThrownOut )
355
0
      {
356
0
        pcPic->exceptionThrownOut = true;
357
0
        throw;
358
0
      }
359
0
    }
360
361
0
    return pcPic->poc;
362
0
  }
363
364
0
  ITT_TASKSTART( itt_domain_oth, itt_handle_finish );
365
366
0
  char c = ( pcSlice->isIntra() ? 'I' : pcSlice->isInterP() ? 'P' : 'B' );
367
0
  if( !pcPic->isReferencePic )
368
0
  {
369
0
    c += 32;  // tolower
370
0
  }
371
372
  //-- For time output for each slice
373
0
  msg( msgl, "POC %4d LId: %2d TId: %1d ( %c-SLICE, QP%3d ) ", pcPic->poc, pcPic->layerId,
374
0
         pcSlice->getTLayer(),
375
0
         c,
376
0
         pcSlice->getSliceQp() );
377
0
  msg( msgl, "[DT %6.3f] ", pcPic->getProcessingTime() );
378
379
0
  for (int iRefList = 0; iRefList < 2; iRefList++)
380
0
  {
381
0
    msg( msgl, "[L%d ", iRefList);
382
0
    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
0
    msg( msgl, "] ");
414
0
  }
415
416
0
  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
0
  m_picListManager.markUnusedPicturesReusable();
445
446
0
  if( m_parseFrameDelay > 0 )
447
0
  {
448
0
    checkPictureHashSEI( pcPic );
449
0
  }
450
451
0
  ITT_TASKEND( itt_domain_oth, itt_handle_finish );
452
453
0
  pcPic->progress = Picture::finished;
454
455
0
  return pcSlice->getPOC();
456
0
}
457
458
void DecLib::checkPictureHashSEI( Picture* pcPic )
459
0
{
460
0
  if( !m_decodedPictureHashSEIEnabled )
461
0
  {
462
0
    return;
463
0
  }
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
0
{
566
0
  if( m_picListManager.getFrontPic() == nullptr )
567
0
  {
568
0
    return nullptr;
569
0
  }
570
571
0
  const SPS* activeSPS      = m_picListManager.getFrontPic()->cs->sps.get();
572
0
  const int  maxNrSublayers = activeSPS->getMaxTLayers();
573
574
0
  int numReorderPicsHighestTid;
575
0
  int maxDecPicBufferingHighestTid;
576
0
  if( m_iMaxTemporalLayer == -1 || m_iMaxTemporalLayer >= maxNrSublayers )
577
0
  {
578
0
    numReorderPicsHighestTid     = activeSPS->getNumReorderPics( maxNrSublayers - 1 );
579
0
    maxDecPicBufferingHighestTid = activeSPS->getMaxDecPicBuffering( maxNrSublayers - 1 );
580
0
  }
581
0
  else
582
0
  {
583
0
    numReorderPicsHighestTid     = activeSPS->getNumReorderPics( m_iMaxTemporalLayer );
584
0
    maxDecPicBufferingHighestTid = activeSPS->getMaxDecPicBuffering( m_iMaxTemporalLayer );
585
0
  }
586
587
0
  return m_picListManager.getNextOutputPic( numReorderPicsHighestTid, maxDecPicBufferingHighestTid, bFlush );
588
0
}
589
590
void DecLib::reconPicture( Picture* pcPic )
591
0
{
592
0
  CHECK_FATAL( std::any_of( m_decLibRecon.begin(), m_decLibRecon.end(), [=]( auto& rec ) { return rec.getCurrPic() == pcPic; } ),
593
0
         "(Reused) Picture structure is still in progress in decLibRecon." );
594
595
0
  DecLibRecon* reconInstance = &m_decLibRecon.front();
596
0
  move_to_end( m_decLibRecon.begin(), m_decLibRecon );
597
598
0
  Picture* donePic = reconInstance->waitForPrevDecompressedPic();
599
0
  try
600
0
  {
601
0
    reconInstance->decompressPicture( pcPic );
602
0
  }
603
0
  catch( ... )
604
0
  {
605
0
    pcPic->reconDone.setException( std::current_exception() );
606
0
    pcPic->error = true;
607
0
  }
608
609
0
  if( donePic )
610
0
  {
611
0
    finishPicture( donePic );
612
0
  }
613
0
}
614
615
void DecLib::blockAndFinishPictures( Picture* pcPic )
616
0
{
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
0
  for( auto& recon: m_decLibRecon )
620
0
  {
621
0
    if( pcPic && recon.getCurrPic() != pcPic )
622
0
    {
623
0
      continue;
624
0
    }
625
626
0
    if( Picture* donePic = recon.waitForPrevDecompressedPic() )
627
0
    {
628
0
      finishPicture( donePic );
629
0
    }
630
0
  }
631
0
}
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
}