OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 * | |
10 */ | |
11 | |
12 #include "webrtc/modules/rtp_rtcp/source/h264/bitstream_rewriter.h" | |
13 | |
14 #include <algorithm> | |
15 #include <memory> | |
16 | |
17 #include "webrtc/base/bitbuffer.h" | |
18 #include "webrtc/base/bytebuffer.h" | |
19 #include "webrtc/base/checks.h" | |
20 #include "webrtc/base/logging.h" | |
21 | |
22 #include "webrtc/modules/rtp_rtcp/source/h264/h264_common.h" | |
23 #include "webrtc/modules/rtp_rtcp/source/h264/sps_parser.h" | |
24 | |
25 namespace webrtc { | |
26 | |
27 // The maximum expected growth from adding a VUI to the SPS. It's actually | |
28 // closer to 24 or so, but better safe than sorry. | |
29 const size_t kMaxVuiSpsIncrease = 64; | |
30 const size_t kNaluTypeSize = 1; | |
31 | |
32 #define RETURN_FALSE_ON_FAIL(x) \ | |
33 if (!(x)) { \ | |
34 LOG_F(LS_ERROR) << " (line:" << __LINE__ << ") FAILED: " #x; \ | |
35 return false; \ | |
36 } | |
37 | |
38 #define COPY_UINT8(src, dest, tmp) \ | |
39 do { \ | |
40 RETURN_FALSE_ON_FAIL((src)->ReadUInt8(&tmp)); \ | |
41 if (dest) \ | |
42 RETURN_FALSE_ON_FAIL((dest)->WriteUInt8(tmp)); \ | |
43 } while (0) | |
44 | |
45 #define COPY_EXP_GOLOMB(src, dest, tmp) \ | |
46 do { \ | |
47 RETURN_FALSE_ON_FAIL((src)->ReadExponentialGolomb(&tmp)); \ | |
48 if (dest) \ | |
49 RETURN_FALSE_ON_FAIL((dest)->WriteExponentialGolomb(tmp)); \ | |
50 } while (0) | |
51 | |
52 #define COPY_BITS(src, dest, tmp, bits) \ | |
53 do { \ | |
54 RETURN_FALSE_ON_FAIL((src)->ReadBits(&tmp, bits)); \ | |
55 if (dest) \ | |
56 RETURN_FALSE_ON_FAIL((dest)->WriteBits(tmp, bits)); \ | |
57 } while (0) | |
58 | |
59 bool H264BitstreamRewriter::ParseAndRewriteSps(const uint8_t* buffer, | |
60 size_t length) { | |
61 if (length <= kNaluTypeSize) | |
62 return false; | |
63 static const uint8_t kTypeMask = 0x1F; | |
64 uint8_t nal_type = buffer[0] & kTypeMask; | |
65 if (nal_type != H264Common::kSps) { | |
66 LOG(LS_ERROR) << "NALU is not an SPS."; | |
67 return false; | |
68 } | |
69 // Parse out the SPS RBSP. It should be small, so it's ok that we create a | |
70 // copy. We'll eventually write this back. | |
71 std::unique_ptr<rtc::ByteBufferWriter> sps_rbsp( | |
72 H264Common::ParseRbsp(buffer + kNaluTypeSize, length - kNaluTypeSize)); | |
73 rtc::BitBuffer parsed_sps(reinterpret_cast<const uint8_t*>(sps_rbsp->Data()), | |
74 sps_rbsp->Length()); | |
75 SpsParser sps_parser; | |
76 if (!sps_parser.ParseFromRbspDecodedBitBuffer(&parsed_sps)) | |
77 return false; | |
78 | |
79 sps_state_ = sps_parser.GetState(); | |
noahric
2016/05/18 01:35:06
If you want sps_state_ to be exposed, have it as a
sprang_webrtc
2016/05/20 16:10:59
Done.
| |
80 if (sps_state_->pic_order_cnt_type >= 2) { | |
81 // No need to rewrite VUI in this case. | |
82 return false; | |
83 } | |
84 | |
85 // Create a new SPS buffer, large enough to hold our modifications. | |
86 // Also make sure these are zeroed out, since the last byte, if unaligned, | |
87 // needs to end in zeroes. | |
88 std::unique_ptr<uint8_t[]> translated_sps_rbsp( | |
89 new uint8_t[length + kMaxVuiSpsIncrease]()); | |
90 // We're going to completely muck up alignment, so we need a BitBuffer to | |
91 // write with. | |
92 rtc::BitBufferWriter sps_writer(translated_sps_rbsp.get(), | |
93 length + kMaxVuiSpsIncrease); | |
94 | |
95 // Check how far the SpsParser has read, and copy that data in bulk. | |
96 size_t byte_offset; | |
97 size_t bit_offset; | |
98 parsed_sps.GetCurrentOffset(&byte_offset, &bit_offset); | |
99 memcpy(translated_sps_rbsp.get(), sps_rbsp->Data(), | |
100 byte_offset + (bit_offset > 0 ? 1 : 0)); // OK to copy the last bits. | |
101 | |
102 // SpsParser will have read the vui_params_present flag, which we want to | |
noahric
2016/05/18 01:35:06
That's a suuuuuper dangerous coupling. I think you
sprang_webrtc
2016/05/20 16:10:59
Yeah, this was a bit iffy. Screwups should be caug
| |
103 // modify, so back off one bit; | |
104 if (bit_offset == 0) { | |
105 --byte_offset; | |
106 bit_offset = 7; | |
107 } else { | |
108 --bit_offset; | |
109 } | |
110 sps_writer.Seek(byte_offset, bit_offset); | |
111 | |
112 bool vui_updated; | |
113 if (!CopyAndRewriteVui(&parsed_sps, &sps_writer, &vui_updated)) { | |
114 LOG(LS_ERROR) << "Failed to parse/copy SPS VUI."; | |
115 return false; | |
116 } | |
117 | |
118 if (!vui_updated) | |
119 return false; | |
120 | |
121 if (!CopyRemainingBits(&parsed_sps, &sps_writer)) | |
122 return false; | |
123 | |
124 // Pad up to next byte with zeros. | |
125 sps_writer.GetCurrentOffset(&byte_offset, &bit_offset); | |
126 if (bit_offset > 0) { | |
127 sps_writer.WriteBits(0, 8 - bit_offset); | |
128 ++byte_offset; | |
129 bit_offset = 0; | |
130 } | |
131 RTC_DCHECK(byte_offset <= length + kMaxVuiSpsIncrease); | |
132 | |
133 // Parsed and rewritten successfully, so go ahead and start writing to | |
134 // destination and mark that we're rewriting the picture order count type. | |
135 // Start by writing over the NALU header. | |
136 output_buffer_.reset(new rtc::ByteBufferWriter()); | |
137 output_buffer_->WriteBytes(reinterpret_cast<const char*>(buffer), | |
138 kNaluTypeSize); | |
139 H264Common::WriteRbsp(translated_sps_rbsp.get(), byte_offset, | |
140 output_buffer_.get()); | |
141 return true; | |
142 } | |
143 | |
144 bool H264BitstreamRewriter::CopyAndRewriteVui(rtc::BitBuffer* source, | |
noahric
2016/05/18 01:35:06
If you pass sps_state as an arg, the class doesn't
sprang_webrtc
2016/05/20 16:10:59
Done.
| |
145 rtc::BitBufferWriter* destination, | |
146 bool* out_vui_rewritten) { | |
147 uint32_t golomb_tmp; | |
148 uint32_t bits_tmp; | |
149 *out_vui_rewritten = false; | |
150 | |
151 // | |
152 // vui_parameters_present_flag: u(1) | |
153 // | |
154 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); | |
155 | |
156 // ********* IMPORTANT! ********** | |
157 // Now we're at the VUI, so we want to (1) add it if it isn't present, and | |
158 // (2) rewrite frame reordering values so no reordering is allowed. | |
159 if (!sps_state_->vui_params_present) { | |
160 // Write a simple VUI with the parameters we want and 0 for all other flags. | |
161 // There are 8 flags to be off before the bitstream restriction flag. | |
162 RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 8)); | |
163 // bitstream_restriction_flag: u(1) | |
164 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); | |
165 RETURN_FALSE_ON_FAIL( | |
166 AddBitstreamRestriction(destination, sps_state_->max_num_ref_frames)); | |
167 } else { | |
168 // Parse out the full VUI. | |
169 // aspect_ratio_info_present_flag: u(1) | |
170 COPY_BITS(source, destination, bits_tmp, 1); | |
171 if (bits_tmp == 1) { | |
172 // aspect_ratio_idc: u(8) | |
173 COPY_BITS(source, destination, bits_tmp, 8); | |
174 if (bits_tmp == 255u) { // Extended_SAR | |
175 // sar_width/sar_height: u(16) each. | |
176 COPY_BITS(source, destination, bits_tmp, 32); | |
177 } | |
178 } | |
179 // overscan_info_present_flag: u(1) | |
180 COPY_BITS(source, destination, bits_tmp, 1); | |
181 if (bits_tmp == 1) { | |
182 // overscan_appropriate_flag: u(1) | |
183 COPY_BITS(source, destination, bits_tmp, 1); | |
184 } | |
185 // video_signal_type_present_flag: u(1) | |
186 COPY_BITS(source, destination, bits_tmp, 1); | |
187 if (bits_tmp == 1) { | |
188 // video_format + video_full_range_flag: u(3) + u(1) | |
189 COPY_BITS(source, destination, bits_tmp, 4); | |
190 // colour_description_present_flag: u(1) | |
191 COPY_BITS(source, destination, bits_tmp, 1); | |
192 if (bits_tmp == 1) { | |
193 // colour_primaries, transfer_characteristics, matrix_coefficients: | |
194 // u(8) each. | |
195 COPY_BITS(source, destination, bits_tmp, 24); | |
196 } | |
197 } | |
198 // chroma_loc_info_present_flag: u(1) | |
199 COPY_BITS(source, destination, bits_tmp, 1); | |
200 if (bits_tmp == 1) { | |
201 // chroma_sample_loc_type_(top|bottom)_field: ue(v) each. | |
202 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
203 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
204 } | |
205 // timing_info_present_flag: u(1) | |
206 COPY_BITS(source, destination, bits_tmp, 1); | |
207 if (bits_tmp == 1) { | |
208 // num_units_in_tick, time_scale: u(32) each | |
209 COPY_BITS(source, destination, bits_tmp, 32); | |
210 COPY_BITS(source, destination, bits_tmp, 32); | |
211 // fixed_frame_rate_flag: u(1) | |
212 COPY_BITS(source, destination, bits_tmp, 1); | |
213 } | |
214 // nal_hrd_parameters_present_flag: u(1) | |
215 uint32_t nal_hrd_parameters_present_flag; | |
216 COPY_BITS(source, destination, nal_hrd_parameters_present_flag, 1); | |
217 if (nal_hrd_parameters_present_flag == 1) { | |
218 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination)); | |
219 } | |
220 // vcl_hrd_parameters_present_flag: u(1) | |
221 uint32_t vcl_hrd_parameters_present_flag; | |
222 COPY_BITS(source, destination, vcl_hrd_parameters_present_flag, 1); | |
223 if (vcl_hrd_parameters_present_flag == 1) { | |
224 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination)); | |
225 } | |
226 if (nal_hrd_parameters_present_flag == 1 || | |
227 vcl_hrd_parameters_present_flag == 1) { | |
228 // low_delay_hrd_flag: u(1) | |
229 COPY_BITS(source, destination, bits_tmp, 1); | |
230 } | |
231 // pic_struct_present_flag: u(1) | |
232 COPY_BITS(source, destination, bits_tmp, 1); | |
233 | |
234 // bitstream_restriction_flag: u(1) | |
235 uint32_t bitstream_restriction_flag; | |
236 RETURN_FALSE_ON_FAIL(source->ReadBits(&bitstream_restriction_flag, 1)); | |
237 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); | |
238 if (bitstream_restriction_flag == 0) { | |
239 // We're adding one from scratch. | |
240 RETURN_FALSE_ON_FAIL( | |
241 AddBitstreamRestriction(destination, sps_state_->max_num_ref_frames)); | |
242 } else { | |
243 // We're replacing. | |
244 // motion_vectors_over_pic_boundaries_flag: u(1) | |
245 COPY_BITS(source, destination, bits_tmp, 1); | |
246 // max_bytes_per_pic_denom: ue(v) | |
247 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
248 // max_bits_per_mb_denom: ue(v) | |
249 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
250 // log2_max_mv_length_horizontal: ue(v) | |
251 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
252 // log2_max_mv_length_vertical: ue(v) | |
253 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
254 // ********* IMPORTANT! ********** | |
255 // The next two are the ones we need to set to low numbers: | |
256 // max_num_reorder_frames: ue(v) | |
257 // max_dec_frame_buffering: ue(v) | |
258 // However, if they are already set to no greater than the numbers we | |
259 // want, then we don't need to be rewriting. | |
260 uint32_t max_num_reorder_frames, max_dec_frame_buffering; | |
261 RETURN_FALSE_ON_FAIL( | |
262 source->ReadExponentialGolomb(&max_num_reorder_frames)); | |
263 RETURN_FALSE_ON_FAIL( | |
264 source->ReadExponentialGolomb(&max_dec_frame_buffering)); | |
265 if (max_num_reorder_frames == 0 && | |
266 max_dec_frame_buffering <= sps_state_->max_num_ref_frames) { | |
267 LOG(LS_INFO) << "VUI bitstream already contains an optimal VUI."; | |
268 return true; | |
269 } | |
270 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0)); | |
271 RETURN_FALSE_ON_FAIL( | |
272 destination->WriteExponentialGolomb(sps_state_->max_num_ref_frames)); | |
273 } | |
274 } | |
275 *out_vui_rewritten = true; | |
276 return true; | |
277 } | |
278 | |
279 // Copies a VUI HRD parameters segment. | |
280 bool H264BitstreamRewriter::CopyHrdParameters( | |
281 rtc::BitBuffer* source, | |
282 rtc::BitBufferWriter* destination) { | |
283 uint32_t golomb_tmp; | |
284 uint32_t bits_tmp; | |
285 | |
286 // cbp_cnt_minus1: ue(v) | |
287 uint32_t cbp_cnt_minus1; | |
288 COPY_EXP_GOLOMB(source, destination, cbp_cnt_minus1); | |
289 // bit_rate_scale and cbp_size_scale: u(4) each | |
290 COPY_BITS(source, destination, bits_tmp, 8); | |
291 for (size_t i = 0; i <= cbp_cnt_minus1; ++i) { | |
292 // bit_rate_value_minus1 and cbp_size_value_minus1: ue(v) each | |
293 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
294 COPY_EXP_GOLOMB(source, destination, golomb_tmp); | |
295 // cbr_flag: u(1) | |
296 COPY_BITS(source, destination, bits_tmp, 1); | |
297 } | |
298 // initial_cbp_removal_delay_length_minus1: u(5) | |
299 COPY_BITS(source, destination, bits_tmp, 5); | |
300 // cbp_removal_delay_length_minus1: u(5) | |
301 COPY_BITS(source, destination, bits_tmp, 5); | |
302 // dbp_output_delay_length_minus1: u(5) | |
303 COPY_BITS(source, destination, bits_tmp, 5); | |
304 // time_offset_length: u(5) | |
305 COPY_BITS(source, destination, bits_tmp, 5); | |
306 return true; | |
307 } | |
308 | |
309 // These functions are similar to webrtc::H264SpsParser::Parse, and based on the | |
310 // same version of the H.264 standard. You can find it here: | |
311 // http://www.itu.int/rec/T-REC-H.264 | |
312 | |
313 // Adds a bitstream restriction VUI segment. | |
314 bool H264BitstreamRewriter::AddBitstreamRestriction( | |
315 rtc::BitBufferWriter* destination, | |
316 uint32_t max_num_ref_frames) { | |
317 // motion_vectors_over_pic_boundaries_flag: u(1) | |
318 // Default is 1 when not present. | |
319 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1)); | |
320 // max_bytes_per_pic_denom: ue(v) | |
321 // Default is 2 when not present. | |
322 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(2)); | |
323 // max_bits_per_mb_denom: ue(v) | |
324 // Default is 1 when not present. | |
325 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(1)); | |
326 // log2_max_mv_length_horizontal: ue(v) | |
327 // log2_max_mv_length_vertical: ue(v) | |
328 // Both default to 16 when not present. | |
329 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16)); | |
330 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16)); | |
331 | |
332 // ********* IMPORTANT! ********** | |
333 // max_num_reorder_frames: ue(v) | |
334 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0)); | |
335 // max_dec_frame_buffering: ue(v) | |
336 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(max_num_ref_frames)); | |
337 return true; | |
338 } | |
339 | |
340 bool H264BitstreamRewriter::CopyRemainingBits( | |
341 rtc::BitBuffer* source, | |
342 rtc::BitBufferWriter* destination) { | |
343 uint32_t bits_tmp; | |
344 // Try to get at least the destination aligned. | |
345 if (source->RemainingBitCount() > 0 && source->RemainingBitCount() % 8 != 0) { | |
346 size_t misaligned_bits = source->RemainingBitCount() % 8; | |
347 COPY_BITS(source, destination, bits_tmp, misaligned_bits); | |
348 } | |
349 while (source->RemainingBitCount() > 0) { | |
350 size_t count = std::min(static_cast<size_t>(32u), | |
351 static_cast<size_t>(source->RemainingBitCount())); | |
352 COPY_BITS(source, destination, bits_tmp, count); | |
353 } | |
354 // TODO(noahric): The last byte could be all zeroes now, which we should just | |
355 // strip. | |
356 return true; | |
357 } | |
358 | |
359 } // namespace webrtc | |
OLD | NEW |