1 // Copyright (c) 2017-2019 Intel Corporation
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in all
11 // copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 // SOFTWARE.
20
21 #include "umc_defs.h"
22 #ifdef MFX_ENABLE_H265_VIDEO_DECODE
23
24 #include "umc_h265_nal_spl.h"
25 #include "mfx_common.h" // for trace routines
26
27 namespace UMC_HEVC_DECODER
28 {
29 // NAL unit definitions
30 enum
31 {
32 NAL_UNITTYPE_SHIFT_H265 = 1,
33 NAL_UNITTYPE_BITS_H265 = 0x7e,
34 };
35
36 // Change memory region to little endian for reading with 32-bit DWORDs and remove start code emulation prevention byteps
37 void SwapMemoryAndRemovePreventingBytes_H265(void *pDestination, size_t &nDstSize, void *pSource, size_t nSrcSize, std::vector<uint32_t> *pRemovedOffsets);
38
39 // Search bitstream for start code
FindStartCode(const uint8_t * pb,size_t & nSize)40 static int32_t FindStartCode(const uint8_t *pb, size_t &nSize)
41 {
42 // there is no data
43 if ((int32_t) nSize < 4)
44 return -1;
45
46 // find start code
47 while ((4 <= nSize) && ((0 != pb[0]) ||
48 (0 != pb[1]) ||
49 (1 != pb[2])))
50 {
51 pb += 1;
52 nSize -= 1;
53 }
54
55 if (4 <= nSize)
56 return ((pb[0] << 24) | (pb[1] << 16) | (pb[2] << 8) | (pb[3]));
57
58 return -1;
59
60 } // int32_t FindStartCode(uint8_t * (&pb), size_t &nSize)
61
62 // NAL unit splitter class
63 class StartCodeIterator : public StartCodeIteratorBase
64 {
65 public:
66
StartCodeIterator()67 StartCodeIterator()
68 : m_code(-1)
69 , m_pts(-1)
70 {
71 Reset();
72 }
73
Reset()74 virtual void Reset()
75 {
76 m_code = -1;
77 m_pts = -1;
78 m_prev.clear();
79 }
80
81 // Initialize with bitstream buffer
Init(UMC::MediaData * pSource)82 virtual int32_t Init(UMC::MediaData * pSource)
83 {
84 Reset();
85 StartCodeIteratorBase::Init(pSource);
86 int32_t iCode = UMC_HEVC_DECODER::FindStartCode(m_pSource, m_nSourceSize);
87 return iCode;
88 }
89
90 // Returns first NAL unit ID in memory buffer
CheckNalUnitType(UMC::MediaData * pSource)91 virtual int32_t CheckNalUnitType(UMC::MediaData * pSource)
92 {
93 if (!pSource)
94 return -1;
95
96 uint8_t * source = (uint8_t *)pSource->GetDataPointer();
97 size_t size = pSource->GetDataSize();
98
99 int32_t startCodeSize;
100 return FindStartCode(source, size, startCodeSize);
101 }
102
103 // Set bitstream pointer to start code address
MoveToStartCode(UMC::MediaData * pSource)104 virtual int32_t MoveToStartCode(UMC::MediaData * pSource)
105 {
106 if (!pSource)
107 return -1;
108
109 if (m_code == -1)
110 m_prev.clear();
111
112 uint8_t * source = (uint8_t *)pSource->GetDataPointer();
113 size_t size = pSource->GetDataSize();
114
115 int32_t startCodeSize;
116 int32_t iCodeNext = FindStartCode(source, size, startCodeSize);
117
118 pSource->MoveDataPointer((int32_t)(source - (uint8_t *)pSource->GetDataPointer()));
119 if (iCodeNext != -1)
120 {
121 pSource->MoveDataPointer(-startCodeSize);
122 }
123
124 return iCodeNext;
125 }
126
127 // Set destination bitstream pointer and size to NAL unit
GetNALUnit(UMC::MediaData * pSource,UMC::MediaData * pDst)128 virtual int32_t GetNALUnit(UMC::MediaData * pSource, UMC::MediaData * pDst)
129 {
130 if (!pSource)
131 return EndOfStream(pDst);
132
133 int32_t iCode = GetNALUnitInternal(pSource, pDst);
134 if (iCode == -1)
135 {
136 bool endOfStream = pSource && ((pSource->GetFlags() & UMC::MediaData::FLAG_VIDEO_DATA_END_OF_STREAM) != 0);
137 if (endOfStream)
138 iCode = EndOfStream(pDst);
139 }
140
141 return iCode;
142 }
143
144 // Set destination bitstream pointer and size to NAL unit
GetNALUnitInternal(UMC::MediaData * pSource,UMC::MediaData * pDst)145 int32_t GetNALUnitInternal(UMC::MediaData * pSource, UMC::MediaData * pDst)
146 {
147 MFX_AUTO_LTRACE(MFX_TRACE_LEVEL_HOTSPOTS, "GetNALUnitInternal");
148 static const uint8_t startCodePrefix[] = {0, 0, 1};
149
150 if (m_code == -1)
151 m_prev.clear();
152
153 uint8_t * source = (uint8_t *)pSource->GetDataPointer();
154 size_t size = pSource->GetDataSize();
155
156 if (!size)
157 return -1;
158
159 int32_t startCodeSize;
160
161 int32_t iCodeNext = FindStartCode(source, size, startCodeSize);
162
163 // Use start code data which is saved from previous call because start code could be split between input buffers from application
164 if (!m_prev.empty())
165 {
166 if (iCodeNext == -1)
167 {
168 size_t szToAdd = source - (uint8_t *)pSource->GetDataPointer();
169 size_t szToMove = szToAdd;
170 if (m_prev.size() + szToAdd > m_suggestedSize)
171 {
172 szToAdd = (m_suggestedSize > m_prev.size()) ? m_suggestedSize - m_prev.size() : 0;
173 }
174
175 m_prev.insert(m_prev.end(), (uint8_t *)pSource->GetDataPointer(), (uint8_t *)pSource->GetDataPointer() + szToAdd);
176 pSource->MoveDataPointer((int32_t)szToMove);
177 return -1;
178 }
179
180 source -= startCodeSize;
181 m_prev.insert(m_prev.end(), (uint8_t *)pSource->GetDataPointer(), source);
182 pSource->MoveDataPointer((int32_t)(source - (uint8_t *)pSource->GetDataPointer()));
183
184 pDst->SetFlags(UMC::MediaData::FLAG_VIDEO_DATA_NOT_FULL_FRAME);
185 pDst->SetBufferPointer(&(m_prev[3]), m_prev.size() - 3);
186 pDst->SetDataSize(m_prev.size() - 3);
187 pDst->SetTime(m_pts);
188 int32_t code = m_code;
189 m_code = -1;
190 m_pts = -1;
191 return code;
192 }
193
194 if (iCodeNext == -1)
195 {
196 pSource->MoveDataPointer((int32_t)(source - (uint8_t *)pSource->GetDataPointer()));
197 return -1;
198 }
199
200 m_pts = pSource->GetTime();
201 m_code = iCodeNext;
202
203 // move before start code
204 pSource->MoveDataPointer((int32_t)(source - (uint8_t *)pSource->GetDataPointer() - startCodeSize));
205
206 int32_t startCodeSize1;
207 iCodeNext = FindStartCode(source, size, startCodeSize1);
208
209 pSource->MoveDataPointer(startCodeSize);
210
211 uint32_t flags = pSource->GetFlags();
212
213 if (iCodeNext == -1 && !(flags & UMC::MediaData::FLAG_VIDEO_DATA_NOT_FULL_UNIT))
214 {
215 iCodeNext = 1;
216 startCodeSize1 = 0;
217 source += size;
218 size = 0;
219 }
220
221 if (iCodeNext == -1)
222 {
223 if (m_code == NAL_UT_SPS)
224 {
225 pSource->MoveDataPointer(-startCodeSize); // leave start code for SPS
226 return -1;
227 }
228
229 VM_ASSERT(!m_prev.size());
230
231 size_t sz = source - (uint8_t *)pSource->GetDataPointer();
232 size_t szToMove = sz;
233 if (sz > m_suggestedSize)
234 {
235 sz = m_suggestedSize;
236 }
237
238 if (!m_prev.size())
239 m_prev.insert(m_prev.end(), startCodePrefix, startCodePrefix + sizeof(startCodePrefix));
240 m_prev.insert(m_prev.end(), (uint8_t *)pSource->GetDataPointer(), (uint8_t *)pSource->GetDataPointer() + sz);
241 pSource->MoveDataPointer((int32_t)szToMove);
242 return -1;
243 }
244
245 // fill
246 size_t nal_size = source - (uint8_t *)pSource->GetDataPointer() - startCodeSize1;
247 pDst->SetBufferPointer((uint8_t*)pSource->GetDataPointer(), nal_size);
248 pDst->SetDataSize(nal_size);
249 pDst->SetFlags(pSource->GetFlags());
250 pSource->MoveDataPointer((int32_t)nal_size);
251
252 int32_t code = m_code;
253 m_code = -1;
254
255 pDst->SetTime(m_pts);
256 m_pts = -1;
257 return code;
258 }
259
260 // Reset state because stream is finished
EndOfStream(UMC::MediaData * pDst)261 int32_t EndOfStream(UMC::MediaData * pDst)
262 {
263 if (m_code == -1)
264 {
265 m_prev.clear();
266 return -1;
267 }
268
269 if (m_prev.size())
270 {
271 pDst->SetBufferPointer(&(m_prev[3]), m_prev.size() - 3);
272 pDst->SetDataSize(m_prev.size() - 3);
273 pDst->SetTime(m_pts);
274 int32_t code = m_code;
275 m_code = -1;
276 m_pts = -1;
277 return code;
278 }
279
280 m_code = -1;
281 return -1;
282 }
283
284 private:
285 std::vector<uint8_t> m_prev;
286 int32_t m_code;
287 double m_pts;
288
289 // Searches NAL unit start code, places input pointer to it and fills up size paramters
290 // ML: OPT: TODO: Replace with MaxL's fast start code search
FindStartCode(uint8_t * (& pb),size_t & size,int32_t & startCodeSize)291 int32_t FindStartCode(uint8_t * (&pb), size_t & size, int32_t & startCodeSize)
292 {
293 uint32_t zeroCount = 0;
294
295 int32_t i = 0;
296 for (; i < (int32_t)size - 2; )
297 {
298 if (pb[1])
299 {
300 pb += 2;
301 i += 2;
302 continue;
303 }
304
305 zeroCount = 0;
306 if (!pb[0])
307 zeroCount++;
308
309 uint32_t j;
310 for (j = 1; j < (uint32_t)size - i; j++)
311 {
312 if (pb[j])
313 break;
314 }
315
316 zeroCount = zeroCount ? j: j - 1;
317
318 pb += j;
319 i += j;
320
321 if (i >= (int32_t)size)
322 {
323 break;
324 }
325
326 if (zeroCount >= 2 && pb[0] == 1)
327 {
328 startCodeSize = std::min(zeroCount + 1, 4u);
329 size -= i + 1;
330 pb++; // remove 0x01 symbol
331 if (size >= 1)
332 {
333 return (pb[0] & NAL_UNITTYPE_BITS_H265) >> NAL_UNITTYPE_SHIFT_H265;
334 }
335 else
336 {
337 pb -= startCodeSize;
338 size += startCodeSize;
339 startCodeSize = 0;
340 return -1;
341 }
342 }
343
344 zeroCount = 0;
345 }
346
347 if (!zeroCount)
348 {
349 for (uint32_t k = 0; k < size - i; k++, pb++)
350 {
351 if (pb[0])
352 {
353 zeroCount = 0;
354 continue;
355 }
356
357 zeroCount++;
358 }
359 }
360
361 zeroCount = std::min(zeroCount, 3u);
362 pb -= zeroCount;
363 size = zeroCount;
364 startCodeSize = zeroCount;
365 return -1;
366 }
367 };
368
369 // Memory big-to-little endian converter implementation
370 class Swapper : public SwapperBase
371 {
372 public:
373
SwapMemory(uint8_t * pDestination,size_t & nDstSize,uint8_t * pSource,size_t nSrcSize,std::vector<uint32_t> * pRemovedOffsets)374 virtual void SwapMemory(uint8_t *pDestination, size_t &nDstSize, uint8_t *pSource, size_t nSrcSize, std::vector<uint32_t> *pRemovedOffsets)
375 {
376 SwapMemoryAndRemovePreventingBytes_H265(pDestination, nDstSize, pSource, nSrcSize, pRemovedOffsets);
377 }
378
SwapMemory(MemoryPiece * pMemDst,MemoryPiece * pMemSrc,std::vector<uint32_t> * pRemovedOffsets)379 virtual void SwapMemory(MemoryPiece * pMemDst, MemoryPiece * pMemSrc, std::vector<uint32_t> *pRemovedOffsets)
380 {
381 size_t dstSize = pMemSrc->GetDataSize();
382 SwapMemory(pMemDst->GetPointer(),
383 dstSize,
384 pMemSrc->GetPointer(),
385 pMemSrc->GetDataSize(),
386 pRemovedOffsets);
387
388 VM_ASSERT(pMemDst->GetSize() >= dstSize);
389 size_t tail_size = std::min<size_t>(pMemDst->GetSize() - dstSize, DEFAULT_NU_TAIL_SIZE);
390 memset(pMemDst->GetPointer() + dstSize, DEFAULT_NU_TAIL_VALUE, tail_size);
391 pMemDst->SetDataSize(dstSize);
392 pMemDst->SetTime(pMemSrc->GetTime());
393 }
394 };
395
NALUnitSplitter_H265()396 NALUnitSplitter_H265::NALUnitSplitter_H265()
397 : m_pSwapper(0)
398 , m_pStartCodeIter(0)
399 {
400 m_MediaData.SetExData(&m_MediaDataEx);
401 }
402
~NALUnitSplitter_H265()403 NALUnitSplitter_H265::~NALUnitSplitter_H265()
404 {
405 Release();
406 }
407
408 // Initialize splitter with default values
Init()409 void NALUnitSplitter_H265::Init()
410 {
411 Release();
412
413 m_pSwapper = new Swapper();
414 m_pStartCodeIter = new StartCodeIterator();
415 }
416
417 // Reset state
Reset()418 void NALUnitSplitter_H265::Reset()
419 {
420 if (m_pStartCodeIter)
421 {
422 m_pStartCodeIter->Reset();
423 }
424 }
425
426 // Free resources
Release()427 void NALUnitSplitter_H265::Release()
428 {
429 delete m_pSwapper;
430 m_pSwapper = 0;
431 delete m_pStartCodeIter;
432 m_pStartCodeIter = 0;
433 }
434
435 // Returns first NAL unit ID in memory buffer
CheckNalUnitType(UMC::MediaData * pSource)436 int32_t NALUnitSplitter_H265::CheckNalUnitType(UMC::MediaData * pSource)
437 {
438 return m_pStartCodeIter->CheckNalUnitType(pSource); // find first start code
439 }
440
441 // Set bitstream pointer to start code address
MoveToStartCode(UMC::MediaData * pSource)442 int32_t NALUnitSplitter_H265::MoveToStartCode(UMC::MediaData * pSource)
443 {
444 return m_pStartCodeIter->MoveToStartCode(pSource); // find first start code
445 }
446
447 // Set destination bitstream pointer and size to NAL unit
GetNalUnits(UMC::MediaData * pSource)448 UMC::MediaDataEx * NALUnitSplitter_H265::GetNalUnits(UMC::MediaData * pSource)
449 {
450 UMC::MediaDataEx * out = &m_MediaData;
451 UMC::MediaDataEx::_MediaDataEx* pMediaDataEx = &m_MediaDataEx;
452
453 int32_t iCode = m_pStartCodeIter->GetNALUnit(pSource, out);
454
455 if (iCode == -1)
456 {
457 pMediaDataEx->count = 0;
458 return 0;
459 }
460
461 pMediaDataEx->values[0] = iCode;
462
463 pMediaDataEx->offsets[0] = 0;
464 pMediaDataEx->offsets[1] = (int32_t)out->GetDataSize();
465 pMediaDataEx->count = 1;
466 pMediaDataEx->index = 0;
467 return out;
468 }
469
470 // Utility class for writing 32-bit little endian integers
471 class H265DwordPointer_
472 {
473 public:
474 // Default constructor
H265DwordPointer_(void)475 H265DwordPointer_(void)
476 {
477 m_pDest = NULL;
478 m_iCur = 0;
479 m_nByteNum = 0;
480 }
481
operator =(void * pDest)482 H265DwordPointer_ operator = (void *pDest)
483 {
484 m_pDest = (uint32_t *) pDest;
485 m_nByteNum = 0;
486 m_iCur = 0;
487
488 return *this;
489 }
490
491 // Increment operator
operator ++(void)492 H265DwordPointer_ &operator ++ (void)
493 {
494 if (4 == ++m_nByteNum)
495 {
496 *m_pDest = m_iCur;
497 m_pDest += 1;
498 m_nByteNum = 0;
499 m_iCur = 0;
500 }
501 else
502 m_iCur <<= 8;
503
504 return *this;
505 }
506
operator =(uint8_t nByte)507 uint8_t operator = (uint8_t nByte)
508 {
509 m_iCur = (m_iCur & ~0x0ff) | ((uint32_t) nByte);
510
511 return nByte;
512 }
513
514 protected:
515 uint32_t *m_pDest; // (uint32_t *) pointer to destination buffer
516 uint32_t m_nByteNum; // (uint32_t) number of current byte in dword
517 uint32_t m_iCur; // (uint32_t) current dword
518 };
519
520 // Utility class for reading big endian bitstream
521 class H265SourcePointer_
522 {
523 public:
524 // Default constructor
H265SourcePointer_(void)525 H265SourcePointer_(void)
526 {
527 m_nZeros = 0;
528 m_pSource = NULL;
529 m_nRemovedBytes = 0;
530 }
531
operator =(void * pSource)532 H265SourcePointer_ &operator = (void *pSource)
533 {
534 m_pSource = (uint8_t *) pSource;
535
536 m_nZeros = 0;
537 m_nRemovedBytes = 0;
538
539 return *this;
540 }
541
operator ++(void)542 H265SourcePointer_ &operator ++ (void)
543 {
544 uint8_t bCurByte = m_pSource[0];
545
546 if (0 == bCurByte)
547 m_nZeros += 1;
548 else
549 {
550 if ((3 == bCurByte) && (2 <= m_nZeros))
551 m_nRemovedBytes += 1;
552 m_nZeros = 0;
553 }
554
555 m_pSource += 1;
556
557 return *this;
558 }
559
IsPrevent(void)560 bool IsPrevent(void)
561 {
562 if ((3 == m_pSource[0]) && (2 <= m_nZeros))
563 return true;
564 else
565 return false;
566 }
567
operator uint8_t(void)568 operator uint8_t (void)
569 {
570 return m_pSource[0];
571 }
572
GetRemovedBytes(void)573 uint32_t GetRemovedBytes(void)
574 {
575 return m_nRemovedBytes;
576 }
577
578 protected:
579 uint8_t *m_pSource; // (uint8_t *) pointer to destination buffer
580 uint32_t m_nZeros; // (uint32_t) number of preceding zeros
581 uint32_t m_nRemovedBytes; // (uint32_t) number of removed bytes
582 };
583
584 // Change memory region to little endian for reading with 32-bit DWORDs and remove start code emulation prevention byteps
SwapMemoryAndRemovePreventingBytes_H265(void * pDestination,size_t & nDstSize,void * pSource,size_t nSrcSize,std::vector<uint32_t> * pRemovedOffsets)585 void SwapMemoryAndRemovePreventingBytes_H265(void *pDestination, size_t &nDstSize, void *pSource, size_t nSrcSize, std::vector<uint32_t> *pRemovedOffsets)
586 {
587 H265DwordPointer_ pDst;
588 H265SourcePointer_ pSrc;
589 size_t i;
590
591 // DwordPointer object is swapping written bytes
592 // H265SourcePointer_ removes preventing start-code bytes
593
594 // reset pointer(s)
595 pSrc = pSource;
596 pDst = pDestination;
597
598 // first two bytes
599 i = 0;
600 while (i < std::min<uint32_t>(2, nSrcSize))
601 {
602 pDst = (uint8_t) pSrc;
603 ++pDst;
604 ++pSrc;
605 ++i;
606 }
607
608 // do swapping
609 if (NULL != pRemovedOffsets)
610 {
611 while (i < (uint32_t) nSrcSize)
612 {
613 if (false == pSrc.IsPrevent())
614 {
615 pDst = (uint8_t) pSrc;
616 ++pDst;
617 }
618 else
619 pRemovedOffsets->push_back(uint32_t(i));
620
621 ++pSrc;
622 ++i;
623 }
624 }
625 else
626 {
627 while (i < (uint32_t) nSrcSize)
628 {
629 if (false == pSrc.IsPrevent())
630 {
631 pDst = (uint8_t) pSrc;
632 ++pDst;
633 }
634
635 ++pSrc;
636 ++i;
637 }
638 }
639
640 // write padding bytes
641 nDstSize = nSrcSize - pSrc.GetRemovedBytes();
642 while (nDstSize & 3)
643 {
644 pDst = (uint8_t) (0);
645 ++nDstSize;
646 ++pDst;
647 }
648
649 } // void SwapMemoryAndRemovePreventingBytes_H265(void *pDst, size_t &nDstSize, void *pSrc, size_t nSrcSize, , std::vector<uint32_t> *pRemovedOffsets)
650
651 } // namespace UMC_HEVC_DECODER
652
653 #endif // MFX_ENABLE_H265_VIDEO_DECODE
654