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 "d3d12_video_encoder_nalu_writer_h264.h"
25 #include <algorithm>
26
27 void
rbsp_trailing(d3d12_video_encoder_bitstream * pBitstream)28 d3d12_video_nalu_writer_h264::rbsp_trailing(d3d12_video_encoder_bitstream *pBitstream)
29 {
30 pBitstream->put_bits(1, 1);
31 int32_t iLeft = pBitstream->get_num_bits_for_byte_align();
32
33 if (iLeft) {
34 pBitstream->put_bits(iLeft, 0);
35 }
36
37 bool isAligned = pBitstream->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
38 assert(isAligned);
39 }
40
41 uint32_t
write_sps_bytes(d3d12_video_encoder_bitstream * pBitstream,H264_SPS * pSPS)42 d3d12_video_nalu_writer_h264::write_sps_bytes(d3d12_video_encoder_bitstream *pBitstream, H264_SPS *pSPS)
43 {
44 int32_t iBytesWritten = pBitstream->get_byte_count();
45
46 // Standard constraint to be between 0 and 31 inclusive
47 assert(pSPS->seq_parameter_set_id >= 0);
48 assert(pSPS->seq_parameter_set_id < 32);
49
50 pBitstream->put_bits(8, pSPS->profile_idc);
51 pBitstream->put_bits(1, 0); // constraint_set0_flag
52 pBitstream->put_bits(1, 0); // constraint_set1_flag
53 pBitstream->put_bits(1, 0); // constraint_set2_flag
54 pBitstream->put_bits(1, pSPS->constraint_set3_flag);
55 pBitstream->put_bits(1, 0); // constraint_set4_flag
56 pBitstream->put_bits(1, 0); // constraint_set5_flag
57 pBitstream->put_bits(2, 0);
58 pBitstream->put_bits(8, pSPS->level_idc);
59 pBitstream->exp_Golomb_ue(pSPS->seq_parameter_set_id);
60
61 // Only support profiles defined in D3D12 Video Encode
62 // If adding new profile support, check that the chroma_format_idc and bit depth are set correctly below
63 // for the new additions
64 assert((pSPS->profile_idc == H264_PROFILE_MAIN) || (pSPS->profile_idc == H264_PROFILE_HIGH) ||
65 (pSPS->profile_idc == H264_PROFILE_HIGH10));
66
67 if ((pSPS->profile_idc == H264_PROFILE_HIGH) || (pSPS->profile_idc == H264_PROFILE_HIGH10)) {
68 // chroma_format_idc always 4.2.0
69 pBitstream->exp_Golomb_ue(1);
70 // Assume no separate_colour_plane_flag given chroma_format_idc = 1
71 pBitstream->exp_Golomb_ue(pSPS->bit_depth_luma_minus8);
72 pBitstream->exp_Golomb_ue(pSPS->bit_depth_chroma_minus8);
73 // qpprime_y_zero_transform_bypass_flag
74 pBitstream->put_bits(1, 0);
75 // seq_scaling_matrix_present_flag)
76 pBitstream->put_bits(1, 0);
77 }
78
79 pBitstream->exp_Golomb_ue(pSPS->log2_max_frame_num_minus4);
80
81 pBitstream->exp_Golomb_ue(pSPS->pic_order_cnt_type);
82 if (pSPS->pic_order_cnt_type == 0) {
83 pBitstream->exp_Golomb_ue(pSPS->log2_max_pic_order_cnt_lsb_minus4);
84 }
85 pBitstream->exp_Golomb_ue(pSPS->max_num_ref_frames);
86 pBitstream->put_bits(1, pSPS->gaps_in_frame_num_value_allowed_flag);
87 pBitstream->exp_Golomb_ue(pSPS->pic_width_in_mbs_minus1);
88 pBitstream->exp_Golomb_ue(pSPS->pic_height_in_map_units_minus1);
89
90 // No support for interlace in D3D12 Video Encode
91 // frame_mbs_only_flag coded as 1
92 pBitstream->put_bits(1, 1); // frame_mbs_only_flag
93 pBitstream->put_bits(1, pSPS->direct_8x8_inference_flag);
94
95 // no cropping
96 pBitstream->put_bits(1, pSPS->frame_cropping_flag); // frame_cropping_flag
97 if (pSPS->frame_cropping_flag) {
98 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_left_offset);
99 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_right_offset);
100 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_top_offset);
101 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_bottom_offset);
102 }
103
104 // We're not including the VUI so this better be zero.
105 pBitstream->put_bits(1, 0); // vui_paramenters_present_flag
106
107 rbsp_trailing(pBitstream);
108 pBitstream->flush();
109
110 iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
111 return (uint32_t) iBytesWritten;
112 }
113
114 uint32_t
write_pps_bytes(d3d12_video_encoder_bitstream * pBitstream,H264_PPS * pPPS,BOOL bIsHighProfile)115 d3d12_video_nalu_writer_h264::write_pps_bytes(d3d12_video_encoder_bitstream *pBitstream,
116 H264_PPS * pPPS,
117 BOOL bIsHighProfile)
118 {
119 int32_t iBytesWritten = pBitstream->get_byte_count();
120
121 // Standard constraint to be between 0 and 31 inclusive
122 assert(pPPS->seq_parameter_set_id >= 0);
123 assert(pPPS->seq_parameter_set_id < 32);
124
125 // Standard constraint to be between 0 and 255 inclusive
126 assert(pPPS->pic_parameter_set_id >= 0);
127 assert(pPPS->pic_parameter_set_id < 256);
128
129 pBitstream->exp_Golomb_ue(pPPS->pic_parameter_set_id);
130 pBitstream->exp_Golomb_ue(pPPS->seq_parameter_set_id);
131 pBitstream->put_bits(1, pPPS->entropy_coding_mode_flag);
132 pBitstream->put_bits(1, pPPS->pic_order_present_flag); // bottom_field_pic_order_in_frame_present_flag
133 pBitstream->exp_Golomb_ue(0); // num_slice_groups_minus1
134
135
136 pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l0_active_minus1);
137 pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l1_active_minus1);
138 pBitstream->put_bits(1, 0); // weighted_pred_flag
139 pBitstream->put_bits(2, 0); // weighted_bipred_idc
140 pBitstream->exp_Golomb_se(0); // pic_init_qp_minus26
141 pBitstream->exp_Golomb_se(0); // pic_init_qs_minus26
142 pBitstream->exp_Golomb_se(0); // chroma_qp_index_offset
143 pBitstream->put_bits(1, 1); // deblocking_filter_control_present_flag
144 pBitstream->put_bits(1, pPPS->constrained_intra_pred_flag);
145 pBitstream->put_bits(1, 0); // redundant_pic_cnt_present_flag
146
147 if (bIsHighProfile) {
148 pBitstream->put_bits(1, pPPS->transform_8x8_mode_flag);
149 pBitstream->put_bits(1, 0); // pic_scaling_matrix_present_flag
150 pBitstream->exp_Golomb_se(0); // second_chroma_qp_index_offset
151 }
152
153 rbsp_trailing(pBitstream);
154 pBitstream->flush();
155
156 iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
157 return (uint32_t) iBytesWritten;
158 }
159
160 uint32_t
wrap_sps_nalu(d3d12_video_encoder_bitstream * pNALU,d3d12_video_encoder_bitstream * pRBSP)161 d3d12_video_nalu_writer_h264::wrap_sps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
162 {
163 return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_SPS);
164 }
165
166 uint32_t
wrap_pps_nalu(d3d12_video_encoder_bitstream * pNALU,d3d12_video_encoder_bitstream * pRBSP)167 d3d12_video_nalu_writer_h264::wrap_pps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
168 {
169 return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_PPS);
170 }
171
172 void
write_nalu_end(d3d12_video_encoder_bitstream * pNALU)173 d3d12_video_nalu_writer_h264::write_nalu_end(d3d12_video_encoder_bitstream *pNALU)
174 {
175 pNALU->flush();
176 pNALU->set_start_code_prevention(FALSE);
177 int32_t iNALUnitLen = pNALU->get_byte_count();
178
179 if (FALSE == pNALU->m_bBufferOverflow && 0x00 == pNALU->get_bitstream_buffer()[iNALUnitLen - 1]) {
180 pNALU->put_bits(8, 0x03);
181 pNALU->flush();
182 }
183 }
184
185 uint32_t
wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream * pNALU,d3d12_video_encoder_bitstream * pRBSP,uint32_t iNaluIdc,uint32_t iNaluType)186 d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
187 d3d12_video_encoder_bitstream *pRBSP,
188 uint32_t iNaluIdc,
189 uint32_t iNaluType)
190 {
191 bool isAligned = pRBSP->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
192 assert(isAligned);
193
194 int32_t iBytesWritten = pNALU->get_byte_count();
195
196 pNALU->set_start_code_prevention(FALSE);
197
198 // NAL start code
199 pNALU->put_bits(24, 0);
200 pNALU->put_bits(8, 1);
201
202 // NAL header
203 pNALU->put_bits(1, 0);
204 pNALU->put_bits(2, iNaluIdc);
205 pNALU->put_bits(5, iNaluType);
206 pNALU->flush();
207
208 // NAL body
209 pRBSP->flush();
210
211 if (pRBSP->get_start_code_prevention_status()) {
212 // Direct copying.
213 pNALU->append_byte_stream(pRBSP);
214 } else {
215 // Copy with start code prevention.
216 pNALU->set_start_code_prevention(TRUE);
217 int32_t iLength = pRBSP->get_byte_count();
218 uint8_t *pBuffer = pRBSP->get_bitstream_buffer();
219
220 for (int32_t i = 0; i < iLength; i++) {
221 pNALU->put_bits(8, pBuffer[i]);
222 }
223 }
224
225 isAligned = pNALU->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
226 assert(isAligned);
227 write_nalu_end(pNALU);
228
229 pNALU->flush();
230
231 iBytesWritten = pNALU->get_byte_count() - iBytesWritten;
232 return (uint32_t) iBytesWritten;
233 }
234
235 void
sps_to_nalu_bytes(H264_SPS * pSPS,std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)236 d3d12_video_nalu_writer_h264::sps_to_nalu_bytes(H264_SPS * pSPS,
237 std::vector<uint8_t> & headerBitstream,
238 std::vector<uint8_t>::iterator placingPositionStart,
239 size_t & writtenBytes)
240 {
241 // Wrap SPS into NALU and copy full NALU into output byte array
242 d3d12_video_encoder_bitstream rbsp, nalu;
243
244 if (!rbsp.create_bitstream(MAX_COMPRESSED_SPS)) {
245 debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_SPS) failed\n");
246 assert(false);
247 }
248
249 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_SPS)) {
250 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_SPS) failed\n");
251 assert(false);
252 }
253
254 rbsp.set_start_code_prevention(TRUE);
255 if (write_sps_bytes(&rbsp, pSPS) <= 0u) {
256 debug_printf("write_sps_bytes(&rbsp, pSPS) didn't write any bytes.\n");
257 assert(false);
258 }
259
260 if (wrap_sps_nalu(&nalu, &rbsp) <= 0u) {
261 debug_printf("wrap_sps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
262 assert(false);
263 }
264
265 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
266 // memory.
267 uint8_t *naluBytes = nalu.get_bitstream_buffer();
268 size_t naluByteSize = nalu.get_byte_count();
269
270 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
271 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
272 headerBitstream.resize(startDstIndex + naluByteSize);
273 }
274
275 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
276
277 writtenBytes = naluByteSize;
278 }
279
280 void
pps_to_nalu_bytes(H264_PPS * pPPS,std::vector<uint8_t> & headerBitstream,BOOL bIsHighProfile,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)281 d3d12_video_nalu_writer_h264::pps_to_nalu_bytes(H264_PPS * pPPS,
282 std::vector<uint8_t> & headerBitstream,
283 BOOL bIsHighProfile,
284 std::vector<uint8_t>::iterator placingPositionStart,
285 size_t & writtenBytes)
286 {
287 // Wrap PPS into NALU and copy full NALU into output byte array
288 d3d12_video_encoder_bitstream rbsp, nalu;
289 if (!rbsp.create_bitstream(MAX_COMPRESSED_PPS)) {
290 debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_PPS) failed\n");
291 assert(false);
292 }
293
294 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
295 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
296 assert(false);
297 }
298
299 rbsp.set_start_code_prevention(TRUE);
300
301 if (write_pps_bytes(&rbsp, pPPS, bIsHighProfile) <= 0u) {
302 debug_printf("write_pps_bytes(&rbsp, pPPS, bIsHighProfile) didn't write any bytes.\n");
303 assert(false);
304 }
305
306 if (wrap_pps_nalu(&nalu, &rbsp) <= 0u) {
307 debug_printf("wrap_pps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
308 assert(false);
309 }
310
311 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
312 // memory.
313 uint8_t *naluBytes = nalu.get_bitstream_buffer();
314 size_t naluByteSize = nalu.get_byte_count();
315
316 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
317 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
318 headerBitstream.resize(startDstIndex + naluByteSize);
319 }
320
321 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
322
323 writtenBytes = naluByteSize;
324 }
325
326 void
write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)327 d3d12_video_nalu_writer_h264::write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
328 std::vector<uint8_t>::iterator placingPositionStart,
329 size_t & writtenBytes)
330 {
331 d3d12_video_encoder_bitstream rbsp, nalu;
332 if (!rbsp.create_bitstream(8)) {
333 debug_printf("rbsp.create_bitstream(8) failed\n");
334 assert(false);
335 }
336 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
337 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
338 assert(false);
339 }
340
341 rbsp.set_start_code_prevention(TRUE);
342 if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) <= 0u) {
343 debug_printf(
344 "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) didn't write any bytes.\n");;
345 assert(false);
346 }
347
348 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
349 // memory.
350 uint8_t *naluBytes = nalu.get_bitstream_buffer();
351 size_t naluByteSize = nalu.get_byte_count();
352
353 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
354 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
355 headerBitstream.resize(startDstIndex + naluByteSize);
356 }
357
358 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
359
360 writtenBytes = naluByteSize;
361 }
362
363 void
write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)364 d3d12_video_nalu_writer_h264::write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
365 std::vector<uint8_t>::iterator placingPositionStart,
366 size_t & writtenBytes)
367 {
368 d3d12_video_encoder_bitstream rbsp, nalu;
369 if (!rbsp.create_bitstream(8)) {
370 debug_printf("rbsp.create_bitstream(8) failed.\n");
371 assert(false);
372 }
373
374 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
375 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
376 assert(false);
377 }
378
379 rbsp.set_start_code_prevention(TRUE);
380 if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) <= 0u) {
381
382 debug_printf(
383 "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) didn't write any bytes.\n");
384 assert(false);
385 }
386
387 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
388 // memory.
389 uint8_t *naluBytes = nalu.get_bitstream_buffer();
390 size_t naluByteSize = nalu.get_byte_count();
391
392 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
393 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
394 headerBitstream.resize(startDstIndex + naluByteSize);
395 }
396
397 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
398
399 writtenBytes = naluByteSize;
400 }
401