• 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 "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 }