• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © Microsoft Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include <climits>
25 #include "d3d12_video_encoder_bitstream.h"
26 
d3d12_video_encoder_bitstream()27 d3d12_video_encoder_bitstream::d3d12_video_encoder_bitstream()
28 {
29    m_pBitsBuffer       = nullptr;
30    m_uiBitsBufferSize  = 0;
31    m_uiOffset          = 0;
32    m_iBitsToGo         = 32;
33    m_uintEncBuffer     = 0;
34    m_bExternalBuffer   = false;
35    m_bBufferOverflow   = false;
36    m_bPreventStartCode = false;
37    m_bAllowReallocate  = false;
38 }
39 
~d3d12_video_encoder_bitstream()40 d3d12_video_encoder_bitstream::~d3d12_video_encoder_bitstream()
41 {
42    if (!m_bExternalBuffer) {
43       if (m_pBitsBuffer) {
44          delete[](m_pBitsBuffer);
45          (m_pBitsBuffer) = NULL;
46       }
47    }
48 }
49 
50 int32_t
get_exp_golomb0_code_len(uint32_t uiVal)51 d3d12_video_encoder_bitstream::get_exp_golomb0_code_len(uint32_t uiVal)
52 {
53    int32_t iLen = 0;
54    uiVal++;
55 
56    if (uiVal >= 0x10000) {
57       uiVal >>= 16;
58       iLen += 16;
59    }
60    if (uiVal >= 0x100) {
61       uiVal >>= 8;
62       iLen += 8;
63    }
64 
65    assert(uiVal < 256);
66 
67    return iLen + m_iLog_2_N[uiVal];
68 }
69 
70 void
exp_Golomb_ue(uint32_t uiVal)71 d3d12_video_encoder_bitstream::exp_Golomb_ue(uint32_t uiVal)
72 {
73    if (uiVal != UINT32_MAX) {
74       int32_t iLen = get_exp_golomb0_code_len(uiVal);
75       put_bits((iLen << 1) + 1, uiVal + 1);
76    } else {
77       put_bits(32, 0);
78       put_bits(1, 1);
79       put_bits(32, 1);
80    }
81 }
82 
83 void
exp_Golomb_se(int32_t iVal)84 d3d12_video_encoder_bitstream::exp_Golomb_se(int32_t iVal)
85 {
86    if (iVal > 0) {
87       exp_Golomb_ue((iVal << 1) - 1);
88    } else {
89       exp_Golomb_ue(((-iVal) << 1) - (iVal == INT_MIN));
90    }
91 }
92 
93 void
setup_bitstream(uint32_t uiInitBufferSize,uint8_t * pBuffer)94 d3d12_video_encoder_bitstream::setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer)
95 {
96    m_pBitsBuffer      = pBuffer;
97    m_uiBitsBufferSize = uiInitBufferSize;
98    m_uiOffset         = 0;
99    memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
100    m_bExternalBuffer  = true;
101    m_bAllowReallocate = false;
102 }
103 
104 bool
create_bitstream(uint32_t uiInitBufferSize)105 d3d12_video_encoder_bitstream::create_bitstream(uint32_t uiInitBufferSize)
106 {
107    assert((uiInitBufferSize) >= 4 && !(uiInitBufferSize & 3));
108 
109    m_pBitsBuffer = (uint8_t *) new uint8_t[uiInitBufferSize];
110 
111    if (nullptr == m_pBitsBuffer) {
112       return false;
113    }
114 
115    m_uiBitsBufferSize = uiInitBufferSize;
116    m_uiOffset         = 0;
117    memset(m_pBitsBuffer, 0, m_uiBitsBufferSize);
118    m_bExternalBuffer = false;
119 
120    return true;
121 }
122 
123 bool
reallocate_buffer()124 d3d12_video_encoder_bitstream::reallocate_buffer()
125 {
126    uint32_t uiBufferSize = m_uiBitsBufferSize * 3 / 2;
127    uint8_t *pNewBuffer   = (uint8_t *) new uint8_t[uiBufferSize];
128 
129    if (nullptr == pNewBuffer) {
130       return false;
131    }
132 
133    memcpy(pNewBuffer, m_pBitsBuffer, m_uiOffset * sizeof(uint8_t));
134    if (m_pBitsBuffer) {
135       delete[](m_pBitsBuffer);
136       (m_pBitsBuffer) = NULL;
137    }
138    m_pBitsBuffer      = pNewBuffer;
139    m_uiBitsBufferSize = uiBufferSize;
140    return true;
141 }
142 
143 bool
verify_buffer(uint32_t uiBytesToWrite)144 d3d12_video_encoder_bitstream::verify_buffer(uint32_t uiBytesToWrite)
145 {
146    if (!m_bBufferOverflow) {
147       if (m_uiOffset + uiBytesToWrite > m_uiBitsBufferSize) {
148          if (!m_bAllowReallocate || !reallocate_buffer()) {
149             m_bBufferOverflow = true;
150             return false;
151          }
152       }
153 
154       return true;
155    }
156 
157    return false;
158 }
159 
160 void
inc_current_offset(int32_t dwOffset)161 d3d12_video_encoder_bitstream::inc_current_offset(int32_t dwOffset)
162 {
163    assert(32 == m_iBitsToGo && m_uiOffset < m_uiBitsBufferSize);
164    m_uiOffset += dwOffset;
165 }
166 
167 void
get_current_buffer_position_and_size(uint8_t ** ppCurrBufPos,int32_t * pdwLeftBufSize)168 d3d12_video_encoder_bitstream::get_current_buffer_position_and_size(uint8_t **ppCurrBufPos, int32_t *pdwLeftBufSize)
169 {
170    assert(32 == m_iBitsToGo && m_uiOffset < m_uiBitsBufferSize);
171    *ppCurrBufPos   = m_pBitsBuffer + m_uiOffset;
172    *pdwLeftBufSize = m_uiBitsBufferSize - m_uiOffset;
173 }
174 
175 void
attach(uint8_t * pBitsBuffer,uint32_t uiBufferSize)176 d3d12_video_encoder_bitstream::attach(uint8_t *pBitsBuffer, uint32_t uiBufferSize)
177 {
178    m_pBitsBuffer      = pBitsBuffer;
179    m_uiBitsBufferSize = uiBufferSize;
180    m_bExternalBuffer  = true;
181    m_bBufferOverflow  = false;
182    m_bAllowReallocate = false;
183 
184    clear();
185 }
186 
187 void
write_byte_start_code_prevention(uint8_t u8Val)188 d3d12_video_encoder_bitstream::write_byte_start_code_prevention(uint8_t u8Val)
189 {
190    int32_t  iOffset = m_uiOffset;
191    uint8_t *pBuffer = m_pBitsBuffer + iOffset;
192 
193    if (m_bPreventStartCode && iOffset > 1) {
194       if (((u8Val & 0xfc) | pBuffer[-2] | pBuffer[-1]) == 0) {
195          *pBuffer++ = 3;
196          iOffset++;
197       }
198    }
199 
200    *pBuffer = u8Val;
201    iOffset++;
202 
203    m_uiOffset = iOffset;
204 }
205 
206 #define WRITE_BYTE(byte) write_byte_start_code_prevention(byte)
207 
208 void
put_bits(int32_t uiBitsCount,uint32_t iBitsVal)209 d3d12_video_encoder_bitstream::put_bits(int32_t uiBitsCount, uint32_t iBitsVal)
210 {
211    assert(uiBitsCount <= 32);
212 
213    if (uiBitsCount < m_iBitsToGo) {
214       m_uintEncBuffer |= (iBitsVal << (m_iBitsToGo - uiBitsCount));
215       m_iBitsToGo -= uiBitsCount;
216    } else if (verify_buffer(4)) {
217       int32_t iLeftOverBits = uiBitsCount - m_iBitsToGo;
218       m_uintEncBuffer |= (iBitsVal >> iLeftOverBits);
219 
220       uint8_t *temp = (uint8_t *) (&m_uintEncBuffer);
221       WRITE_BYTE(*(temp + 3));
222       WRITE_BYTE(*(temp + 2));
223       WRITE_BYTE(*(temp + 1));
224       WRITE_BYTE(*temp);
225 
226       m_uintEncBuffer = 0;
227       m_iBitsToGo     = 32 - iLeftOverBits;
228 
229       if (iLeftOverBits > 0) {
230          m_uintEncBuffer = (iBitsVal << (32 - iLeftOverBits));
231       }
232    }
233 }
234 
235 void
flush()236 d3d12_video_encoder_bitstream::flush()
237 {
238    bool isAligned = is_byte_aligned();   // causes side-effects in object state, don't put inside assert()
239    assert(isAligned);
240 
241    uint32_t temp = (uint32_t)(32 - m_iBitsToGo);
242 
243    if (!verify_buffer(temp >> 3)) {
244       return;
245    }
246 
247    while (temp > 0) {
248       WRITE_BYTE((uint8_t)(m_uintEncBuffer >> 24));
249       m_uintEncBuffer <<= 8;
250       temp -= 8;
251    }
252 
253    m_iBitsToGo     = 32;
254    m_uintEncBuffer = 0;
255 }
256 
257 void
append_byte_stream(d3d12_video_encoder_bitstream * pStream)258 d3d12_video_encoder_bitstream::append_byte_stream(d3d12_video_encoder_bitstream *pStream)
259 {
260    bool isStreamAligned =
261       pStream->is_byte_aligned();   // causes side-effects in object state, don't put inside assert()
262    assert(isStreamAligned);
263    bool isThisAligned = is_byte_aligned();   // causes side-effects in object state, don't put inside assert()
264    assert(isThisAligned);
265    assert(m_iBitsToGo == 32);
266 
267    uint8_t *pDst  = m_pBitsBuffer + m_uiOffset;
268    uint8_t *pSrc  = pStream->get_bitstream_buffer();
269    uint32_t uiLen = (uint32_t) pStream->get_byte_count();
270 
271    if (!verify_buffer(uiLen)) {
272       return;
273    }
274 
275    memcpy(pDst, pSrc, uiLen);
276    m_uiOffset += uiLen;
277 }
278