Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(420)

Side by Side Diff: webrtc/common_video/h264/sps_vui_rewriter.cc

Issue 1979443004: Add H264 bitstream rewriting to limit frame reordering marker in header (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Addressed comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/common_video/h264/sps_vui_rewriter.h"
13
14 #include <algorithm>
15 #include <memory>
16
17 #include "webrtc/base/bitbuffer.h"
18 #include "webrtc/base/checks.h"
19 #include "webrtc/base/logging.h"
20
21 #include "webrtc/common_video/h264/h264_common.h"
22 #include "webrtc/common_video/h264/sps_parser.h"
23
24 namespace webrtc {
25
26 // The maximum expected growth from adding a VUI to the SPS. It's actually
27 // closer to 24 or so, but better safe than sorry.
28 const size_t kMaxVuiSpsIncrease = 64;
29
30 #define RETURN_FALSE_ON_FAIL(x) \
31 if (!(x)) { \
32 LOG_F(LS_ERROR) << " (line:" << __LINE__ << ") FAILED: " #x; \
33 return false; \
34 }
35
36 #define COPY_UINT8(src, dest, tmp) \
37 do { \
38 RETURN_FALSE_ON_FAIL((src)->ReadUInt8(&tmp)); \
39 if (dest) \
40 RETURN_FALSE_ON_FAIL((dest)->WriteUInt8(tmp)); \
41 } while (0)
42
43 #define COPY_EXP_GOLOMB(src, dest, tmp) \
44 do { \
45 RETURN_FALSE_ON_FAIL((src)->ReadExponentialGolomb(&tmp)); \
46 if (dest) \
47 RETURN_FALSE_ON_FAIL((dest)->WriteExponentialGolomb(tmp)); \
48 } while (0)
49
50 #define COPY_BITS(src, dest, tmp, bits) \
51 do { \
52 RETURN_FALSE_ON_FAIL((src)->ReadBits(&tmp, bits)); \
53 if (dest) \
54 RETURN_FALSE_ON_FAIL((dest)->WriteBits(tmp, bits)); \
55 } while (0)
56
57 typedef const SpsParser::SpsState& Sps;
58
59 bool CopyAndRewriteVui(Sps sps,
60 rtc::BitBuffer* source,
61 rtc::BitBufferWriter* destination,
62 SpsVuiRewriter::ParseResult* out_vui_rewritten);
63 bool CopyHrdParameters(rtc::BitBuffer* source,
64 rtc::BitBufferWriter* destination);
65 bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
66 uint32_t max_num_ref_frames);
67 bool CopyRemainingBits(rtc::BitBuffer* source,
68 rtc::BitBufferWriter* destination);
69
70 SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps(
71 const uint8_t* buffer,
72 size_t length,
73 rtc::Optional<SpsParser::SpsState>* sps,
74 rtc::Buffer* destination) {
75 rtc::BitBuffer source_buffer(buffer, length);
76 rtc::Optional<SpsParser::SpsState> sps_state =
77 SpsParser::ParseSpsUpToVui(&source_buffer);
78 if (!sps_state)
79 return ParseResult::kFailure;
80
81 *sps = sps_state;
82
83 if (sps_state->pic_order_cnt_type >= 2) {
84 // No need to rewrite VUI in this case.
85 return ParseResult::kPocOk;
86 }
87
88 // We're going to completely muck up alignment, so we need a BitBuffer to
89 // write with.
90 rtc::Buffer out_buffer(length + kMaxVuiSpsIncrease);
91 rtc::BitBufferWriter sps_writer(out_buffer.data(), out_buffer.size());
92
93 // Check how far the SpsParser has read, and copy that data in bulk.
94 size_t byte_offset;
95 size_t bit_offset;
96 source_buffer.GetCurrentOffset(&byte_offset, &bit_offset);
97 memcpy(out_buffer.data(), buffer,
98 byte_offset + (bit_offset > 0 ? 1 : 0)); // OK to copy the last bits.
99
100 // SpsParser will have read the vui_params_present flag, which we want to
101 // modify, so back off a bit;
102 if (bit_offset == 0) {
103 --byte_offset;
104 bit_offset = 7;
105 } else {
106 --bit_offset;
107 }
108 sps_writer.Seek(byte_offset, bit_offset);
109
110 ParseResult vui_updated;
111 if (!CopyAndRewriteVui(*sps_state, &source_buffer, &sps_writer,
112 &vui_updated)) {
113 LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
114 return ParseResult::kFailure;
115 }
116
117 if (vui_updated == ParseResult::kVuiOk) {
118 // No update necessary after all, just return.
119 return vui_updated;
120 }
121
122 if (!CopyRemainingBits(&source_buffer, &sps_writer)) {
123 LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
124 return ParseResult::kFailure;
125 }
126
127 // Pad up to next byte with zero bits.
128 sps_writer.GetCurrentOffset(&byte_offset, &bit_offset);
129 if (bit_offset > 0) {
130 sps_writer.WriteBits(0, 8 - bit_offset);
131 ++byte_offset;
132 bit_offset = 0;
133 }
134
135 RTC_DCHECK(byte_offset <= length + kMaxVuiSpsIncrease);
136 RTC_CHECK(destination != nullptr);
137
138 out_buffer.SetSize(byte_offset);
139
140 // Write updates SPS to destination with added RBSP
141 H264::WriteRbsp(out_buffer.data(), out_buffer.size(), destination);
142
143 return ParseResult::kVuiRewritten;
144 }
145
146 bool CopyAndRewriteVui(Sps sps,
147 rtc::BitBuffer* source,
148 rtc::BitBufferWriter* destination,
149 SpsVuiRewriter::ParseResult* out_vui_rewritten) {
150 uint32_t golomb_tmp;
151 uint32_t bits_tmp;
152
153 //
154 // vui_parameters_present_flag: u(1)
155 //
156 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
157
158 // ********* IMPORTANT! **********
159 // Now we're at the VUI, so we want to (1) add it if it isn't present, and
160 // (2) rewrite frame reordering values so no reordering is allowed.
161 if (!sps.vui_params_present) {
162 // Write a simple VUI with the parameters we want and 0 for all other flags.
163 // There are 8 flags to be off before the bitstream restriction flag.
164 RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 8));
165 // bitstream_restriction_flag: u(1)
166 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
167 RETURN_FALSE_ON_FAIL(
168 AddBitstreamRestriction(destination, sps.max_num_ref_frames));
169 } else {
170 // Parse out the full VUI.
171 // aspect_ratio_info_present_flag: u(1)
172 COPY_BITS(source, destination, bits_tmp, 1);
173 if (bits_tmp == 1) {
174 // aspect_ratio_idc: u(8)
175 COPY_BITS(source, destination, bits_tmp, 8);
176 if (bits_tmp == 255u) { // Extended_SAR
177 // sar_width/sar_height: u(16) each.
178 COPY_BITS(source, destination, bits_tmp, 32);
179 }
180 }
181 // overscan_info_present_flag: u(1)
182 COPY_BITS(source, destination, bits_tmp, 1);
183 if (bits_tmp == 1) {
184 // overscan_appropriate_flag: u(1)
185 COPY_BITS(source, destination, bits_tmp, 1);
186 }
187 // video_signal_type_present_flag: u(1)
188 COPY_BITS(source, destination, bits_tmp, 1);
189 if (bits_tmp == 1) {
190 // video_format + video_full_range_flag: u(3) + u(1)
191 COPY_BITS(source, destination, bits_tmp, 4);
192 // colour_description_present_flag: u(1)
193 COPY_BITS(source, destination, bits_tmp, 1);
194 if (bits_tmp == 1) {
195 // colour_primaries, transfer_characteristics, matrix_coefficients:
196 // u(8) each.
197 COPY_BITS(source, destination, bits_tmp, 24);
198 }
199 }
200 // chroma_loc_info_present_flag: u(1)
201 COPY_BITS(source, destination, bits_tmp, 1);
202 if (bits_tmp == 1) {
203 // chroma_sample_loc_type_(top|bottom)_field: ue(v) each.
204 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
205 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
206 }
207 // timing_info_present_flag: u(1)
208 COPY_BITS(source, destination, bits_tmp, 1);
209 if (bits_tmp == 1) {
210 // num_units_in_tick, time_scale: u(32) each
211 COPY_BITS(source, destination, bits_tmp, 32);
212 COPY_BITS(source, destination, bits_tmp, 32);
213 // fixed_frame_rate_flag: u(1)
214 COPY_BITS(source, destination, bits_tmp, 1);
215 }
216 // nal_hrd_parameters_present_flag: u(1)
217 uint32_t nal_hrd_parameters_present_flag;
218 COPY_BITS(source, destination, nal_hrd_parameters_present_flag, 1);
219 if (nal_hrd_parameters_present_flag == 1) {
220 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
221 }
222 // vcl_hrd_parameters_present_flag: u(1)
223 uint32_t vcl_hrd_parameters_present_flag;
224 COPY_BITS(source, destination, vcl_hrd_parameters_present_flag, 1);
225 if (vcl_hrd_parameters_present_flag == 1) {
226 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
227 }
228 if (nal_hrd_parameters_present_flag == 1 ||
229 vcl_hrd_parameters_present_flag == 1) {
230 // low_delay_hrd_flag: u(1)
231 COPY_BITS(source, destination, bits_tmp, 1);
232 }
233 // pic_struct_present_flag: u(1)
234 COPY_BITS(source, destination, bits_tmp, 1);
235
236 // bitstream_restriction_flag: u(1)
237 uint32_t bitstream_restriction_flag;
238 RETURN_FALSE_ON_FAIL(source->ReadBits(&bitstream_restriction_flag, 1));
239 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
240 if (bitstream_restriction_flag == 0) {
241 // We're adding one from scratch.
242 RETURN_FALSE_ON_FAIL(
243 AddBitstreamRestriction(destination, sps.max_num_ref_frames));
244 } else {
245 // We're replacing.
246 // motion_vectors_over_pic_boundaries_flag: u(1)
247 COPY_BITS(source, destination, bits_tmp, 1);
248 // max_bytes_per_pic_denom: ue(v)
249 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
250 // max_bits_per_mb_denom: ue(v)
251 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
252 // log2_max_mv_length_horizontal: ue(v)
253 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
254 // log2_max_mv_length_vertical: ue(v)
255 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
256 // ********* IMPORTANT! **********
257 // The next two are the ones we need to set to low numbers:
258 // max_num_reorder_frames: ue(v)
259 // max_dec_frame_buffering: ue(v)
260 // However, if they are already set to no greater than the numbers we
261 // want, then we don't need to be rewriting.
262 uint32_t max_num_reorder_frames, max_dec_frame_buffering;
263 RETURN_FALSE_ON_FAIL(
264 source->ReadExponentialGolomb(&max_num_reorder_frames));
265 RETURN_FALSE_ON_FAIL(
266 source->ReadExponentialGolomb(&max_dec_frame_buffering));
267 if (max_num_reorder_frames == 0 &&
268 max_dec_frame_buffering <= sps.max_num_ref_frames) {
269 LOG(LS_INFO) << "VUI bitstream already contains an optimal VUI.";
270 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiOk;
271 return true;
272 }
273 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
274 RETURN_FALSE_ON_FAIL(
275 destination->WriteExponentialGolomb(sps.max_num_ref_frames));
276 }
277 }
278 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
279 return true;
280 }
281
282 // Copies a VUI HRD parameters segment.
283 bool CopyHrdParameters(rtc::BitBuffer* source,
284 rtc::BitBufferWriter* destination) {
285 uint32_t golomb_tmp;
286 uint32_t bits_tmp;
287
288 // cbp_cnt_minus1: ue(v)
289 uint32_t cbp_cnt_minus1;
290 COPY_EXP_GOLOMB(source, destination, cbp_cnt_minus1);
291 // bit_rate_scale and cbp_size_scale: u(4) each
292 COPY_BITS(source, destination, bits_tmp, 8);
293 for (size_t i = 0; i <= cbp_cnt_minus1; ++i) {
294 // bit_rate_value_minus1 and cbp_size_value_minus1: ue(v) each
295 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
296 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
297 // cbr_flag: u(1)
298 COPY_BITS(source, destination, bits_tmp, 1);
299 }
300 // initial_cbp_removal_delay_length_minus1: u(5)
301 COPY_BITS(source, destination, bits_tmp, 5);
302 // cbp_removal_delay_length_minus1: u(5)
303 COPY_BITS(source, destination, bits_tmp, 5);
304 // dbp_output_delay_length_minus1: u(5)
305 COPY_BITS(source, destination, bits_tmp, 5);
306 // time_offset_length: u(5)
307 COPY_BITS(source, destination, bits_tmp, 5);
308 return true;
309 }
310
311 // These functions are similar to webrtc::H264SpsParser::Parse, and based on the
312 // same version of the H.264 standard. You can find it here:
313 // http://www.itu.int/rec/T-REC-H.264
314
315 // Adds a bitstream restriction VUI segment.
316 bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
317 uint32_t max_num_ref_frames) {
318 // motion_vectors_over_pic_boundaries_flag: u(1)
319 // Default is 1 when not present.
320 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
321 // max_bytes_per_pic_denom: ue(v)
322 // Default is 2 when not present.
323 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(2));
324 // max_bits_per_mb_denom: ue(v)
325 // Default is 1 when not present.
326 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(1));
327 // log2_max_mv_length_horizontal: ue(v)
328 // log2_max_mv_length_vertical: ue(v)
329 // Both default to 16 when not present.
330 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
331 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
332
333 // ********* IMPORTANT! **********
334 // max_num_reorder_frames: ue(v)
335 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
336 // max_dec_frame_buffering: ue(v)
337 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(max_num_ref_frames));
338 return true;
339 }
340
341 bool CopyRemainingBits(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
OLDNEW
« no previous file with comments | « webrtc/common_video/h264/sps_vui_rewriter.h ('k') | webrtc/common_video/h264/sps_vui_rewriter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698