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