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