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