/src/vvdec/source/Lib/CommonLib/PicListManager.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 | | #include "PicListManager.h" |
44 | | |
45 | | #include "Picture.h" |
46 | | |
47 | | #ifndef DEBUG_PIC_ORDER |
48 | | # define DEBUG_PIC_ORDER 0 |
49 | | #endif |
50 | | #if DEBUG_PIC_ORDER |
51 | | # define IF_DEBUG_PIC_ORDER( ... ) __VA_ARGS__ |
52 | | #else |
53 | | # define IF_DEBUG_PIC_ORDER( ... ) |
54 | | #endif |
55 | | |
56 | | namespace vvdec |
57 | | { |
58 | | static std::ostream& operator<<( std::ostream& strm, PicList& picList ) |
59 | 0 | { |
60 | 0 | for( auto& p: picList ) |
61 | 0 | { |
62 | 0 | char stateC = ' '; |
63 | 0 | switch (p->progress) { |
64 | 0 | case Picture::init: stateC = ' '; break; |
65 | 0 | case Picture::parsing: stateC = 'p'; break; |
66 | 0 | case Picture::parsed: stateC = '.'; break; |
67 | 0 | case Picture::reconstructing: stateC = 'x'; break; |
68 | 0 | case Picture::reconstructed: |
69 | 0 | case Picture::finished: stateC = 'X'; |
70 | 0 | if( !p->neededForOutput ) |
71 | 0 | { |
72 | 0 | stateC = 'o'; |
73 | 0 | if( p->lockedByApplication ) stateC = 'L'; |
74 | 0 | else if( p->dpbReferenceMark ) stateC = 'R'; |
75 | 0 | } |
76 | 0 | } |
77 | 0 | strm << p->poc << stateC << ' '; |
78 | 0 | } |
79 | 0 |
|
80 | 0 | return strm; |
81 | 0 | } |
82 | | |
83 | | static std::ostream& operator<<( std::ostream& strm, PicListRange& picRange ) |
84 | 0 | { |
85 | 0 | for( auto& p: picRange ) |
86 | 0 | { |
87 | 0 | strm << p->poc << ( p->progress >= Picture::finished ? "x" : " " ) << " "; |
88 | 0 | } |
89 | 0 | return strm; |
90 | 0 | } |
91 | | |
92 | | static bool isIDR( NalUnitType type ) |
93 | 0 | { |
94 | 0 | return type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || type == NAL_UNIT_CODED_SLICE_IDR_N_LP; |
95 | 0 | } |
96 | | static bool isIDR( const Picture* pic ) |
97 | 0 | { |
98 | 0 | return isIDR( pic->eNalUnitType ); |
99 | 0 | } |
100 | | |
101 | | void PicListManager::deleteBuffers() |
102 | 0 | { |
103 | 0 | for( auto& pcPic: m_cPicList ) |
104 | 0 | { |
105 | 0 | pcPic->destroy(); |
106 | |
|
107 | 0 | delete pcPic; |
108 | 0 | pcPic = NULL; |
109 | 0 | } |
110 | 0 | m_cPicList.clear(); |
111 | 0 | } |
112 | | |
113 | | void PicListManager::create(int frameDelay, int decInstances, const UserAllocator& userAllocator ) |
114 | 0 | { |
115 | 0 | m_parseFrameDelay = frameDelay; |
116 | 0 | m_parallelDecInst = decInstances; |
117 | 0 | m_userAllocator = userAllocator; |
118 | 0 | } |
119 | | |
120 | | Picture* PicListManager::getNewPicBuffer( const SPS& sps, const PPS& pps, const uint32_t temporalLayer, const int layerId, const VPS* vps ) |
121 | 0 | { |
122 | 0 | CHECK_FATAL( m_parseFrameDelay < 0, "Parser frame delay is invalid" ); |
123 | |
|
124 | 0 | Picture* pcPic = nullptr; |
125 | 0 | const int iMaxRefPicNum = ( vps == nullptr || vps->m_numLayersInOls[vps->m_iTargetLayer] == 1 ) |
126 | 0 | ? sps.getMaxDecPicBuffering( temporalLayer ) + 1 |
127 | 0 | : vps->getMaxDecPicBuffering( temporalLayer ); // m_uiMaxDecPicBuffering has the space for the picture currently being decoded |
128 | 0 | const unsigned int picMargin = 16 + sps.getMaxCUWidth(); |
129 | |
|
130 | 0 | bool externAllocator = m_userAllocator.enabled; |
131 | 0 | if ( externAllocator ) |
132 | 0 | { |
133 | 0 | if (sps.getBitDepth() == 8 ) |
134 | 0 | { |
135 | 0 | externAllocator = false; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | UserAllocator* userAllocator = externAllocator ? &m_userAllocator : nullptr; |
139 | |
|
140 | 0 | if( m_cPicList.size() < (uint32_t)iMaxRefPicNum + m_parseFrameDelay ) |
141 | 0 | { |
142 | 0 | pcPic = new Picture(); |
143 | 0 | pcPic->create( sps.getChromaFormatIdc(), |
144 | 0 | Size( pps.getPicWidthInLumaSamples(), pps.getPicHeightInLumaSamples() ), |
145 | 0 | sps.getMaxCUWidth(), |
146 | 0 | picMargin, |
147 | 0 | layerId, |
148 | 0 | userAllocator ); |
149 | 0 | pcPic->createWrapAroundBuf( sps.getUseWrapAround(), sps.getMaxCUWidth() ); |
150 | 0 | m_cPicList.push_back( pcPic ); |
151 | |
|
152 | 0 | return pcPic; |
153 | 0 | } |
154 | | |
155 | 0 | for( PicList::iterator itPic = m_cPicList.begin(); itPic != m_cPicList.end(); ++itPic ) |
156 | 0 | { |
157 | 0 | Picture* pic = *itPic; |
158 | 0 | if( pic->progress < Picture::reconstructed || pic->stillReferenced || pic->dpbReferenceMark || pic->neededForOutput || pic->lockedByApplication ) |
159 | 0 | { |
160 | 0 | continue; |
161 | 0 | } |
162 | | |
163 | 0 | if ( externAllocator ) |
164 | 0 | { |
165 | 0 | pic->destroy(); |
166 | 0 | pic->create( sps.getChromaFormatIdc(), |
167 | 0 | Size( pps.getPicWidthInLumaSamples(), pps.getPicHeightInLumaSamples() ), |
168 | 0 | sps.getMaxCUWidth(), |
169 | 0 | picMargin, |
170 | 0 | layerId, |
171 | 0 | userAllocator ); |
172 | 0 | pic->createWrapAroundBuf( sps.getUseWrapAround(), sps.getMaxCUWidth() ); |
173 | 0 | pic->resetForUse( layerId ); |
174 | 0 | } |
175 | | |
176 | | // take the picture to be reused and move it to the end of the list |
177 | 0 | move_to_end( itPic, m_cPicList ); |
178 | |
|
179 | 0 | pcPic = pic; |
180 | |
|
181 | 0 | if ( externAllocator ) |
182 | 0 | { |
183 | 0 | return pcPic; |
184 | 0 | } |
185 | | |
186 | 0 | break; |
187 | 0 | } |
188 | | |
189 | 0 | if( !pcPic ) |
190 | 0 | { |
191 | | // There is no room for this picture, either because of faulty encoder or dropped NAL. Extend the buffer. |
192 | 0 | pcPic = new Picture(); |
193 | 0 | pcPic->create( sps.getChromaFormatIdc(), |
194 | 0 | Size( pps.getPicWidthInLumaSamples(), pps.getPicHeightInLumaSamples() ), |
195 | 0 | sps.getMaxCUWidth(), |
196 | 0 | picMargin, |
197 | 0 | layerId, |
198 | 0 | userAllocator ); |
199 | 0 | pcPic->createWrapAroundBuf( sps.getUseWrapAround(), sps.getMaxCUWidth() ); |
200 | 0 | m_cPicList.push_back( pcPic ); |
201 | |
|
202 | 0 | return pcPic; |
203 | 0 | } |
204 | | |
205 | 0 | if( pcPic->lumaSize() != Size( pps.getPicWidthInLumaSamples(), pps.getPicHeightInLumaSamples() ) |
206 | 0 | || pcPic->cs->pcv->maxCUWidth != sps.getMaxCUWidth() |
207 | 0 | || pcPic->cs->pcv->maxCUHeight != sps.getMaxCUHeight() |
208 | 0 | || pcPic->layerId != layerId |
209 | 0 | || pcPic->margin != picMargin |
210 | 0 | ) |
211 | 0 | { |
212 | 0 | pcPic->destroy(); |
213 | 0 | pcPic->create( sps.getChromaFormatIdc(), |
214 | 0 | Size( pps.getPicWidthInLumaSamples(), pps.getPicHeightInLumaSamples() ), |
215 | 0 | sps.getMaxCUWidth(), |
216 | 0 | picMargin, |
217 | 0 | layerId, |
218 | 0 | userAllocator ); |
219 | 0 | pcPic->createWrapAroundBuf( sps.getUseWrapAround(), sps.getMaxCUWidth() ); |
220 | 0 | } |
221 | |
|
222 | 0 | pcPic->resetForUse( layerId ); |
223 | |
|
224 | 0 | return pcPic; |
225 | 0 | } |
226 | | |
227 | | void PicListManager::markUnusedPicturesReusable() |
228 | 0 | { |
229 | | // collect all pictures, that are still referenced by an in-progress picture |
230 | 0 | m_allRefPics.clear(); |
231 | 0 | m_allRefPics.reserve( m_cPicList.size() ); |
232 | 0 | for( auto& pic: m_cPicList ) |
233 | 0 | { |
234 | 0 | if( pic->progress >= Picture::reconstructed ) |
235 | 0 | { |
236 | 0 | continue; |
237 | 0 | } |
238 | | |
239 | 0 | for( auto& s: pic->slices ) |
240 | 0 | { |
241 | 0 | for( auto l: { REF_PIC_LIST_0, REF_PIC_LIST_1 } ) |
242 | 0 | { |
243 | 0 | for( int iRefIdx = 0; iRefIdx < s->getNumRefIdx( l ); iRefIdx++ ) |
244 | 0 | { |
245 | 0 | const Picture* refPic = s->getRefPic( l, iRefIdx ); |
246 | 0 | m_allRefPics.insert( refPic ); |
247 | 0 | } |
248 | 0 | } |
249 | 0 | } |
250 | 0 | } |
251 | | |
252 | | // remove stillReferenced flag from all others |
253 | 0 | for( auto& pic: m_cPicList ) |
254 | 0 | { |
255 | 0 | if( pic->progress < Picture::finished // only unmark pictures up to the first unfinished pic |
256 | 0 | && !( pic->progress == Picture::reconstructed && pic->wasLost ) ) // unless that is not a filled-in lost picture |
257 | 0 | { |
258 | 0 | break; |
259 | 0 | } |
260 | | |
261 | 0 | if( pic->stillReferenced && !pic->dpbReferenceMark && m_allRefPics.count( pic ) == 0 ) |
262 | 0 | { |
263 | 0 | pic->stillReferenced = false; |
264 | 0 | pic->m_subPicRefBufs.clear(); |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | Picture* PicListManager::findClosestPic( int iLostPoc ) |
270 | 0 | { |
271 | 0 | int closestPoc = INT32_MAX; |
272 | 0 | Picture* closestPic = nullptr; |
273 | 0 | for( auto& rpcPic: m_cPicList ) |
274 | 0 | { |
275 | 0 | if( rpcPic->progress >= Picture::reconstructed && abs( rpcPic->getPOC() - iLostPoc ) < closestPoc |
276 | 0 | && abs( rpcPic->getPOC() - iLostPoc ) != 0 ) |
277 | 0 | { |
278 | 0 | closestPoc = abs( rpcPic->getPOC() - iLostPoc ); |
279 | 0 | closestPic = rpcPic; |
280 | 0 | } |
281 | 0 | } |
282 | 0 | return closestPic; |
283 | 0 | } |
284 | | |
285 | | Picture* PicListManager::getNextOutputPic( uint32_t numReorderPicsHighestTid, |
286 | | uint32_t maxDecPicBufferingHighestTid, |
287 | | bool bFlush ) |
288 | 0 | { |
289 | 0 | (void)maxDecPicBufferingHighestTid; // unused |
290 | |
|
291 | 0 | if( m_cPicList.empty() ) |
292 | 0 | { |
293 | 0 | return nullptr; |
294 | 0 | } |
295 | | |
296 | | // find picture range up to the first random access point |
297 | 0 | auto seqStart = m_cPicList.cbegin(); |
298 | 0 | auto seqEnd = m_cPicList.cend(); |
299 | |
|
300 | 0 | bool foundOutputPic = false; |
301 | 0 | for( auto itPic = seqStart; itPic != m_cPicList.cend(); ++itPic ) |
302 | 0 | { |
303 | 0 | if( !( *itPic )->neededForOutput && ( *itPic )->progress >= Picture::reconstructed ) |
304 | 0 | { |
305 | 0 | continue; |
306 | 0 | } |
307 | | |
308 | 0 | if( ( *itPic )->progress < Picture::finished ) |
309 | 0 | { |
310 | 0 | seqEnd = itPic; |
311 | 0 | break; |
312 | 0 | } |
313 | | |
314 | 0 | if( isIDR( *itPic ) && !(*itPic)->getMixedNaluTypesInPicFlag()) |
315 | 0 | { |
316 | 0 | if( !foundOutputPic ) // if there was no picture needed for output before the first RAP, |
317 | 0 | { // we begin the range at the RAP... |
318 | 0 | seqStart = itPic; |
319 | 0 | } |
320 | 0 | else // ...otherwise it ends at the RAP |
321 | 0 | { |
322 | 0 | seqEnd = itPic; |
323 | 0 | break; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | 0 | foundOutputPic |= (*itPic)->neededForOutput; |
328 | | |
329 | | // ignore pictures, that are not needed for output or referenced any more |
330 | 0 | if( !foundOutputPic && !(*itPic)->dpbReferenceMark ) |
331 | 0 | { |
332 | 0 | seqStart = itPic; |
333 | 0 | } |
334 | 0 | } |
335 | 0 | if( !foundOutputPic ) |
336 | 0 | { |
337 | 0 | return nullptr; |
338 | 0 | } |
339 | | |
340 | 0 | PicListRange picRange{ seqStart, seqEnd }; |
341 | |
|
342 | 0 | IF_DEBUG_PIC_ORDER( std::cout << "list: " << m_cPicList << std::flush ); |
343 | | // IF_DEBUG_PIC_ORDER( std::cout << std::endl << "range: " << picRange << std::flush ); |
344 | |
|
345 | 0 | if( m_tuneInDelay <= numReorderPicsHighestTid + m_parallelDecInst + 1 && !bFlush ) |
346 | 0 | { |
347 | 0 | IF_DEBUG_PIC_ORDER( std::cout << std::endl ); |
348 | 0 | ++m_tuneInDelay; |
349 | 0 | return nullptr; |
350 | 0 | } |
351 | | |
352 | | // when there is an IDR picture coming up, we can flush all pictures before that |
353 | 0 | if( seqEnd != m_cPicList.cend() && isIDR( *seqEnd ) && !(*seqEnd)->getMixedNaluTypesInPicFlag() ) |
354 | 0 | { |
355 | 0 | bFlush = true; |
356 | 0 | IF_DEBUG_PIC_ORDER( std::cout << " flush" ); |
357 | 0 | } |
358 | |
|
359 | 0 | unsigned numPicsNotYetDisplayed = 0; |
360 | 0 | unsigned dpbFullness = 0; |
361 | 0 | if( !bFlush ) |
362 | 0 | { |
363 | 0 | for( auto& pcPic: picRange ) |
364 | 0 | { |
365 | 0 | if( pcPic->neededForOutput && pcPic->progress >= Picture::finished ) |
366 | 0 | { |
367 | 0 | numPicsNotYetDisplayed++; |
368 | 0 | dpbFullness++; |
369 | 0 | } |
370 | 0 | else if( pcPic->dpbReferenceMark && pcPic->progress >= Picture::finished ) |
371 | 0 | { |
372 | 0 | dpbFullness++; |
373 | 0 | } |
374 | 0 | } |
375 | 0 | } |
376 | |
|
377 | 0 | IF_DEBUG_PIC_ORDER( std::cout << " " << numPicsNotYetDisplayed << '/' << numReorderPicsHighestTid << " "<< dpbFullness << '/' << maxDecPicBufferingHighestTid << " " ); |
378 | 0 | (void)dpbFullness; |
379 | |
|
380 | 0 | Picture * lowestPOCPic = nullptr; |
381 | 0 | if( numPicsNotYetDisplayed > numReorderPicsHighestTid + ( m_firstOutputPic ? MAX_OUT_OF_ORDER_PICS : 0 ) |
382 | | // || dpbFullness > maxDecPicBufferingHighestTid |
383 | 0 | || bFlush ) |
384 | 0 | { |
385 | |
|
386 | 0 | for( auto& pcPic: picRange ) |
387 | 0 | { |
388 | | //CHECK( pcPic->fieldPic, "Interlaced not suported" ); |
389 | |
|
390 | 0 | if( pcPic->neededForOutput && pcPic->progress >= Picture::finished && |
391 | 0 | ( lowestPOCPic==nullptr || pcPic->poc < lowestPOCPic->poc ) ) |
392 | 0 | { |
393 | 0 | lowestPOCPic = pcPic; |
394 | 0 | } |
395 | 0 | } |
396 | 0 | } |
397 | |
|
398 | 0 | if( lowestPOCPic ) |
399 | 0 | { |
400 | 0 | m_firstOutputPic = false; |
401 | 0 | lowestPOCPic->lockedByApplication = true; |
402 | 0 | lowestPOCPic->neededForOutput = false; |
403 | |
|
404 | 0 | IF_DEBUG_PIC_ORDER( std::cout << " ==> " << lowestPOCPic->poc ); |
405 | 0 | } |
406 | 0 | IF_DEBUG_PIC_ORDER( std::cout << std::endl ); |
407 | |
|
408 | 0 | return lowestPOCPic; |
409 | 0 | } |
410 | | |
411 | | void PicListManager::releasePicture( Picture* pic ) |
412 | 0 | { |
413 | 0 | if( pic ) |
414 | 0 | { |
415 | 0 | pic->lockedByApplication = false; |
416 | 0 | } |
417 | 0 | } |
418 | | |
419 | | } // namespace vvdec |