/src/vvenc/source/Lib/EncoderLib/GOPCfg.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) 2019-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC 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 | | |
44 | | /** \file GOPCfg.cpp |
45 | | \brief |
46 | | */ |
47 | | |
48 | | #include "GOPCfg.h" |
49 | | #include "EncStage.h" |
50 | | |
51 | | //! \ingroup EncoderLib |
52 | | //! \{ |
53 | | |
54 | | namespace vvenc { |
55 | | |
56 | | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
57 | | |
58 | | void GOPCfg::initGopList( int refreshType, bool poc0idr, int intraPeriod, int gopSize, int leadFrames, bool bPicReordering, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ], const vvencMCTF& mctfCfg, int firstPassMode, int minIntraDist ) |
59 | 0 | { |
60 | 0 | CHECK( gopSize < 1, "gop size has to be greater than 0" ); |
61 | |
|
62 | 0 | m_mctfCfg = &mctfCfg; |
63 | 0 | m_refreshType = refreshType; |
64 | 0 | m_picReordering = bPicReordering; |
65 | 0 | m_poc0idr = poc0idr; |
66 | 0 | m_fixIntraPeriod = intraPeriod; |
67 | 0 | m_firstPassMode = firstPassMode; |
68 | 0 | m_maxGopSize = gopSize; |
69 | 0 | m_defGopSize = m_fixIntraPeriod > 0 ? std::min( m_maxGopSize, m_fixIntraPeriod ) : m_maxGopSize; |
70 | |
|
71 | 0 | const int numGops = m_fixIntraPeriod > 0 ? m_fixIntraPeriod / m_defGopSize : 0; |
72 | 0 | const int remainSize = m_fixIntraPeriod > 0 ? m_fixIntraPeriod - ( m_defGopSize * numGops ) : 0; |
73 | 0 | const int minPrevPoc = xGetMinPoc( m_maxGopSize, cfgGopList ); |
74 | 0 | const int numDefault = ( ( -1 * minPrevPoc ) + m_maxGopSize - 1 ) / m_maxGopSize + 1; |
75 | 0 | CHECK( numDefault <= 0, "number of gop lists to be created is 0" ); |
76 | | |
77 | | // setup default gop lists |
78 | 0 | GOPEntryList* prevGopList = nullptr; |
79 | 0 | int pocOffset = 0; |
80 | 0 | m_defaultGopLists.resize( numDefault ); |
81 | 0 | for( int i = 0; i < numDefault; i++ ) |
82 | 0 | { |
83 | 0 | xCreateGopList( m_maxGopSize, m_defGopSize, pocOffset, cfgGopList, prevGopList, m_defaultGopLists[ i ] ); |
84 | 0 | prevGopList = &m_defaultGopLists[ i ]; |
85 | 0 | pocOffset += m_defGopSize; |
86 | 0 | } |
87 | 0 | if( remainSize ) |
88 | 0 | { |
89 | 0 | const int prevGopIdx = Clip3<int>( 0, (int)m_defaultGopLists.size() - 1, numGops - 1 ); |
90 | 0 | prevGopList = &m_defaultGopLists[ prevGopIdx ]; |
91 | 0 | pocOffset = numGops * m_defGopSize; |
92 | 0 | xCreateGopList( m_maxGopSize, remainSize, pocOffset, cfgGopList, prevGopList, m_remainGopList ); |
93 | 0 | } |
94 | | |
95 | | // set some defaults based on gop list layouts |
96 | 0 | xSetDefaultRPL ( m_defaultGopLists ); |
97 | 0 | xSetDBPConstraints( m_defaultGopLists ); |
98 | | // sanity check |
99 | 0 | for( int i = 0; i < (int)m_defaultGopLists.size() + 1; i++ ) |
100 | 0 | { |
101 | 0 | GOPEntryList& gopList = i < (int)m_defaultGopLists.size() ? m_defaultGopLists[ i ] : m_remainGopList; |
102 | 0 | if( ! xCheckDBPConstraints( gopList ) ) |
103 | 0 | { |
104 | 0 | THROW( "gop lists do not fullfill default DPB constraints" ); |
105 | 0 | } |
106 | 0 | } |
107 | | |
108 | 0 | CHECK( leadFrames < 0, "negative number of lead frames not supported" ); |
109 | | |
110 | | // start with first gop list |
111 | 0 | m_gopList = remainSize != 0 && !poc0idr ? &m_remainGopList : &m_defaultGopLists[ 0 ]; |
112 | 0 | m_nextListIdx = remainSize != 0 && !poc0idr ? 0 : std::min( 1, (int)m_defaultGopLists.size() - 1 ); |
113 | 0 | xCreatePocToGopIdx( *m_gopList, !m_poc0idr, m_pocToGopIdx ); |
114 | | |
115 | | // lets start with poc 0 |
116 | 0 | m_gopNum = 0; |
117 | 0 | m_nextPoc = -leadFrames; |
118 | 0 | m_pocOffset = 0; |
119 | 0 | m_cnOffset = 0; |
120 | 0 | m_numTillGop = poc0idr ? 0 : (int)m_gopList->size() - 1; |
121 | 0 | m_numTillIntra = poc0idr ? 0 : (int)m_gopList->size() - 1; |
122 | 0 | m_minIntraDist = minIntraDist; |
123 | 0 | m_lastIntraPOC = -1; |
124 | 0 | m_adjustNoLPcodingOrder = m_refreshType == VVENC_DRT_IDR_NO_RADL && m_fixIntraPeriod <= m_maxGopSize; |
125 | 0 | } |
126 | | |
127 | | void GOPCfg::getNextGopEntry( GOPEntry& gopEntry ) |
128 | 0 | { |
129 | | // check for lead frames |
130 | 0 | if( m_nextPoc < 0 ) |
131 | 0 | { |
132 | 0 | const int pocAdj = m_poc0idr ? 0 : 1; |
133 | 0 | const bool isStartOfIntra = m_fixIntraPeriod > 0 ? ( ( m_nextPoc + pocAdj ) % m_fixIntraPeriod == 0 ) : false; ; |
134 | | |
135 | | // try to estimate temporal layer 0 leading frames |
136 | 0 | bool isTl0 = false; |
137 | 0 | if( m_fixIntraPeriod == 1 || m_picReordering == false || m_defGopSize <= 1 || isStartOfIntra ) |
138 | 0 | { |
139 | | // for AI or LD every frame is temporal layer 0 |
140 | 0 | isTl0 = true; |
141 | 0 | } |
142 | 0 | else |
143 | 0 | { |
144 | | // dyadic gop with or without intra period |
145 | 0 | if( m_fixIntraPeriod > 0 ) |
146 | 0 | { |
147 | 0 | const int ipOffset = -m_fixIntraPeriod * ( ( m_nextPoc - ( m_fixIntraPeriod - 1 ) ) / m_fixIntraPeriod ); |
148 | 0 | const int pocInIp = m_nextPoc + ipOffset; |
149 | 0 | isTl0 = ( pocInIp + pocAdj ) % m_defGopSize == 0; |
150 | 0 | } |
151 | 0 | else |
152 | 0 | { |
153 | 0 | isTl0 = ( ( m_nextPoc + pocAdj ) % m_defGopSize ) == 0; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | // create some fake gop entry for leading frames |
158 | 0 | gopEntry.m_POC = m_nextPoc; |
159 | 0 | gopEntry.m_codingNum = m_nextPoc; |
160 | 0 | gopEntry.m_sliceType = isStartOfIntra ? 'I' : 'B'; |
161 | 0 | gopEntry.m_temporalId = isTl0 ? 0 : 1; |
162 | 0 | gopEntry.m_isStartOfIntra = isStartOfIntra; |
163 | 0 | gopEntry.m_isValid = true; |
164 | 0 | if( isStartOfIntra ) m_lastIntraPOC = m_nextPoc; |
165 | | |
166 | | // continue with next frame |
167 | 0 | m_nextPoc += 1; |
168 | 0 | return; |
169 | 0 | } |
170 | | |
171 | 0 | const int pocInGop = m_nextPoc - m_pocOffset; |
172 | 0 | const int gopId = m_pocToGopIdx[ pocInGop % (int)m_pocToGopIdx.size() ]; |
173 | 0 | const bool isLeading0 = m_poc0idr && m_nextPoc == 0 ? true : false; // for poc0idr, we have a leading poc 0 |
174 | | |
175 | 0 | gopEntry = (*m_gopList)[ gopId ]; |
176 | 0 | gopEntry.m_POC = m_nextPoc; |
177 | 0 | gopEntry.m_codingNum = isLeading0 ? 0 : m_cnOffset + gopId; |
178 | 0 | gopEntry.m_gopNum = m_gopNum; |
179 | 0 | if( m_nextPoc != 0 && m_adjustNoLPcodingOrder ) |
180 | 0 | { |
181 | 0 | xAdjustNoLPcodingOrder( gopEntry, gopId ); |
182 | 0 | } |
183 | 0 | gopEntry.m_isValid = true; |
184 | | |
185 | | // mark start of intra |
186 | 0 | if( m_numTillIntra == 0 ) |
187 | 0 | { |
188 | 0 | gopEntry.m_sliceType = 'I'; |
189 | 0 | gopEntry.m_isStartOfIntra = true; |
190 | 0 | gopEntry.m_temporalId = 0; |
191 | 0 | gopEntry.m_vtl = 0; |
192 | 0 | m_lastIntraPOC = m_nextPoc; |
193 | 0 | } |
194 | | |
195 | | // check for end of current gop |
196 | 0 | CHECK( m_numTillIntra == 0 && m_numTillGop != 0, "start of new intra period only at start of new gop expected" ); |
197 | 0 | if( m_numTillGop == 0 ) |
198 | 0 | { |
199 | 0 | const int prevGopSize = (int)m_gopList->size(); |
200 | | // check for start of new intra period |
201 | 0 | if( m_numTillIntra == 0 ) |
202 | 0 | { |
203 | 0 | m_gopList = &m_defaultGopLists[ 0 ]; |
204 | 0 | m_nextListIdx = std::min( 1, (int)m_defaultGopLists.size() - 1 ); |
205 | 0 | m_numTillIntra = m_fixIntraPeriod; |
206 | 0 | m_adjustNoLPcodingOrder = m_refreshType == VVENC_DRT_IDR_NO_RADL && m_fixIntraPeriod <= m_maxGopSize; |
207 | 0 | } |
208 | 0 | else |
209 | 0 | { |
210 | 0 | const int remainSize = m_numTillIntra > 0 ? std::min( m_defGopSize, m_numTillIntra ) : m_defGopSize; |
211 | 0 | if( remainSize == (int)m_defaultGopLists[ m_nextListIdx ].size() && prevGopSize == m_defGopSize ) |
212 | 0 | { |
213 | 0 | m_gopList = &m_defaultGopLists[ m_nextListIdx ]; |
214 | 0 | m_nextListIdx = std::min( m_nextListIdx + 1, (int)m_defaultGopLists.size() - 1 ); |
215 | 0 | } |
216 | 0 | else |
217 | 0 | { |
218 | 0 | CHECK( remainSize != (int)m_remainGopList.size() || prevGopSize != m_defGopSize, "remaining size does not match size of pre-calculated gop list" ); |
219 | 0 | m_gopList = &m_remainGopList; |
220 | 0 | } |
221 | | //if we have a full gop, we don't need to change the coding order... |
222 | 0 | m_adjustNoLPcodingOrder = m_refreshType == VVENC_DRT_IDR_NO_RADL && m_numTillIntra <= m_maxGopSize; |
223 | 0 | } |
224 | 0 | xCreatePocToGopIdx( *m_gopList, !m_poc0idr, m_pocToGopIdx ); |
225 | 0 | m_numTillGop = (int)m_gopList->size(); |
226 | 0 | m_cnOffset += isLeading0 ? 1 : prevGopSize; |
227 | 0 | if( ! isLeading0 ) |
228 | 0 | { |
229 | 0 | m_pocOffset += prevGopSize; |
230 | 0 | m_gopNum += 1; |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | | // continue with next pic |
235 | 0 | m_nextPoc += 1; |
236 | 0 | m_numTillGop -= 1; |
237 | 0 | if( m_numTillIntra > 0 ) |
238 | 0 | { |
239 | 0 | m_numTillIntra -= 1; |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | void GOPCfg::startIntraPeriod( GOPEntry& gopEntry ) |
244 | 0 | { |
245 | | // set gop entry |
246 | 0 | gopEntry.m_sliceType = 'I'; |
247 | 0 | gopEntry.m_isStartOfIntra = true; |
248 | 0 | gopEntry.m_isStartOfGop = true; |
249 | 0 | gopEntry.m_temporalId = 0; |
250 | 0 | gopEntry.m_vtl = 0; |
251 | 0 | m_lastIntraPOC = gopEntry.m_POC; |
252 | | |
253 | | // start with first gop list |
254 | 0 | m_gopList = &m_defaultGopLists[ 0 ]; |
255 | 0 | m_nextListIdx = std::min( 1, (int)m_defaultGopLists.size() - 1 ); |
256 | 0 | m_numTillIntra = m_fixIntraPeriod; |
257 | 0 | m_numTillGop = (int)m_gopList->size(); |
258 | |
|
259 | 0 | xCreatePocToGopIdx( *m_gopList, !m_poc0idr, m_pocToGopIdx ); |
260 | | |
261 | | // process current / first I picture |
262 | 0 | m_numTillGop -= 1; |
263 | 0 | if( m_numTillIntra > 0 ) |
264 | 0 | { |
265 | 0 | m_numTillIntra -= 1; |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | void GOPCfg::fixStartOfLastGop( GOPEntry& gopEntry ) |
270 | 0 | { |
271 | 0 | gopEntry.m_isStartOfGop = true; |
272 | 0 | if( ! m_poc0idr && gopEntry.m_gopNum == 0 && ! gopEntry.m_isStartOfIntra ) |
273 | 0 | { |
274 | 0 | gopEntry.m_isStartOfIntra = true; |
275 | 0 | gopEntry.m_sliceType = 'I'; |
276 | 0 | gopEntry.m_temporalId = 0; |
277 | 0 | gopEntry.m_vtl = 0; |
278 | 0 | m_lastIntraPOC = gopEntry.m_POC; |
279 | 0 | } |
280 | 0 | } |
281 | | |
282 | | void GOPCfg::getDefaultRPLLists( RPLList& rpl0, RPLList& rpl1 ) const |
283 | 0 | { |
284 | 0 | const int numRpl = (int)m_defaultRPLList.size(); |
285 | | // TODO (jb): check size + 1, fix getNumRPL() as well |
286 | 0 | rpl0.resize( numRpl + 1 ); |
287 | 0 | rpl1.resize( numRpl + 1 ); |
288 | 0 | for( int i = 0; i < numRpl; i++ ) |
289 | 0 | { |
290 | 0 | rpl0[ i ].initFromGopEntry( *m_defaultRPLList[ i ], 0 ); |
291 | 0 | rpl1[ i ].initFromGopEntry( *m_defaultRPLList[ i ], 1 ); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | bool GOPCfg::isSTAallowed( int poc ) const |
296 | 0 | { |
297 | 0 | int intraDistBack = poc - m_lastIntraPOC; |
298 | 0 | int intraDistForward = m_numTillIntra + 1; |
299 | | |
300 | | //if intraDistBack == 0 we have a regular I-Slice and there we need to do the STA analysis as well |
301 | 0 | return ( ( intraDistBack >= m_minIntraDist && intraDistForward >= m_minIntraDist ) || intraDistBack == 0 ); |
302 | 0 | } |
303 | | |
304 | | bool GOPCfg::hasNonZeroTemporalId() const |
305 | 0 | { |
306 | 0 | return m_maxTid > 0; |
307 | 0 | } |
308 | | |
309 | | bool GOPCfg::hasLeadingPictures() const |
310 | 0 | { |
311 | 0 | for( const auto& gopEntry : m_defaultGopLists.back() ) |
312 | 0 | { |
313 | 0 | if( ! gopEntry.m_useBckwdOnly ) |
314 | 0 | { |
315 | 0 | return true; |
316 | 0 | } |
317 | 0 | } |
318 | 0 | return false; |
319 | 0 | } |
320 | | |
321 | | bool GOPCfg::isChromaDeltaQPEnabled() const |
322 | 0 | { |
323 | 0 | for( const auto& gopEntry : m_defaultGopLists.back() ) |
324 | 0 | { |
325 | 0 | if( gopEntry.m_CbQPoffset || gopEntry.m_CrQPoffset ) |
326 | 0 | { |
327 | 0 | return true; |
328 | 0 | } |
329 | 0 | } |
330 | 0 | return false; |
331 | 0 | } |
332 | | |
333 | | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
334 | | |
335 | | int GOPCfg::xGetMinPoc( int maxGopSize, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ] ) const |
336 | 0 | { |
337 | 0 | int minPoc = 0; |
338 | 0 | for( int i = 0; i < maxGopSize; i++ ) |
339 | 0 | { |
340 | 0 | for( int l = 0; l < 2; l++ ) |
341 | 0 | { |
342 | 0 | for( int j = 0; j < cfgGopList[ i ].m_numRefPics[ l ]; j++ ) |
343 | 0 | { |
344 | 0 | const int refPoc = cfgGopList[ i ].m_POC - cfgGopList[ i ].m_deltaRefPics[ l ][ j ]; |
345 | 0 | if( refPoc < minPoc ) |
346 | 0 | minPoc = refPoc; |
347 | 0 | } |
348 | 0 | } |
349 | 0 | } |
350 | 0 | return minPoc; |
351 | 0 | } |
352 | | |
353 | | void GOPCfg::xCreateGopList( int maxGopSize, int gopSize, int pocOffset, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ], const GOPEntryList* prevGopList, GOPEntryList& gopList ) const |
354 | 0 | { |
355 | 0 | const bool bSkipPrev = prevGopList == nullptr || (int)prevGopList->size() < maxGopSize; |
356 | 0 | std::vector< std::pair<int, int> > prevGopRefs; |
357 | 0 | if( ! bSkipPrev ) |
358 | 0 | { |
359 | 0 | xGetPrevGopRefs( prevGopList, prevGopRefs ); |
360 | 0 | } |
361 | | |
362 | | // |
363 | | // copy given gop configuration |
364 | | // |
365 | |
|
366 | 0 | gopList.clear(); |
367 | 0 | gopList.resize( maxGopSize ); |
368 | 0 | for( int i = 0; i < maxGopSize; i++ ) |
369 | 0 | { |
370 | 0 | gopList[ i ].setDefaultGOPEntry(); |
371 | 0 | gopList[ i ].copyFromGopCfg( cfgGopList[ i ] ); |
372 | 0 | } |
373 | | |
374 | | // find max temporal id |
375 | 0 | const int maxTid = xGetMaxTid( gopList ); |
376 | | |
377 | | // prune gop list |
378 | 0 | CHECK( gopSize > maxGopSize, "only pruning of gop list supported" ); |
379 | 0 | if( gopSize < maxGopSize ) |
380 | 0 | { |
381 | 0 | xPruneGopList( gopSize, gopList ); |
382 | 0 | } |
383 | | |
384 | | // |
385 | | // fix prediction pattern |
386 | | // |
387 | | |
388 | | // use list of available entries for prediction |
389 | 0 | std::vector<GOPEntry*> availList; |
390 | 0 | availList.resize( gopSize + 1, nullptr ); |
391 | 0 | GOPEntry e1; |
392 | 0 | if( prevGopRefs.size() == 0 || prevGopRefs[ 0 ].first == 0 ) |
393 | 0 | { |
394 | | // insert leading poc 0 to be available |
395 | 0 | CHECK( prevGopRefs.size() && prevGopRefs[ 0 ].first == 0 && prevGopRefs[ 0 ].second != 0, "gop structure should start and end with temporalId 0" ); |
396 | 0 | e1.setDefaultGOPEntry(); |
397 | 0 | e1.m_POC = 0; |
398 | 0 | availList[ 0 ] = &e1; |
399 | | // remove leading poc 0 from prev |
400 | 0 | if( prevGopRefs.size() && prevGopRefs[ 0 ].first == 0 ) |
401 | 0 | { |
402 | 0 | for( int i = 0; i < (int)prevGopRefs.size() - 1; i++ ) |
403 | 0 | { |
404 | 0 | prevGopRefs[ i ] = prevGopRefs[ i + 1 ]; |
405 | 0 | } |
406 | 0 | prevGopRefs.pop_back(); |
407 | 0 | } |
408 | 0 | } |
409 | | |
410 | | // loop over gop list |
411 | 0 | std::vector<int> newFwd; |
412 | 0 | std::vector<int> newBckwd; |
413 | 0 | std::vector<int> availFwd; |
414 | 0 | std::vector<int> availBckwd; |
415 | 0 | newFwd.reserve( MAX_REF_PICS ); |
416 | 0 | newBckwd.reserve( MAX_REF_PICS ); |
417 | 0 | availFwd.reserve( MAX_REF_PICS ); |
418 | 0 | availBckwd.reserve( MAX_REF_PICS ); |
419 | 0 | for( int i = 0; i < gopList.size(); i++ ) |
420 | 0 | { |
421 | 0 | GOPEntry* gopEntry = &gopList[ i ]; |
422 | | |
423 | | // loop over both reference lists |
424 | 0 | for( int l = 0; l < 2; l++ ) |
425 | 0 | { |
426 | 0 | newFwd.clear(); |
427 | 0 | newBckwd.clear(); |
428 | 0 | availFwd.clear(); |
429 | 0 | availBckwd.clear(); |
430 | | |
431 | | // fill list of possible reference pictures in forward / backward direction |
432 | 0 | xAddRefPicsFwd( availFwd, gopEntry, availList ); |
433 | 0 | xAddRefPicsBckwd( availBckwd, gopEntry, availList ); |
434 | | // access to previous gop not used/allowed for non-ref pics at highest temporal layer |
435 | 0 | if( gopEntry->m_temporalId <= 1 || gopEntry->m_temporalId < maxTid ) |
436 | 0 | { |
437 | 0 | xAddRefPicsPrevGOP( availBckwd, gopEntry, prevGopRefs ); |
438 | 0 | } |
439 | | |
440 | | // check / fix all active references |
441 | 0 | for( int j = 0; j < gopEntry->m_numRefPicsActive[ l ]; j++ ) |
442 | 0 | { |
443 | 0 | const int delta = gopEntry->m_deltaRefPics[ l ][ j ]; |
444 | 0 | CHECK( delta == 0, "error in gop configuration: try to reference own picture" ); |
445 | | // start with forward or backward prediction |
446 | 0 | std::vector<int>& sameDir = delta < 0 ? availFwd : availBckwd; |
447 | 0 | std::vector<int>& invertDir = delta < 0 ? availBckwd : availFwd; |
448 | | // check if candidates are available in same direction list |
449 | 0 | if( sameDir.size() ) |
450 | 0 | { |
451 | | // try to find correct match |
452 | 0 | auto itr = find( sameDir.begin(), sameDir.end(), delta ); |
453 | 0 | if( itr == sameDir.end() ) |
454 | 0 | { |
455 | | // no match found, choose replacement |
456 | 0 | itr = sameDir.begin(); |
457 | 0 | } |
458 | | // use delta candidate and remove candidate to prevent doublets |
459 | 0 | std::vector<int>& newDst = *itr < 0 ? newFwd : newBckwd; |
460 | 0 | newDst.push_back( *itr ); |
461 | 0 | sameDir.erase( itr ); |
462 | 0 | } |
463 | 0 | else if( invertDir.size() ) |
464 | 0 | { |
465 | | // corret match not possible, we are inverting the prediction direction |
466 | 0 | auto itr = invertDir.begin(); |
467 | | // use delta candidate and remove candidate to prevent doublets |
468 | 0 | std::vector<int>& newDst = *itr < 0 ? newFwd : newBckwd; |
469 | 0 | newDst.push_back( *itr ); |
470 | 0 | invertDir.erase( itr ); |
471 | 0 | } |
472 | 0 | } |
473 | | |
474 | | // clear old delta list |
475 | 0 | std::memset( gopEntry->m_deltaRefPics[ l ], 0, sizeof( gopEntry->m_deltaRefPics[ l ] ) ); |
476 | | |
477 | | // ensure new delta lists are sorted |
478 | 0 | std::sort( newFwd.begin(), newFwd.end(), []( auto& a, auto& b ){ return a > b; } ); |
479 | 0 | std::sort( newBckwd.begin(), newBckwd.end(), []( auto& a, auto& b ){ return a < b; } ); |
480 | | |
481 | | // copy new delta poc list |
482 | 0 | CHECK( (int)newFwd.size() + (int)newBckwd.size() >= VVENC_MAX_NUM_REF_PICS, "number of delta ref pic entries exceeds array bounds" ); |
483 | 0 | gopEntry->m_numRefPics[ l ] = (int)newFwd.size() + (int)newBckwd.size(); |
484 | 0 | gopEntry->m_numRefPicsActive[ l ] = (int)newFwd.size() + (int)newBckwd.size(); |
485 | 0 | std::vector<int>& src1 = l == 0 ? newBckwd : newFwd; |
486 | 0 | std::vector<int>& src2 = l == 0 ? newFwd : newBckwd; |
487 | 0 | int dstIdx = 0; |
488 | 0 | for( int j = 0; j < (int)src1.size(); j++ ) |
489 | 0 | { |
490 | 0 | gopEntry->m_deltaRefPics[ l ][ dstIdx ] = src1[ j ]; |
491 | 0 | dstIdx += 1; |
492 | 0 | } |
493 | 0 | for( int j = 0; j < (int)src2.size(); j++ ) |
494 | 0 | { |
495 | 0 | gopEntry->m_deltaRefPics[ l ][ dstIdx ] = src2[ j ]; |
496 | 0 | dstIdx += 1; |
497 | 0 | } |
498 | 0 | } |
499 | | |
500 | | // available for later use as ref |
501 | 0 | availList[ gopEntry->m_POC ] = gopEntry; |
502 | 0 | } |
503 | | |
504 | | // |
505 | | // add entries to ref lists, which are required as reference for later pictures in coding order |
506 | | // |
507 | | |
508 | | // create fake entry as start of potential next gop, |
509 | | // to ensure pictures referenced from next gop are keept alive |
510 | 0 | GOPEntry e2; |
511 | 0 | e2.setDefaultGOPEntry(); |
512 | 0 | e2.m_POC = 2 * maxGopSize; |
513 | 0 | e2.m_temporalId = 0; |
514 | 0 | const bool bIsLast = m_maxGopSize != gopSize; |
515 | 0 | if( ! bIsLast ) |
516 | 0 | { |
517 | 0 | std::vector<int> pocList; |
518 | 0 | pocList.reserve( gopSize ); |
519 | 0 | xGetRefsOfNextGop( gopSize, cfgGopList, pocOffset, pocList ); |
520 | 0 | e2.m_numRefPics[ 0 ] = (int)pocList.size(); |
521 | 0 | for( int i = 0; i < (int)pocList.size(); i++ ) |
522 | 0 | { |
523 | 0 | const int reqPoc = pocList[ i ] + maxGopSize; // shift poc from next to current gop |
524 | 0 | e2.m_deltaRefPics[ 0 ][ i ] = e2.m_POC - reqPoc; |
525 | 0 | } |
526 | 0 | } |
527 | 0 | const GOPEntry* prevEntry = &e2; |
528 | | |
529 | | // traverse gop list from back to front |
530 | 0 | for( int i = gopSize - 1; i >= 0; i-- ) |
531 | 0 | { |
532 | 0 | GOPEntry* gopEntry = &gopList[ i ]; |
533 | | // loop over all ref lists |
534 | 0 | for( int j = 0; j < 2; j++ ) |
535 | 0 | { |
536 | | // loop over all delta POCs in previous entry |
537 | 0 | for( int k = 0; k < prevEntry->m_numRefPics[ j ]; k++ ) |
538 | 0 | { |
539 | 0 | const int reqPoc = prevEntry->m_POC - prevEntry->m_deltaRefPics[ j ][ k ]; |
540 | 0 | if( reqPoc == gopEntry->m_POC ) |
541 | 0 | { |
542 | 0 | continue; |
543 | 0 | } |
544 | 0 | bool bAvail = false; |
545 | | // search if required poc is also part of the ref lists of current gop entry |
546 | 0 | for( int l = 0; l < 2; l++ ) |
547 | 0 | { |
548 | 0 | for( int m = 0; m < gopEntry->m_numRefPics[ l ]; m++ ) |
549 | 0 | { |
550 | 0 | const int availPoc = gopEntry->m_POC - gopEntry->m_deltaRefPics[ l ][ m ]; |
551 | 0 | if( reqPoc == availPoc ) |
552 | 0 | { |
553 | 0 | bAvail = true; |
554 | 0 | break; |
555 | 0 | } |
556 | 0 | } |
557 | 0 | if( bAvail ) |
558 | 0 | { |
559 | 0 | break; |
560 | 0 | } |
561 | 0 | } |
562 | | // if required poc not found, add this poc to current entry lists |
563 | 0 | if( ! bAvail ) |
564 | 0 | { |
565 | 0 | const int delta = gopEntry->m_POC - reqPoc; |
566 | 0 | const int addList = delta > 0 ? 0 : 1; // add to backward L0 or forward L1 list |
567 | 0 | CHECK( gopEntry->m_numRefPics[ addList ] >= VVENC_MAX_NUM_REF_PICS, "out of array bounds" ); |
568 | 0 | gopEntry->m_deltaRefPics[ addList ][ gopEntry->m_numRefPics[ addList ] ] = delta; |
569 | 0 | gopEntry->m_numRefPics[ addList ] += 1; |
570 | | // small optimization: sort additional poc by value to decrease required bits for encoding the list |
571 | 0 | const int sign = delta > 0 ? 1 : -1; |
572 | 0 | for( int n = gopEntry->m_numRefPics[ addList ] - 1; n > gopEntry->m_numRefPicsActive[ addList ]; n-- ) |
573 | 0 | { |
574 | 0 | if( gopEntry->m_deltaRefPics[ addList ][ n ] * sign > gopEntry->m_deltaRefPics[ addList ][ n - 1 ] * sign ) |
575 | 0 | break; |
576 | 0 | std::swap( gopEntry->m_deltaRefPics[ addList ][ n ], gopEntry->m_deltaRefPics[ addList ][ n - 1 ] ); |
577 | 0 | } |
578 | 0 | } |
579 | 0 | } |
580 | 0 | } |
581 | 0 | prevEntry = gopEntry; |
582 | 0 | } |
583 | | |
584 | | // poc to gop map for fastwr access |
585 | 0 | std::vector<int> pocToGopIdx; |
586 | 0 | xCreatePocToGopIdx( gopList, false, pocToGopIdx ); |
587 | | |
588 | | // mark first gop entry |
589 | 0 | gopList[ pocToGopIdx[ 0 ] ].m_isStartOfGop = true; |
590 | | |
591 | | // STSA, forward B flags, MCTF index, virtual TLayer |
592 | 0 | xSetSTSA ( gopList, pocToGopIdx ); |
593 | 0 | xSetBckwdOnly( gopList ); |
594 | 0 | xSetMctfIndex( maxGopSize, gopList ); |
595 | 0 | xSetVTL ( gopList ); |
596 | 0 | if( m_firstPassMode == 2 || m_firstPassMode == 4 ) |
597 | 0 | { |
598 | 0 | xSetSkipFirstPass( gopList ); |
599 | 0 | } |
600 | 0 | } |
601 | | |
602 | | void GOPCfg::xGetPrevGopRefs( const GOPEntryList* prevGopList, std::vector< std::pair<int, int> >& prevGopRefs ) const |
603 | 0 | { |
604 | 0 | if( ! prevGopList ) |
605 | 0 | { |
606 | 0 | return; |
607 | 0 | } |
608 | | |
609 | 0 | prevGopRefs.reserve( MAX_REF_PICS ); |
610 | |
|
611 | 0 | const int gopSize = (int)prevGopList->size(); |
612 | 0 | const GOPEntry& lastEntry = prevGopList->back(); |
613 | | |
614 | | // last encoded picture available for reference |
615 | 0 | prevGopRefs.push_back( std::make_pair( lastEntry.m_POC, lastEntry.m_temporalId ) ); |
616 | | |
617 | | // insert all available reference pictures |
618 | 0 | for( int l = 0; l < 2; l++ ) |
619 | 0 | { |
620 | 0 | for( int i = 0; i < lastEntry.m_numRefPics[ l ]; i++ ) |
621 | 0 | { |
622 | | // check referenced poc already found |
623 | 0 | const int refPoc = lastEntry.m_POC - lastEntry.m_deltaRefPics[ l ][ i ]; |
624 | 0 | bool bFound = false; |
625 | 0 | for( int j = 0; j < (int)prevGopRefs.size(); j++ ) |
626 | 0 | { |
627 | 0 | if( prevGopRefs[ j ].first == refPoc ) |
628 | 0 | { |
629 | 0 | bFound = true; |
630 | 0 | break; |
631 | 0 | } |
632 | 0 | } |
633 | 0 | if( bFound ) |
634 | 0 | { |
635 | 0 | continue; |
636 | 0 | } |
637 | | // find temporal layer of referenced poc |
638 | 0 | int refTid = -1; |
639 | 0 | int cmpPoc = refPoc; |
640 | 0 | if( cmpPoc < 0 ) |
641 | 0 | { |
642 | | // ensure value positive |
643 | 0 | const int numGops = -1 * cmpPoc / gopSize + 1; |
644 | 0 | cmpPoc += numGops * gopSize; |
645 | 0 | } |
646 | 0 | cmpPoc = cmpPoc % gopSize; |
647 | 0 | for( int j = 0; j < gopSize; j++ ) |
648 | 0 | { |
649 | 0 | if( ( (*prevGopList)[ j ].m_POC % gopSize ) == cmpPoc ) |
650 | 0 | { |
651 | 0 | refTid = (*prevGopList)[ j ].m_temporalId; |
652 | 0 | break; |
653 | 0 | } |
654 | 0 | } |
655 | 0 | CHECK( refTid < 0, "error in gop configuration: gop entry not found or temporalId negative" ); |
656 | | // store referenced poc |
657 | 0 | prevGopRefs.push_back( std::make_pair( refPoc, refTid ) ); |
658 | 0 | } |
659 | 0 | } |
660 | | |
661 | | // correct poc of referenced pictures by previous gop size |
662 | 0 | for( int i = 0; i < (int)prevGopRefs.size(); i++ ) |
663 | 0 | { |
664 | 0 | prevGopRefs[ i ].first -= gopSize; |
665 | 0 | CHECK( prevGopRefs[ i ].first > 0, "error in gop configuration: reference picture points to future gop" ); |
666 | 0 | } |
667 | | |
668 | 0 | std::sort( prevGopRefs.begin(), prevGopRefs.end(), []( auto& a, auto& b ){ return a.first > b.first; } ); |
669 | 0 | } |
670 | | |
671 | | void GOPCfg::xPruneGopList( int gopSize, GOPEntryList& gopList ) const |
672 | 0 | { |
673 | 0 | const int oldSize = (int)gopList.size(); |
674 | 0 | CHECK( oldSize <= gopSize, "gop list to short for prunning" ); |
675 | |
|
676 | 0 | std::vector<GOPEntry> prunedList; |
677 | 0 | prunedList.reserve( gopSize ); |
678 | 0 | for( int i = 0; i < (int)gopList.size(); i++ ) |
679 | 0 | { |
680 | 0 | if( gopList[ i ].m_POC == oldSize ) |
681 | 0 | { |
682 | | // always include end of old gop, which is the start of following gop |
683 | 0 | prunedList.push_back( gopList[ i ] ); |
684 | | // fix poc and delta lists of new end of pruned gop |
685 | 0 | GOPEntry& gopEntry = prunedList.back(); |
686 | 0 | const int sizeDiff = oldSize - gopSize; |
687 | 0 | for( int l = 0; l < 2; l++ ) |
688 | 0 | { |
689 | 0 | for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j++ ) |
690 | 0 | { |
691 | 0 | if( gopEntry.m_deltaRefPics[ l ][ j ] > sizeDiff ) |
692 | 0 | gopEntry.m_deltaRefPics[ l ][ j ] -= sizeDiff; |
693 | 0 | else |
694 | 0 | gopEntry.m_deltaRefPics[ l ][ j ] = gopSize; // ref points inside removed pics, lets point to start of gop (poc 0) as default |
695 | 0 | } |
696 | 0 | } |
697 | 0 | gopEntry.m_POC -= sizeDiff; |
698 | 0 | } |
699 | 0 | else if( gopList[ i ].m_POC < gopSize ) |
700 | 0 | { |
701 | 0 | prunedList.push_back( gopList[ i ] ); |
702 | 0 | } |
703 | 0 | } |
704 | |
|
705 | 0 | gopList.clear(); |
706 | 0 | gopList.resize( (int)prunedList.size() ); |
707 | 0 | for( int i = 0; i < (int)prunedList.size(); i++ ) |
708 | 0 | { |
709 | 0 | gopList[ i ] = prunedList[ i ]; |
710 | 0 | } |
711 | |
|
712 | 0 | CHECK( gopList.size() != gopSize, "pruned gop list incomplete" ); |
713 | 0 | } |
714 | | |
715 | | void GOPCfg::xGetRefsOfNextGop( int gopSize, const vvencGOPEntry cfgGopList[ VVENC_MAX_GOP ], int pocOffset, std::vector<int>& pocList ) const |
716 | 0 | { |
717 | 0 | const int minPoc = -1 * ( pocOffset + gopSize ); |
718 | 0 | for( int i = 0; i < gopSize; i++ ) |
719 | 0 | { |
720 | 0 | for( int l = 0; l < 2; l++ ) |
721 | 0 | { |
722 | 0 | for( int j = 0; j < cfgGopList[ i ].m_numRefPics[ l ]; j++ ) |
723 | 0 | { |
724 | 0 | const int refPoc = cfgGopList[ i ].m_POC - cfgGopList[ i ].m_deltaRefPics[ l ][ j ]; |
725 | 0 | if( refPoc >= minPoc && refPoc < 0 ) |
726 | 0 | { |
727 | 0 | auto itr = find( pocList.begin(), pocList.end(), refPoc ); |
728 | 0 | if( itr == pocList.end() ) |
729 | 0 | pocList.push_back( refPoc ); |
730 | 0 | } |
731 | 0 | } |
732 | 0 | } |
733 | 0 | } |
734 | 0 | std::sort( pocList.begin(), pocList.end() ); |
735 | 0 | } |
736 | | |
737 | | void GOPCfg::xSetMctfIndex( int maxGopSize, GOPEntryList& gopList ) const |
738 | 0 | { |
739 | 0 | for( auto& gopEntry : gopList ) |
740 | 0 | { |
741 | 0 | int poc = gopEntry.m_POC; |
742 | | // for pruned gops max poc is start / end of gop and should be filtered, but the used poc has been changed |
743 | | // to pruned gop size and does not fit the original config anymore => set back original max gop size poc |
744 | 0 | if( (int)gopList.size() < maxGopSize && poc == (int)gopList.size() ) |
745 | 0 | { |
746 | 0 | poc = maxGopSize; |
747 | 0 | } |
748 | | // find mctf index |
749 | 0 | for( int i = m_mctfCfg->numFrames - 1; i >= 0; i-- ) |
750 | 0 | { |
751 | 0 | if( ( poc % m_mctfCfg->MCTFFrames[ i ] ) == 0 ) |
752 | 0 | { |
753 | 0 | gopEntry.m_mctfIndex = i; |
754 | 0 | break; |
755 | 0 | } |
756 | 0 | } |
757 | 0 | } |
758 | 0 | } |
759 | | |
760 | | void GOPCfg::xSetSkipFirstPass( GOPEntryList& gopList ) const |
761 | 0 | { |
762 | | // find max temporal id |
763 | 0 | const int maxTid = xGetMaxTid( gopList ); |
764 | | |
765 | | // skip first pass encoding after first frame at max temporal id has been encoded |
766 | 0 | if( maxTid > 1 ) |
767 | 0 | { |
768 | 0 | bool bSkipRem = false; |
769 | 0 | for( auto& gopEntry : gopList ) |
770 | 0 | { |
771 | 0 | gopEntry.m_skipFirstPass = bSkipRem; |
772 | 0 | bSkipRem |= gopEntry.m_temporalId == maxTid; |
773 | 0 | } |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | | void GOPCfg::xCreatePocToGopIdx( const GOPEntryList& gopList, bool bShift, std::vector<int>& pocToGopIdx ) const |
778 | 0 | { |
779 | 0 | const int gopSize = (int)gopList.size(); |
780 | 0 | const int pocOffset = bShift ? -1 : 0; |
781 | |
|
782 | 0 | pocToGopIdx.clear(); |
783 | 0 | pocToGopIdx.resize( gopSize, -1 ); |
784 | |
|
785 | 0 | for( int i = 0; i < gopSize; i++ ) |
786 | 0 | { |
787 | 0 | const GOPEntry& gopEntry = gopList[ i ]; |
788 | 0 | const int poc = ( gopEntry.m_POC + pocOffset ) % gopSize; |
789 | 0 | CHECK( gopEntry.m_POC > gopSize || gopEntry.m_POC < 1, "error: poc out of range" ); |
790 | 0 | CHECK( pocToGopIdx[ poc ] != -1, "error: multiple entries in gop list map to same poc" ); |
791 | 0 | pocToGopIdx[ poc ] = i; |
792 | 0 | } |
793 | 0 | for( int i = 0; i < gopSize; i++ ) |
794 | 0 | { |
795 | 0 | CHECK( pocToGopIdx[ i ] < 0, "error: poc not found in gop list" ); |
796 | 0 | } |
797 | 0 | } |
798 | | |
799 | | void GOPCfg::xSetSTSA( GOPEntryList& gopList, const std::vector<int>& pocToGopIdx ) const |
800 | 0 | { |
801 | | // check STSA condition satisfied for each GOPEntry |
802 | 0 | std::vector<bool> isLocalSTSA; |
803 | 0 | isLocalSTSA.resize( gopList.size(), true ); |
804 | 0 | for( int i = 0; i < (int)gopList.size(); i++ ) |
805 | 0 | { |
806 | 0 | const GOPEntry& gopEntry = gopList[ i ]; |
807 | | // check all ref pics have higher temporal level |
808 | 0 | for( int l = 0; l < 2 && isLocalSTSA[ i ]; l++ ) |
809 | 0 | { |
810 | 0 | for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ ) |
811 | 0 | { |
812 | 0 | const int refPoc = gopEntry.m_POC - gopEntry.m_deltaRefPics[ l ][ j ]; |
813 | 0 | if( refPoc < 0 ) |
814 | 0 | { |
815 | 0 | continue; |
816 | 0 | } |
817 | 0 | const int gopId = pocToGopIdx[ refPoc % (int)pocToGopIdx.size() ]; |
818 | 0 | const GOPEntry& refEntry = gopList[ gopId ]; |
819 | 0 | if( refEntry.m_temporalId >= gopEntry.m_temporalId ) |
820 | 0 | { |
821 | 0 | isLocalSTSA[ i ] = false; |
822 | 0 | break; |
823 | 0 | } |
824 | 0 | } |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | | // accumulate STSA condition for all pics of same temporal level |
829 | 0 | const int maxTid = xGetMaxTid( gopList ); |
830 | 0 | std::vector<bool> isTlSTSA; |
831 | 0 | isTlSTSA.resize( maxTid + 1, true ); |
832 | 0 | for( int i = 0; i < (int)gopList.size(); i++ ) |
833 | 0 | { |
834 | 0 | const GOPEntry& gopEntry = gopList[ i ]; |
835 | 0 | const int tid = gopEntry.m_temporalId; |
836 | 0 | const bool isSTSA = isTlSTSA[ tid ] && isLocalSTSA[ i ]; |
837 | 0 | isTlSTSA[ tid ] = isSTSA; |
838 | 0 | } |
839 | | |
840 | | // set STSA flag for each GOPEntry |
841 | 0 | for( auto& gopEntry : gopList ) |
842 | 0 | { |
843 | 0 | const int tid = gopEntry.m_temporalId; |
844 | 0 | gopEntry.m_isSTSA = isTlSTSA[ tid ]; |
845 | 0 | } |
846 | 0 | } |
847 | | |
848 | | void GOPCfg::xSetBckwdOnly( GOPEntryList& gopList ) const |
849 | 0 | { |
850 | 0 | for( auto& gopEntry : gopList ) |
851 | 0 | { |
852 | 0 | bool useBckwdOnly = true; |
853 | 0 | for( int l = 0; l < 2 && useBckwdOnly; l++ ) |
854 | 0 | { |
855 | 0 | for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ ) |
856 | 0 | { |
857 | 0 | if( gopEntry.m_deltaRefPics[ l ][ j ] < 0 ) |
858 | 0 | { |
859 | 0 | useBckwdOnly = false; |
860 | 0 | break; |
861 | 0 | } |
862 | 0 | } |
863 | 0 | } |
864 | 0 | gopEntry.m_useBckwdOnly = useBckwdOnly; |
865 | 0 | } |
866 | 0 | } |
867 | | |
868 | | void GOPCfg::xSetVTL( GOPEntryList& gopList ) const |
869 | 0 | { |
870 | 0 | if( m_picReordering == 0 ) |
871 | 0 | { |
872 | 0 | const int vtl = std::min( m_maxGopSize >> 2, 3 ); |
873 | 0 | for( auto& gopEntry : gopList ) |
874 | 0 | { |
875 | 0 | CHECK( gopEntry.m_temporalId != 0, "unexpected low delay temporal id" ); |
876 | 0 | if( !gopEntry.m_isStartOfGop && !gopEntry.m_isStartOfIntra ) |
877 | 0 | { |
878 | 0 | gopEntry.m_vtl = vtl; |
879 | 0 | } |
880 | 0 | } |
881 | 0 | } |
882 | 0 | else |
883 | 0 | { |
884 | 0 | for( auto& gopEntry : gopList ) |
885 | 0 | { |
886 | 0 | gopEntry.m_vtl = gopEntry.m_temporalId; |
887 | 0 | } |
888 | 0 | } |
889 | 0 | } |
890 | | |
891 | | void GOPCfg::xSetDefaultRPL( std::vector<GOPEntryList>& defaultLists ) |
892 | 0 | { |
893 | 0 | m_defaultRPLList.clear(); |
894 | | |
895 | | // default RPL index is entry position in gop |
896 | 0 | GOPEntryList& gopList = defaultLists.back(); |
897 | 0 | for( auto& gopEntry : gopList ) |
898 | 0 | { |
899 | 0 | gopEntry.m_defaultRPLIdx = (int)m_defaultRPLList.size(); |
900 | 0 | m_defaultRPLList.push_back( &gopEntry ); |
901 | 0 | } |
902 | | |
903 | | // in prev lists set extra index if RPL list deviates from default list |
904 | 0 | for( int i = 0; i < (int)defaultLists.size() - 1; i++ ) |
905 | 0 | { |
906 | 0 | GOPEntryList& prevList = defaultLists[ i ]; |
907 | 0 | CHECK( gopList.size() != prevList.size(), "default gop list and first gop list have to have same size" ); |
908 | 0 | for( int i = 0; i < (int)prevList.size(); i++ ) |
909 | 0 | { |
910 | 0 | CHECK( prevList[ i ].m_POC != gopList[ i ].m_POC, "default gop list and first gop List have to have same poc layout" ); |
911 | 0 | GOPEntry& gopEntry = prevList[ i ]; |
912 | 0 | GOPEntry& cmpEntry = gopList[ i ]; |
913 | 0 | bool haveSameList = true; |
914 | 0 | for( int l = 0; l < 2 && haveSameList; l++ ) |
915 | 0 | { |
916 | 0 | if( gopEntry.m_numRefPics[ l ] != cmpEntry.m_numRefPics[ l ] ) |
917 | 0 | { |
918 | 0 | haveSameList = false; |
919 | 0 | break; |
920 | 0 | } |
921 | 0 | for( int j = 0; j < gopEntry.m_numRefPics[ l ]; j ++ ) |
922 | 0 | { |
923 | 0 | if( gopEntry.m_deltaRefPics[ l ][ j ] != cmpEntry.m_deltaRefPics[ l ][ j ] ) |
924 | 0 | { |
925 | 0 | haveSameList = false; |
926 | 0 | break; |
927 | 0 | } |
928 | 0 | } |
929 | 0 | } |
930 | 0 | if( haveSameList ) |
931 | 0 | { |
932 | 0 | gopEntry.m_defaultRPLIdx = cmpEntry.m_defaultRPLIdx; |
933 | 0 | } |
934 | 0 | else |
935 | 0 | { |
936 | 0 | gopEntry.m_defaultRPLIdx = (int)m_defaultRPLList.size(); |
937 | 0 | m_defaultRPLList.push_back( &gopEntry ); |
938 | 0 | } |
939 | 0 | } |
940 | 0 | } |
941 | | |
942 | 0 | std::vector<int> hist[ 2 ]; |
943 | 0 | hist[ 0 ].resize( MAX_NUM_REF, 0 ); |
944 | 0 | hist[ 1 ].resize( MAX_NUM_REF, 0 ); |
945 | 0 | for( auto gopEntry : m_defaultRPLList ) |
946 | 0 | { |
947 | 0 | for( int l = 0; l < 2; l++ ) |
948 | 0 | { |
949 | 0 | CHECK( gopEntry->m_numRefPicsActive[ l ] < 0 || gopEntry->m_numRefPicsActive[ l ] >= MAX_NUM_REF, "array index out of bounds"); |
950 | 0 | hist[ l ][ gopEntry->m_numRefPicsActive[ l ] ] += 1; |
951 | 0 | } |
952 | 0 | } |
953 | 0 | for( int l = 0; l < 2; l++ ) |
954 | 0 | { |
955 | 0 | int bestIdx = 0; |
956 | 0 | int maxVal = -1; |
957 | 0 | for( int j = 0; j < (int)hist[ l ].size(); j++ ) |
958 | 0 | { |
959 | 0 | if( hist[ l ][ j ] > maxVal ) |
960 | 0 | { |
961 | 0 | bestIdx = j; |
962 | 0 | maxVal = hist[ l ][ j ]; |
963 | 0 | } |
964 | 0 | } |
965 | 0 | m_defaultNumActive[ l ] = bestIdx; |
966 | 0 | } |
967 | 0 | } |
968 | | |
969 | | void GOPCfg::xSetDBPConstraints( std::vector<GOPEntryList>& defaultLists ) |
970 | 0 | { |
971 | 0 | const GOPEntryList& gopList = defaultLists.back(); |
972 | |
|
973 | 0 | m_maxTid = xGetMaxTid( gopList ); |
974 | |
|
975 | 0 | m_maxDecPicBuffering.resize( m_maxTid + 1, 1 ); |
976 | 0 | m_numReorderPics.resize ( m_maxTid + 1, 0 ); |
977 | | |
978 | | // max DPB size and number reorder pics per temporal level |
979 | 0 | for( const auto& gopEntry : gopList ) |
980 | 0 | { |
981 | 0 | const int numRefPics = xGetMaxRefPics ( gopEntry ); |
982 | 0 | const int numReorder = xGetMaxNumReorder( gopEntry, gopList ); |
983 | 0 | const int tid = gopEntry.m_temporalId; |
984 | 0 | m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], numRefPics + 1 ); |
985 | 0 | m_numReorderPics [ tid ] = std::max( m_numReorderPics[ tid ], numReorder ); |
986 | 0 | } |
987 | | |
988 | | // the value of num_reorder_pics[ i ] shall be in the range of 0 to max_dec_pic_buffering[ i ] - 1, inclusive |
989 | 0 | m_maxDecPicBuffering[ 0 ] = std::max( m_maxDecPicBuffering[ 0 ], m_numReorderPics[ 0 ] +1 ); |
990 | 0 | for( int tid = 1; tid < m_maxTid; tid++ ) |
991 | 0 | { |
992 | | // a lower layer can not have higher reorder value than a higher layer |
993 | 0 | m_numReorderPics[ tid ] = std::max( m_numReorderPics[ tid ], m_numReorderPics[ tid - 1 ] ); |
994 | | // the value of num_reorder_pics[ i ] shall be in the range of 0 to max_dec_pic_buffering[ i ] - 1, inclusive |
995 | 0 | m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], m_numReorderPics[ tid ] +1 ); |
996 | | // a lower layer can not have higher DPB size value than a higher layer |
997 | 0 | m_maxDecPicBuffering[ tid ] = std::max( m_maxDecPicBuffering[ tid ], m_maxDecPicBuffering[ tid - 1 ] ); |
998 | 0 | } |
999 | 0 | } |
1000 | | |
1001 | | bool GOPCfg::xCheckDBPConstraints( const GOPEntryList& gopList ) const |
1002 | 0 | { |
1003 | 0 | if( gopList.size() && gopList[ 0 ].m_temporalId != 0 ) |
1004 | 0 | { |
1005 | 0 | msg.log( VVENC_ERROR, "first entry in gop list must have temporal id 0" ); |
1006 | 0 | return false; |
1007 | 0 | } |
1008 | 0 | const int maxTid = xGetMaxTid( gopList ); |
1009 | 0 | if( maxTid > m_maxTid ) |
1010 | 0 | { |
1011 | 0 | msg.log( VVENC_ERROR, "max temporal level exceeds overall maximum value" ); |
1012 | 0 | return false; |
1013 | 0 | } |
1014 | 0 | for( const auto& gopEntry : gopList ) |
1015 | 0 | { |
1016 | 0 | const int numRefPics = xGetMaxRefPics ( gopEntry ); |
1017 | 0 | const int numReorder = xGetMaxNumReorder( gopEntry, gopList ); |
1018 | 0 | const int tid = gopEntry.m_temporalId; |
1019 | 0 | if( numRefPics + 1 > m_maxDecPicBuffering[ tid ] ) |
1020 | 0 | { |
1021 | 0 | msg.log( VVENC_ERROR, "max DPB size exceeds overall maximum value" ); |
1022 | 0 | return false; |
1023 | 0 | } |
1024 | 0 | if( numReorder > m_numReorderPics[ tid ] ) |
1025 | 0 | { |
1026 | 0 | msg.log( VVENC_ERROR, "max number reorder pics exceeds overall maximum value" ); |
1027 | 0 | return false; |
1028 | 0 | } |
1029 | 0 | } |
1030 | 0 | return true; |
1031 | 0 | } |
1032 | | |
1033 | | void GOPCfg::xAddRefPicsBckwd( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector<GOPEntry*>& availList ) const |
1034 | 0 | { |
1035 | 0 | for( int i = ( gopEntry->m_POC - 1 ); i >= 0; i-- ) |
1036 | 0 | { |
1037 | 0 | const GOPEntry* refEntry = availList[ i ]; |
1038 | 0 | if( refEntry ) |
1039 | 0 | { |
1040 | 0 | if( refEntry->m_temporalId <= gopEntry->m_temporalId ) |
1041 | 0 | { |
1042 | 0 | deltaList.push_back( gopEntry->m_POC - refEntry->m_POC ); |
1043 | 0 | CHECK( deltaList.back() <= 0, "error in backward list: try to access future ref" ); |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | } |
1047 | 0 | } |
1048 | | |
1049 | | void GOPCfg::xAddRefPicsFwd( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector<GOPEntry*>& availList ) const |
1050 | 0 | { |
1051 | 0 | for( int i = ( gopEntry->m_POC + 1 ); i < (int)availList.size(); i++ ) |
1052 | 0 | { |
1053 | 0 | const GOPEntry* refEntry = availList[ i ]; |
1054 | 0 | if( refEntry ) |
1055 | 0 | { |
1056 | 0 | if( refEntry->m_temporalId <= gopEntry->m_temporalId ) |
1057 | 0 | { |
1058 | 0 | deltaList.push_back( gopEntry->m_POC - refEntry->m_POC ); |
1059 | 0 | CHECK( deltaList.back() >= 0, "error in forward list: try to access past ref" ); |
1060 | 0 | } |
1061 | 0 | } |
1062 | 0 | } |
1063 | 0 | } |
1064 | | |
1065 | | void GOPCfg::xAddRefPicsPrevGOP( std::vector<int>& deltaList, const GOPEntry* gopEntry, const std::vector< std::pair<int, int> >& prevGopRefs ) const |
1066 | 0 | { |
1067 | 0 | for( int i = 0; i < (int)prevGopRefs.size(); i++ ) |
1068 | 0 | { |
1069 | 0 | if( prevGopRefs[ i ].second <= gopEntry->m_temporalId ) |
1070 | 0 | { |
1071 | 0 | deltaList.push_back( gopEntry->m_POC - prevGopRefs[ i ].first ); |
1072 | 0 | CHECK( deltaList.back() <= 0, "error in backward list: try to access future ref" ); |
1073 | 0 | } |
1074 | 0 | } |
1075 | 0 | } |
1076 | | |
1077 | | int GOPCfg::xGetMaxTid( const GOPEntryList& gopList ) const |
1078 | 0 | { |
1079 | 0 | int maxTid = 0; |
1080 | 0 | for( auto& gopEntry : gopList ) |
1081 | 0 | { |
1082 | 0 | if( gopEntry.m_temporalId > maxTid ) |
1083 | 0 | { |
1084 | 0 | maxTid = gopEntry.m_temporalId; |
1085 | 0 | } |
1086 | 0 | } |
1087 | 0 | return maxTid; |
1088 | 0 | } |
1089 | | |
1090 | | int GOPCfg::xGetMaxRefPics( const GOPEntry& gopEntry ) const |
1091 | 0 | { |
1092 | 0 | int numRefPics = gopEntry.m_numRefPics[ 0 ]; |
1093 | 0 | for( int j = 0; j < gopEntry.m_numRefPics[ 1 ]; j++ ) |
1094 | 0 | { |
1095 | 0 | bool inL0 = false; |
1096 | 0 | for( int k = 0; k < gopEntry.m_numRefPics[ 0 ]; k++ ) |
1097 | 0 | { |
1098 | 0 | if( gopEntry.m_deltaRefPics[ 1 ][ j ] == gopEntry.m_deltaRefPics[ 0 ][ k ] ) |
1099 | 0 | { |
1100 | 0 | inL0 = true; |
1101 | 0 | break; |
1102 | 0 | } |
1103 | 0 | } |
1104 | 0 | if( ! inL0 ) |
1105 | 0 | { |
1106 | 0 | numRefPics += 1; |
1107 | 0 | } |
1108 | 0 | } |
1109 | 0 | return numRefPics; |
1110 | 0 | } |
1111 | | |
1112 | | int GOPCfg::xGetMaxNumReorder( const GOPEntry& gopEntry, const GOPEntryList& gopList ) const |
1113 | 0 | { |
1114 | 0 | int maxCodingNum = 0; |
1115 | 0 | for( int j = 0; j < (int)gopList.size(); j++ ) |
1116 | 0 | { |
1117 | 0 | if( gopList[ j ].m_POC <= gopEntry.m_POC ) |
1118 | 0 | { |
1119 | 0 | maxCodingNum = j; |
1120 | 0 | } |
1121 | 0 | } |
1122 | 0 | int numReorder = 0; |
1123 | 0 | for( int j = 0; j < maxCodingNum; j++ ) |
1124 | 0 | { |
1125 | 0 | const GOPEntry& cmpEntry = gopList[ j ]; |
1126 | 0 | if( cmpEntry.m_POC > gopEntry.m_POC && cmpEntry.m_temporalId <= gopEntry.m_temporalId ) |
1127 | 0 | { |
1128 | 0 | numReorder += 1; |
1129 | 0 | } |
1130 | 0 | } |
1131 | 0 | return numReorder; |
1132 | 0 | } |
1133 | | |
1134 | | void GOPCfg::xAdjustNoLPcodingOrder( GOPEntry& gopEntry, const int orgGopId ) const |
1135 | 0 | { |
1136 | 0 | if( orgGopId == 0 ) |
1137 | 0 | { |
1138 | | //postpone the intra frame and put it in a new gop |
1139 | 0 | gopEntry.m_codingNum += (int)m_pocToGopIdx.size() - 1; |
1140 | 0 | gopEntry.m_gopNum += 1; |
1141 | 0 | } |
1142 | 0 | else |
1143 | 0 | { |
1144 | 0 | gopEntry.m_codingNum -= 1; |
1145 | 0 | if( orgGopId == 1 && m_fixIntraPeriod > m_maxGopSize ) |
1146 | 0 | { |
1147 | 0 | gopEntry.m_isStartOfGop = true; |
1148 | 0 | } |
1149 | 0 | } |
1150 | |
|
1151 | 0 | return; |
1152 | 0 | } |
1153 | | |
1154 | | } // namespace vvenc |
1155 | | |
1156 | | //! \} |
1157 | | |