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,size_t initial_byte_offset)94 d3d12_video_encoder_bitstream::setup_bitstream(uint32_t uiInitBufferSize, uint8_t *pBuffer, size_t initial_byte_offset)
95 {
96 m_pBitsBuffer = pBuffer;
97 m_uiBitsBufferSize = uiInitBufferSize;
98 m_uiOffset = initial_byte_offset;
99 memset(m_pBitsBuffer + initial_byte_offset, 0, m_uiBitsBufferSize - initial_byte_offset);
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 > 0);
212 assert(uiBitsCount <= 32);
213
214 if (uiBitsCount < m_iBitsToGo) {
215 m_uintEncBuffer |= (iBitsVal << (m_iBitsToGo - uiBitsCount));
216 m_iBitsToGo -= uiBitsCount;
217 } else if (verify_buffer(4)) {
218 int32_t iLeftOverBits = uiBitsCount - m_iBitsToGo;
219 m_uintEncBuffer |= (iBitsVal >> iLeftOverBits);
220
221 uint8_t *temp = (uint8_t *) (&m_uintEncBuffer);
222 WRITE_BYTE(*(temp + 3));
223 WRITE_BYTE(*(temp + 2));
224 WRITE_BYTE(*(temp + 1));
225 WRITE_BYTE(*temp);
226
227 m_uintEncBuffer = 0;
228 m_iBitsToGo = 32 - iLeftOverBits;
229
230 if (iLeftOverBits > 0) {
231 m_uintEncBuffer = (iBitsVal << (32 - iLeftOverBits));
232 }
233 }
234 }
235
236 void
flush()237 d3d12_video_encoder_bitstream::flush()
238 {
239 ASSERTED bool isAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
240 assert(isAligned);
241
242 uint32_t temp = (uint32_t) (32 - m_iBitsToGo);
243
244 if (!verify_buffer(temp >> 3)) {
245 return;
246 }
247
248 while (temp > 0) {
249 WRITE_BYTE((uint8_t) (m_uintEncBuffer >> 24));
250 m_uintEncBuffer <<= 8;
251 temp -= 8;
252 }
253
254 m_iBitsToGo = 32;
255 m_uintEncBuffer = 0;
256 }
257
258 void
append_byte_stream(d3d12_video_encoder_bitstream * pStream)259 d3d12_video_encoder_bitstream::append_byte_stream(d3d12_video_encoder_bitstream *pStream)
260 {
261 ASSERTED bool isStreamAligned =
262 pStream->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
263 assert(isStreamAligned);
264 ASSERTED bool isThisAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
265 assert(isThisAligned);
266 assert(m_iBitsToGo == 32);
267
268 uint8_t *pDst = m_pBitsBuffer + m_uiOffset;
269 uint8_t *pSrc = pStream->get_bitstream_buffer();
270 uint32_t uiLen = (uint32_t) pStream->get_byte_count();
271
272 if (!verify_buffer(uiLen)) {
273 return;
274 }
275
276 memcpy(pDst, pSrc, uiLen);
277 m_uiOffset += uiLen;
278 }
279
280 void
put_aligning_bits()281 d3d12_video_encoder_bitstream::put_aligning_bits()
282 {
283 int32_t iLeft = get_num_bits_for_byte_align();
284 if (iLeft)
285 put_bits(iLeft, 0); // trailing_zero_bit
286
287 ASSERTED bool isAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
288 assert(isAligned);
289 }
290
291 void
put_trailing_bits()292 d3d12_video_encoder_bitstream::put_trailing_bits()
293 {
294 // trailing_one_bit shall be equal to 1.
295 // When the syntax element trailing_one_bit is read, it is a requirement that nbBits is greater than zero.
296 put_bits(1, 1); // trailing_one_bit
297 int32_t nbBits = get_num_bits_for_byte_align();
298 while (nbBits > 0) {
299 put_bits(1, 0); // trailing_zero_bit
300 nbBits--;
301 }
302 ASSERTED bool isAligned = is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
303 assert(isAligned);
304 }
305
306 void
put_su_bits(uint16_t uiBitsCount,int32_t iBitsVal)307 d3d12_video_encoder_bitstream::put_su_bits(uint16_t uiBitsCount, int32_t iBitsVal)
308 {
309 put_bits(uiBitsCount, calculate_su_bits(uiBitsCount, iBitsVal));
310 }
311
312 void
put_ns_bits(uint16_t uiBitsCount,uint32_t iBitsVal)313 d3d12_video_encoder_bitstream::put_ns_bits(uint16_t uiBitsCount, uint32_t iBitsVal)
314 {
315 if (uiBitsCount > 1) {
316 uint32_t width = 0;
317 uint32_t tmp = uiBitsCount;
318 while (tmp) {
319 tmp = (tmp >> 1);
320 width++;
321 }
322 uint32_t m = (1 << width) - uiBitsCount;
323 if (iBitsVal < m)
324 put_bits(width - 1, iBitsVal);
325 else
326 put_bits(width, iBitsVal + m);
327 }
328 }
329
330 uint16_t
calculate_su_bits(uint16_t uiBitsCount,int32_t iBitsVal)331 d3d12_video_encoder_bitstream::calculate_su_bits(uint16_t uiBitsCount, int32_t iBitsVal)
332 {
333 int16_t mask_sign = 1 << (uiBitsCount - 1);
334 if (iBitsVal & mask_sign)
335 iBitsVal = iBitsVal - 2 * mask_sign;
336 return iBitsVal;
337 }
338
339 void
put_le_bytes(size_t uiBytesCount,uint32_t iBitsVal)340 d3d12_video_encoder_bitstream::put_le_bytes(size_t uiBytesCount, uint32_t iBitsVal)
341 {
342 assert(uiBytesCount <= sizeof(iBitsVal));
343 for (size_t i = 0; i < uiBytesCount; i++) {
344 put_bits(8, static_cast<uint8_t>(iBitsVal & 0xFF));
345 iBitsVal >>= 8;
346 }
347 }
348
349 void
put_leb128_bytes(uint64_t iBitsVal)350 d3d12_video_encoder_bitstream::put_leb128_bytes(uint64_t iBitsVal)
351 {
352 do {
353 uint8_t cur_byte = (iBitsVal & 0x7F);
354 iBitsVal >>= 7;
355 if (iBitsVal != 0)
356 cur_byte |= 0x80;
357 put_bits(8, cur_byte);
358 } while (iBitsVal != 0);
359 }
360