Coverage Report

Created: 2026-06-15 06:25

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
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
420
  if( newPic || nalu.m_nalUnitType == NAL_UNIT_EOS )
238
4
  {
239
4
    Picture* outPic = getNextOutputPic( false );
240
4
    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
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
255
4
    return outPic;
256
4
  }
257
258
416
  return nullptr;
259
420
}
260
261
Picture* DecLib::flushPic()
262
332
{
263
332
  Picture* outPic = getNextOutputPic( false );
264
332
  try
265
332
  {
266
    // at end of file, fill the decompression queue and decode pictures until the next output-picture is finished
267
332
    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
332
    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
332
    blockAndFinishPictures( outPic );
289
332
    if( !outPic )
290
332
    {
291
332
      outPic = getNextOutputPic( false );
292
332
    }
293
332
    if( outPic && outPic->progress == Picture::finished )
294
0
    {
295
0
      return outPic;
296
0
    }
297
298
332
    CHECK( outPic, "we shouldn't be holding an output picture here" );
299
    // flush remaining pictures without considering num reorder pics
300
332
    outPic = getNextOutputPic( true );
301
332
    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
332
  }
308
332
  catch( ... )
309
332
  {
310
0
    m_picListManager.releasePicture(outPic);
311
0
    throw;
312
0
  }
313
314
  // At the very end reset parser state
315
332
  InputNALUnit eosNAL;
316
332
  eosNAL.m_nalUnitType = NAL_UNIT_EOS;
317
332
  m_decLibParser.parse( eosNAL );
318
332
  m_checkMissingOutput = false;
319
320
332
  return nullptr;
321
332
}
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() || pcPic->parseDone.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
1.00k
{
566
1.00k
  if( m_picListManager.getFrontPic() == nullptr )
567
1.00k
  {
568
1.00k
    return nullptr;
569
1.00k
  }
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
1.00k
}
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
332
{
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
332
  for( auto& recon: m_decLibRecon )
620
664
  {
621
664
    if( pcPic && recon.getCurrPic() != pcPic )
622
0
    {
623
0
      continue;
624
0
    }
625
626
664
    if( Picture* donePic = recon.waitForPrevDecompressedPic() )
627
0
    {
628
0
      finishPicture( donePic );
629
0
    }
630
664
  }
631
332
}
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
}