/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 | | } |