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 ASSERTED 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(6, pSPS->constraint_set_flags);
52 pBitstream->put_bits(2, 0); // reserved_zero_2bits
53 pBitstream->put_bits(8, pSPS->level_idc);
54 pBitstream->exp_Golomb_ue(pSPS->seq_parameter_set_id);
55
56 // If adding new profile support, check that the chroma_format_idc and bit depth are set correctly below
57 // for the new additions
58 assert((pSPS->profile_idc == H264_PROFILE_MAIN) ||
59 (pSPS->profile_idc == H264_PROFILE_HIGH) ||
60 (pSPS->profile_idc == H264_PROFILE_HIGH10) ||
61 (pSPS->profile_idc == H264_PROFILE_BASELINE) ||
62 (pSPS->profile_idc == H264_PROFILE_CONSTRAINED_BASELINE));
63
64 if ((pSPS->profile_idc == H264_PROFILE_HIGH) || (pSPS->profile_idc == H264_PROFILE_HIGH10)) {
65 // chroma_format_idc always 4.2.0
66 pBitstream->exp_Golomb_ue(1);
67 // Assume no separate_colour_plane_flag given chroma_format_idc = 1
68 pBitstream->exp_Golomb_ue(pSPS->bit_depth_luma_minus8);
69 pBitstream->exp_Golomb_ue(pSPS->bit_depth_chroma_minus8);
70 // qpprime_y_zero_transform_bypass_flag
71 pBitstream->put_bits(1, 0);
72 // seq_scaling_matrix_present_flag)
73 pBitstream->put_bits(1, 0);
74 }
75
76 pBitstream->exp_Golomb_ue(pSPS->log2_max_frame_num_minus4);
77
78 pBitstream->exp_Golomb_ue(pSPS->pic_order_cnt_type);
79 if (pSPS->pic_order_cnt_type == 0) {
80 pBitstream->exp_Golomb_ue(pSPS->log2_max_pic_order_cnt_lsb_minus4);
81 }
82 pBitstream->exp_Golomb_ue(pSPS->max_num_ref_frames);
83 pBitstream->put_bits(1, pSPS->gaps_in_frame_num_value_allowed_flag);
84 pBitstream->exp_Golomb_ue(pSPS->pic_width_in_mbs_minus1);
85 pBitstream->exp_Golomb_ue(pSPS->pic_height_in_map_units_minus1);
86
87 // No support for interlace in D3D12 Video Encode
88 // frame_mbs_only_flag coded as 1
89 pBitstream->put_bits(1, 1); // frame_mbs_only_flag
90 pBitstream->put_bits(1, pSPS->direct_8x8_inference_flag);
91
92 // no cropping
93 pBitstream->put_bits(1, pSPS->frame_cropping_flag); // frame_cropping_flag
94 if (pSPS->frame_cropping_flag) {
95 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_left_offset);
96 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_right_offset);
97 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_top_offset);
98 pBitstream->exp_Golomb_ue(pSPS->frame_cropping_rect_bottom_offset);
99 }
100
101 pBitstream->put_bits(1, pSPS->vui_parameters_present_flag);
102 if (pSPS->vui_parameters_present_flag)
103 {
104 pBitstream->put_bits(1, pSPS->vui.aspect_ratio_info_present_flag);
105 if (pSPS->vui.aspect_ratio_info_present_flag) {
106 pBitstream->put_bits(8, pSPS->vui.aspect_ratio_idc);
107 if (pSPS->vui.aspect_ratio_idc == 255 /*EXTENDED_SAR*/) {
108 pBitstream->put_bits(16, pSPS->vui.sar_width);
109 pBitstream->put_bits(16, pSPS->vui.sar_height);
110 }
111 }
112
113 pBitstream->put_bits(1, pSPS->vui.overscan_info_present_flag);
114 if (pSPS->vui.overscan_info_present_flag) {
115 pBitstream->put_bits(1, pSPS->vui.overscan_appropriate_flag);
116 }
117
118 pBitstream->put_bits(1, pSPS->vui.video_signal_type_present_flag);
119 if (pSPS->vui.video_signal_type_present_flag) {
120 pBitstream->put_bits(3, pSPS->vui.video_format);
121 pBitstream->put_bits(1, pSPS->vui.video_full_range_flag);
122 pBitstream->put_bits(1, pSPS->vui.colour_description_present_flag);
123 if (pSPS->vui.colour_description_present_flag) {
124 pBitstream->put_bits(8, pSPS->vui.colour_primaries);
125 pBitstream->put_bits(8, pSPS->vui.transfer_characteristics);
126 pBitstream->put_bits(8, pSPS->vui.matrix_coefficients);
127 }
128 }
129
130 pBitstream->put_bits(1, pSPS->vui.chroma_loc_info_present_flag);
131 if (pSPS->vui.chroma_loc_info_present_flag) {
132 pBitstream->exp_Golomb_ue(pSPS->vui.chroma_sample_loc_type_top_field);
133 pBitstream->exp_Golomb_ue(pSPS->vui.chroma_sample_loc_type_bottom_field);
134 }
135
136 pBitstream->put_bits(1, pSPS->vui.timing_info_present_flag);
137 if (pSPS->vui.timing_info_present_flag) {
138 pBitstream->put_bits(16, pSPS->vui.num_units_in_tick >> 16);
139 pBitstream->put_bits(16, pSPS->vui.num_units_in_tick & 0xffff);
140 pBitstream->put_bits(16, pSPS->vui.time_scale >> 16);
141 pBitstream->put_bits(16, pSPS->vui.time_scale & 0xffff);
142 pBitstream->put_bits(1, pSPS->vui.fixed_frame_rate_flag);
143 }
144
145 pBitstream->put_bits(1, pSPS->vui.nal_hrd_parameters_present_flag);
146 if (pSPS->vui.nal_hrd_parameters_present_flag) {
147 write_hrd(pBitstream, &pSPS->vui.nal_hrd_parameters);
148 }
149
150 pBitstream->put_bits(1, pSPS->vui.vcl_hrd_parameters_present_flag);
151 if (pSPS->vui.vcl_hrd_parameters_present_flag) {
152 write_hrd(pBitstream, &pSPS->vui.vcl_hrd_parameters);
153 }
154
155 if (pSPS->vui.nal_hrd_parameters_present_flag || pSPS->vui.vcl_hrd_parameters_present_flag) {
156 pBitstream->put_bits(1, pSPS->vui.low_delay_hrd_flag);
157 }
158
159 pBitstream->put_bits(1, pSPS->vui.pic_struct_present_flag);
160 pBitstream->put_bits(1, pSPS->vui.bitstream_restriction_flag);
161 if (pSPS->vui.bitstream_restriction_flag) {
162 pBitstream->put_bits(1, pSPS->vui.motion_vectors_over_pic_boundaries_flag);
163 pBitstream->exp_Golomb_ue(pSPS->vui.max_bytes_per_pic_denom);
164 pBitstream->exp_Golomb_ue(pSPS->vui.max_bits_per_mb_denom);
165 pBitstream->exp_Golomb_ue(pSPS->vui.log2_max_mv_length_horizontal);
166 pBitstream->exp_Golomb_ue(pSPS->vui.log2_max_mv_length_vertical);
167 pBitstream->exp_Golomb_ue(pSPS->vui.num_reorder_frames);
168 pBitstream->exp_Golomb_ue(pSPS->vui.max_dec_frame_buffering);
169 }
170 }
171
172 rbsp_trailing(pBitstream);
173 pBitstream->flush();
174
175 iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
176 return (uint32_t) iBytesWritten;
177 }
178
179 void
write_hrd(d3d12_video_encoder_bitstream * pBitstream,H264_HRD_PARAMS * pHrd)180 d3d12_video_nalu_writer_h264::write_hrd(d3d12_video_encoder_bitstream *pBitstream, H264_HRD_PARAMS *pHrd)
181 {
182 pBitstream->exp_Golomb_ue(pHrd->cpb_cnt_minus1);
183 pBitstream->put_bits(4, pHrd->bit_rate_scale);
184 pBitstream->put_bits(4, pHrd->cpb_size_scale);
185 for (uint32_t i = 0; i <= pHrd->cpb_cnt_minus1; i++) {
186 pBitstream->exp_Golomb_ue(pHrd->bit_rate_value_minus1[i]);
187 pBitstream->exp_Golomb_ue(pHrd->cpb_size_value_minus1[i]);
188 pBitstream->put_bits(1, pHrd->cbr_flag[i]);
189 }
190 pBitstream->put_bits(5, pHrd->initial_cpb_removal_delay_length_minus1);
191 pBitstream->put_bits(5, pHrd->cpb_removal_delay_length_minus1);
192 pBitstream->put_bits(5, pHrd->dpb_output_delay_length_minus1);
193 pBitstream->put_bits(5, pHrd->time_offset_length);
194 }
195
196 uint32_t
write_pps_bytes(d3d12_video_encoder_bitstream * pBitstream,H264_PPS * pPPS,BOOL bIsHighProfile)197 d3d12_video_nalu_writer_h264::write_pps_bytes(d3d12_video_encoder_bitstream *pBitstream,
198 H264_PPS * pPPS,
199 BOOL bIsHighProfile)
200 {
201 int32_t iBytesWritten = pBitstream->get_byte_count();
202
203 // Standard constraint to be between 0 and 31 inclusive
204 assert(pPPS->seq_parameter_set_id >= 0);
205 assert(pPPS->seq_parameter_set_id < 32);
206
207 // Standard constraint to be between 0 and 255 inclusive
208 assert(pPPS->pic_parameter_set_id >= 0);
209 assert(pPPS->pic_parameter_set_id < 256);
210
211 pBitstream->exp_Golomb_ue(pPPS->pic_parameter_set_id);
212 pBitstream->exp_Golomb_ue(pPPS->seq_parameter_set_id);
213 pBitstream->put_bits(1, pPPS->entropy_coding_mode_flag);
214 pBitstream->put_bits(1, pPPS->pic_order_present_flag); // bottom_field_pic_order_in_frame_present_flag
215 pBitstream->exp_Golomb_ue(0); // num_slice_groups_minus1
216
217
218 pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l0_active_minus1);
219 pBitstream->exp_Golomb_ue(pPPS->num_ref_idx_l1_active_minus1);
220 pBitstream->put_bits(1, 0); // weighted_pred_flag
221 pBitstream->put_bits(2, 0); // weighted_bipred_idc
222 pBitstream->exp_Golomb_se(0); // pic_init_qp_minus26
223 pBitstream->exp_Golomb_se(0); // pic_init_qs_minus26
224 pBitstream->exp_Golomb_se(0); // chroma_qp_index_offset
225 pBitstream->put_bits(1, 1); // deblocking_filter_control_present_flag
226 pBitstream->put_bits(1, pPPS->constrained_intra_pred_flag);
227 pBitstream->put_bits(1, 0); // redundant_pic_cnt_present_flag
228
229 if (bIsHighProfile) {
230 pBitstream->put_bits(1, pPPS->transform_8x8_mode_flag);
231 pBitstream->put_bits(1, 0); // pic_scaling_matrix_present_flag
232 pBitstream->exp_Golomb_se(0); // second_chroma_qp_index_offset
233 }
234
235 rbsp_trailing(pBitstream);
236 pBitstream->flush();
237
238 iBytesWritten = pBitstream->get_byte_count() - iBytesWritten;
239 return (uint32_t) iBytesWritten;
240 }
241
242 uint32_t
wrap_sps_nalu(d3d12_video_encoder_bitstream * pNALU,d3d12_video_encoder_bitstream * pRBSP)243 d3d12_video_nalu_writer_h264::wrap_sps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
244 {
245 return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_SPS);
246 }
247
248 uint32_t
wrap_pps_nalu(d3d12_video_encoder_bitstream * pNALU,d3d12_video_encoder_bitstream * pRBSP)249 d3d12_video_nalu_writer_h264::wrap_pps_nalu(d3d12_video_encoder_bitstream *pNALU, d3d12_video_encoder_bitstream *pRBSP)
250 {
251 return wrap_rbsp_into_nalu(pNALU, pRBSP, NAL_REFIDC_REF, NAL_TYPE_PPS);
252 }
253
254 void
write_nalu_end(d3d12_video_encoder_bitstream * pNALU)255 d3d12_video_nalu_writer_h264::write_nalu_end(d3d12_video_encoder_bitstream *pNALU)
256 {
257 pNALU->flush();
258 pNALU->set_start_code_prevention(false);
259 int32_t iNALUnitLen = pNALU->get_byte_count();
260
261 if (false == pNALU->m_bBufferOverflow && 0x00 == pNALU->get_bitstream_buffer()[iNALUnitLen - 1]) {
262 pNALU->put_bits(8, 0x03);
263 pNALU->flush();
264 }
265 }
266
267 uint32_t
wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream * pNALU,d3d12_video_encoder_bitstream * pRBSP,uint32_t iNaluIdc,uint32_t iNaluType,const H264_SLICE_PREFIX_SVC * pSvcExtendedHeader)268 d3d12_video_nalu_writer_h264::wrap_rbsp_into_nalu(d3d12_video_encoder_bitstream *pNALU,
269 d3d12_video_encoder_bitstream *pRBSP,
270 uint32_t iNaluIdc,
271 uint32_t iNaluType,
272 const H264_SLICE_PREFIX_SVC* pSvcExtendedHeader)
273 {
274 bool isAligned = pRBSP->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
275 assert(isAligned);
276
277 int32_t iBytesWritten = pNALU->get_byte_count();
278
279 pNALU->set_start_code_prevention(false);
280
281 // NAL start code
282 pNALU->put_bits(24, 0);
283 pNALU->put_bits(8, 1);
284
285 // NAL header
286 pNALU->put_bits(1, 0);
287 pNALU->put_bits(2, iNaluIdc);
288 pNALU->put_bits(5, iNaluType);
289
290 if (iNaluType == NAL_TYPE_PREFIX)
291 {
292 assert(pSvcExtendedHeader);
293 pNALU->put_bits(1, 1); // svc_extension_flag u(1)
294 // nal_unit_header_svc_extension( )
295 pNALU->put_bits(1, pSvcExtendedHeader->idr_flag);
296 pNALU->put_bits(6, pSvcExtendedHeader->priority_id);
297 pNALU->put_bits(1, pSvcExtendedHeader->no_inter_layer_pred_flag);
298 pNALU->put_bits(3, pSvcExtendedHeader->dependency_id);
299 pNALU->put_bits(4, pSvcExtendedHeader->quality_id);
300 pNALU->put_bits(3, pSvcExtendedHeader->temporal_id);
301 pNALU->put_bits(1, pSvcExtendedHeader->use_ref_base_pic_flag);
302 pNALU->put_bits(1, pSvcExtendedHeader->discardable_flag);
303 pNALU->put_bits(1, pSvcExtendedHeader->output_flag);
304 pNALU->put_bits(2, 3 /* reserved_three_2bits */);
305 }
306
307 pNALU->flush();
308
309 // NAL body
310 pRBSP->flush();
311
312 if (pRBSP->get_start_code_prevention_status()) {
313 // Direct copying.
314 pNALU->append_byte_stream(pRBSP);
315 } else {
316 // Copy with start code prevention.
317 pNALU->set_start_code_prevention(true);
318 int32_t iLength = pRBSP->get_byte_count();
319 uint8_t *pBuffer = pRBSP->get_bitstream_buffer();
320
321 for (int32_t i = 0; i < iLength; i++) {
322 pNALU->put_bits(8, pBuffer[i]);
323 }
324 }
325
326 isAligned = pNALU->is_byte_aligned(); // causes side-effects in object state, don't put inside assert()
327 assert(isAligned);
328 write_nalu_end(pNALU);
329
330 pNALU->flush();
331
332 iBytesWritten = pNALU->get_byte_count() - iBytesWritten;
333 return (uint32_t) iBytesWritten;
334 }
335
336 void
sps_to_nalu_bytes(H264_SPS * pSPS,std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)337 d3d12_video_nalu_writer_h264::sps_to_nalu_bytes(H264_SPS * pSPS,
338 std::vector<uint8_t> & headerBitstream,
339 std::vector<uint8_t>::iterator placingPositionStart,
340 size_t & writtenBytes)
341 {
342 // Wrap SPS into NALU and copy full NALU into output byte array
343 d3d12_video_encoder_bitstream rbsp, nalu;
344
345 if (!rbsp.create_bitstream(MAX_COMPRESSED_SPS)) {
346 debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_SPS) failed\n");
347 assert(false);
348 }
349
350 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_SPS)) {
351 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_SPS) failed\n");
352 assert(false);
353 }
354
355 rbsp.set_start_code_prevention(true);
356 if (write_sps_bytes(&rbsp, pSPS) <= 0u) {
357 debug_printf("write_sps_bytes(&rbsp, pSPS) didn't write any bytes.\n");
358 assert(false);
359 }
360
361 if (wrap_sps_nalu(&nalu, &rbsp) <= 0u) {
362 debug_printf("wrap_sps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
363 assert(false);
364 }
365
366 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
367 // memory.
368 uint8_t *naluBytes = nalu.get_bitstream_buffer();
369 size_t naluByteSize = nalu.get_byte_count();
370
371 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
372 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
373 headerBitstream.resize(startDstIndex + naluByteSize);
374 }
375
376 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
377
378 writtenBytes = naluByteSize;
379 }
380
381 void
pps_to_nalu_bytes(H264_PPS * pPPS,std::vector<uint8_t> & headerBitstream,BOOL bIsHighProfile,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)382 d3d12_video_nalu_writer_h264::pps_to_nalu_bytes(H264_PPS * pPPS,
383 std::vector<uint8_t> & headerBitstream,
384 BOOL bIsHighProfile,
385 std::vector<uint8_t>::iterator placingPositionStart,
386 size_t & writtenBytes)
387 {
388 // Wrap PPS into NALU and copy full NALU into output byte array
389 d3d12_video_encoder_bitstream rbsp, nalu;
390 if (!rbsp.create_bitstream(MAX_COMPRESSED_PPS)) {
391 debug_printf("rbsp.create_bitstream(MAX_COMPRESSED_PPS) failed\n");
392 assert(false);
393 }
394
395 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
396 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
397 assert(false);
398 }
399
400 rbsp.set_start_code_prevention(true);
401
402 if (write_pps_bytes(&rbsp, pPPS, bIsHighProfile) <= 0u) {
403 debug_printf("write_pps_bytes(&rbsp, pPPS, bIsHighProfile) didn't write any bytes.\n");
404 assert(false);
405 }
406
407 if (wrap_pps_nalu(&nalu, &rbsp) <= 0u) {
408 debug_printf("wrap_pps_nalu(&nalu, &rbsp) didn't write any bytes.\n");
409 assert(false);
410 }
411
412 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
413 // memory.
414 uint8_t *naluBytes = nalu.get_bitstream_buffer();
415 size_t naluByteSize = nalu.get_byte_count();
416
417 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
418 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
419 headerBitstream.resize(startDstIndex + naluByteSize);
420 }
421
422 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
423
424 writtenBytes = naluByteSize;
425 }
426
427 void
write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)428 d3d12_video_nalu_writer_h264::write_end_of_stream_nalu(std::vector<uint8_t> & headerBitstream,
429 std::vector<uint8_t>::iterator placingPositionStart,
430 size_t & writtenBytes)
431 {
432 d3d12_video_encoder_bitstream rbsp, nalu;
433 if (!rbsp.create_bitstream(8)) {
434 debug_printf("rbsp.create_bitstream(8) failed\n");
435 assert(false);
436 }
437 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
438 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed\n");
439 assert(false);
440 }
441
442 rbsp.set_start_code_prevention(true);
443 if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) <= 0u) {
444 debug_printf(
445 "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_STREAM) didn't write any bytes.\n");;
446 assert(false);
447 }
448
449 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
450 // memory.
451 uint8_t *naluBytes = nalu.get_bitstream_buffer();
452 size_t naluByteSize = nalu.get_byte_count();
453
454 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
455 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
456 headerBitstream.resize(startDstIndex + naluByteSize);
457 }
458
459 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
460
461 writtenBytes = naluByteSize;
462 }
463
464 void
write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)465 d3d12_video_nalu_writer_h264::write_end_of_sequence_nalu(std::vector<uint8_t> & headerBitstream,
466 std::vector<uint8_t>::iterator placingPositionStart,
467 size_t & writtenBytes)
468 {
469 d3d12_video_encoder_bitstream rbsp, nalu;
470 if (!rbsp.create_bitstream(8)) {
471 debug_printf("rbsp.create_bitstream(8) failed.\n");
472 assert(false);
473 }
474
475 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
476 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
477 assert(false);
478 }
479
480 rbsp.set_start_code_prevention(true);
481 if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) <= 0u) {
482
483 debug_printf(
484 "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_END_OF_SEQUENCE) didn't write any bytes.\n");
485 assert(false);
486 }
487
488 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
489 // memory.
490 uint8_t *naluBytes = nalu.get_bitstream_buffer();
491 size_t naluByteSize = nalu.get_byte_count();
492
493 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
494 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
495 headerBitstream.resize(startDstIndex + naluByteSize);
496 }
497
498 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
499
500 writtenBytes = naluByteSize;
501 }
502
503 void
write_access_unit_delimiter_nalu(std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)504 d3d12_video_nalu_writer_h264::write_access_unit_delimiter_nalu(std::vector<uint8_t> & headerBitstream,
505 std::vector<uint8_t>::iterator placingPositionStart,
506 size_t & writtenBytes)
507 {
508 d3d12_video_encoder_bitstream rbsp, nalu;
509 if (!rbsp.create_bitstream(8)) {
510 debug_printf("rbsp.create_bitstream(8) failed.\n");
511 assert(false);
512 }
513
514 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
515 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
516 assert(false);
517 }
518
519 rbsp.set_start_code_prevention(true);
520 rbsp.put_bits(3, 2/*primary_pic_type*/);
521 rbsp_trailing(&rbsp);
522 rbsp.flush();
523 if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_NONREF, NAL_TYPE_ACCESS_UNIT_DELIMITER) <= 0u) {
524
525 debug_printf(
526 "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_NONREF, NAL_TYPE_ACCESS_UNIT_DELIMITER) didn't write any bytes.\n");
527 assert(false);
528 }
529
530 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
531 // memory.
532 uint8_t *naluBytes = nalu.get_bitstream_buffer();
533 size_t naluByteSize = nalu.get_byte_count();
534
535 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
536 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
537 headerBitstream.resize(startDstIndex + naluByteSize);
538 }
539
540 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
541
542 writtenBytes = naluByteSize;
543 }
544
545 void
write_sei_nalu(H264_SEI_MESSAGE sei_message,std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)546 d3d12_video_nalu_writer_h264::write_sei_nalu(H264_SEI_MESSAGE sei_message,
547 std::vector<uint8_t> & headerBitstream,
548 std::vector<uint8_t>::iterator placingPositionStart,
549 size_t & writtenBytes)
550 {
551 // Fill byte buffer with sei_message() payload
552 d3d12_video_encoder_bitstream sei_payload_bitstream;
553 if (!sei_payload_bitstream.create_bitstream(2 * sizeof(H264_SEI_MESSAGE))) {
554 debug_printf("sei_payload_bitstream.create_bitstream(2 * sizeof(H264_SEI_MESSAGE) failed.\n");
555 assert(false);
556 }
557
558 switch (sei_message.payload_type)
559 {
560 case H264_SEI_SCALABILITY_INFO:
561 {
562 sei_payload_bitstream.put_bits(1, 0); // temporal_id_nesting_flag
563 sei_payload_bitstream.put_bits(1, 0); // priority_layer_info_present_flag
564 sei_payload_bitstream.put_bits(1, 0); // priority_id_setting_flag
565 sei_payload_bitstream.exp_Golomb_ue(sei_message.scalability_info.num_layers_minus1);
566 for (uint32_t i = 0; i <= sei_message.scalability_info.num_layers_minus1; i++)
567 {
568 sei_payload_bitstream.exp_Golomb_ue(i); // layer_id[i]
569 sei_payload_bitstream.put_bits(6, 0); // priority_id[i]
570 sei_payload_bitstream.put_bits(1, 0); // discardable_flag[i]
571 sei_payload_bitstream.put_bits(3, 0); // dependency_id[i]
572 sei_payload_bitstream.put_bits(4, 0); // quality_id[i]
573 sei_payload_bitstream.put_bits(3, sei_message.scalability_info.temporal_id[i]); // temporal_id[i]
574 sei_payload_bitstream.put_bits(1, 0); // sub_pic_layer_flag[i]
575 sei_payload_bitstream.put_bits(1, 0); // sub_region_layer_flag[i]
576 sei_payload_bitstream.put_bits(1, 0); // iroi_division_info_present_flag[i]
577 sei_payload_bitstream.put_bits(1, 0); // profile_level_info_present_flag[i]
578 sei_payload_bitstream.put_bits(1, 0); // bitrate_info_present_flag[i]
579 sei_payload_bitstream.put_bits(1, 0); // frm_rate_info_present_flag[i]
580 sei_payload_bitstream.put_bits(1, 0); // frm_size_info_present_flag[i]
581 sei_payload_bitstream.put_bits(1, 0); // layer_dependency_info_present_flag[i]
582 sei_payload_bitstream.put_bits(1, 0); // parameter_sets_info_present_flag[i]
583 sei_payload_bitstream.put_bits(1, 0); // bitstream_restriction_info_present_flag[i]
584 sei_payload_bitstream.put_bits(1, 0); // exact_inter_layer_pred_flag[i]
585 sei_payload_bitstream.put_bits(1, 0); // layer_conversion_flag[i]
586 sei_payload_bitstream.put_bits(1, 0); // layer_output_flag[i]
587 sei_payload_bitstream.exp_Golomb_ue(0); // layer_dependency_info_src_layer_id_delta [i]
588 sei_payload_bitstream.exp_Golomb_ue(0); // parameter_sets_info_src_layer_id_delta [i]
589 }
590 } break;
591 default:
592 debug_printf("[d3d12_video_nalu_writer_h264::write_sei_nalu] Unsupported sei_message.payload_type.\n");
593 assert(false);
594 return;
595 break;
596 }
597
598 // Add trailing bits after sei_message() bits in sei_payload_bitstream and flush
599 if(!sei_payload_bitstream.is_byte_aligned())
600 rbsp_trailing(&sei_payload_bitstream);
601 sei_payload_bitstream.flush();
602
603 // Set payload_size from bitstream data written
604 uint32_t payload_size = sei_payload_bitstream.get_byte_count();
605
606 //
607 // Wrap sei_payload_bitstream in RBSP and NALU
608 //
609
610 d3d12_video_encoder_bitstream rbsp, nalu;
611 if (!rbsp.create_bitstream(2 * sizeof(H264_SEI_MESSAGE))) {
612 debug_printf("rbsp.create_bitstream(2 * sizeof(H264_SEI_MESSAGE)) failed.\n");
613 assert(false);
614 }
615
616 if (!nalu.create_bitstream(2 * sizeof(H264_SEI_MESSAGE))) {
617 debug_printf("nalu.create_bitstream(2 * sizeof(H264_SEI_MESSAGE)) failed.\n");
618 assert(false);
619 }
620
621 rbsp.set_start_code_prevention(true);
622
623 //
624 // Write payload_type to bitstream
625 //
626 uint32_t payload_type = static_cast<uint32_t>(sei_message.payload_type);
627 while(payload_type >= 255)
628 {
629 rbsp.put_bits(8, 255 /* payload_type */);
630 payload_type -= 255;
631 }
632 rbsp.put_bits(8, payload_type);
633
634 //
635 // Write payload_size to bitstream
636 //
637 while(payload_size >= 255)
638 {
639 rbsp.put_bits(8, 255 /* payload_size */);
640 payload_size -= 255;
641 }
642 rbsp.put_bits(8, payload_size);
643
644 rbsp.flush();
645 rbsp.append_byte_stream(&sei_payload_bitstream);
646
647 rbsp_trailing(&rbsp);
648 rbsp.flush();
649 if (wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_NONREF, NAL_TYPE_SEI) <= 0u) {
650
651 debug_printf(
652 "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_NONREF, NAL_TYPE_ACCESS_UNIT_DELIMITER) didn't write any bytes.\n");
653 assert(false);
654 }
655
656 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
657 // memory.
658 uint8_t *naluBytes = nalu.get_bitstream_buffer();
659 size_t naluByteSize = nalu.get_byte_count();
660
661 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
662 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
663 headerBitstream.resize(startDstIndex + naluByteSize);
664 }
665
666 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
667
668 writtenBytes = naluByteSize;
669 }
670
671 void
write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix,std::vector<uint8_t> & headerBitstream,std::vector<uint8_t>::iterator placingPositionStart,size_t & writtenBytes)672 d3d12_video_nalu_writer_h264::write_slice_svc_prefix(const H264_SLICE_PREFIX_SVC & nal_svc_prefix,
673 std::vector<uint8_t> & headerBitstream,
674 std::vector<uint8_t>::iterator placingPositionStart,
675 size_t & writtenBytes)
676 {
677 d3d12_video_encoder_bitstream rbsp, nalu;
678 if (!rbsp.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
679 debug_printf("rbsp.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
680 assert(false);
681 }
682
683 if (!nalu.create_bitstream(2 * MAX_COMPRESSED_PPS)) {
684 debug_printf("nalu.create_bitstream(2 * MAX_COMPRESSED_PPS) failed.\n");
685 assert(false);
686 }
687
688 rbsp.set_start_code_prevention(true);
689
690 // prefix_nal_unit_svc ( )
691 if (nal_svc_prefix.nal_ref_idc == NAL_REFIDC_REF)
692 {
693 rbsp.put_bits(1, nal_svc_prefix.store_ref_base_pic_flag);
694 rbsp.put_bits(1, 0 /* additional_prefix_nal_unit_extension_flag */);
695 }
696 else
697 {
698 // No more_rbsp_data( ) so we don't need to code anything else
699 }
700
701 rbsp_trailing(&rbsp);
702 rbsp.flush();
703 if (wrap_rbsp_into_nalu(&nalu, &rbsp, nal_svc_prefix.nal_ref_idc, NAL_TYPE_PREFIX, &nal_svc_prefix) <= 0u) {
704
705 debug_printf(
706 "wrap_rbsp_into_nalu(&nalu, &rbsp, NAL_REFIDC_REF, NAL_TYPE_ACCESS_UNIT_DELIMITER) didn't write any bytes.\n");
707 assert(false);
708 }
709
710 // Deep copy nalu into headerBitstream, nalu gets out of scope here and its destructor frees the nalu object buffer
711 // memory.
712 uint8_t *naluBytes = nalu.get_bitstream_buffer();
713 size_t naluByteSize = nalu.get_byte_count();
714
715 auto startDstIndex = std::distance(headerBitstream.begin(), placingPositionStart);
716 if (headerBitstream.size() < (startDstIndex + naluByteSize)) {
717 headerBitstream.resize(startDstIndex + naluByteSize);
718 }
719
720 std::copy_n(&naluBytes[0], naluByteSize, &headerBitstream.data()[startDstIndex]);
721
722 writtenBytes = naluByteSize;
723 }