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