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 "common_video/h264/sps_vui_rewriter.h"
13
14 #include <string.h>
15
16 #include <cstdint>
17 #include <vector>
18
19 #include "api/video/color_space.h"
20 #include "common_video/h264/h264_common.h"
21 #include "common_video/h264/sps_parser.h"
22 #include "rtc_base/bit_buffer.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/logging.h"
25 #include "rtc_base/numerics/safe_minmax.h"
26 #include "system_wrappers/include/metrics.h"
27
28 namespace webrtc {
29
30 namespace {
31
32 // The maximum expected growth from adding a VUI to the SPS. It's actually
33 // closer to 24 or so, but better safe than sorry.
34 const size_t kMaxVuiSpsIncrease = 64;
35
36 const char* kSpsValidHistogramName = "WebRTC.Video.H264.SpsValid";
37 enum SpsValidEvent {
38 kReceivedSpsVuiOk = 1,
39 kReceivedSpsRewritten = 2,
40 kReceivedSpsParseFailure = 3,
41 kSentSpsPocOk = 4,
42 kSentSpsVuiOk = 5,
43 kSentSpsRewritten = 6,
44 kSentSpsParseFailure = 7,
45 kSpsRewrittenMax = 8
46 };
47
48 #define RETURN_FALSE_ON_FAIL(x) \
49 if (!(x)) { \
50 RTC_LOG_F(LS_ERROR) << " (line:" << __LINE__ << ") FAILED: " #x; \
51 return false; \
52 }
53
54 #define COPY_UINT8(src, dest, tmp) \
55 do { \
56 RETURN_FALSE_ON_FAIL((src)->ReadUInt8(&tmp)); \
57 if (dest) \
58 RETURN_FALSE_ON_FAIL((dest)->WriteUInt8(tmp)); \
59 } while (0)
60
61 #define COPY_EXP_GOLOMB(src, dest, tmp) \
62 do { \
63 RETURN_FALSE_ON_FAIL((src)->ReadExponentialGolomb(&tmp)); \
64 if (dest) \
65 RETURN_FALSE_ON_FAIL((dest)->WriteExponentialGolomb(tmp)); \
66 } while (0)
67
68 #define COPY_BITS(src, dest, tmp, bits) \
69 do { \
70 RETURN_FALSE_ON_FAIL((src)->ReadBits(&tmp, bits)); \
71 if (dest) \
72 RETURN_FALSE_ON_FAIL((dest)->WriteBits(tmp, bits)); \
73 } while (0)
74
75 bool CopyAndRewriteVui(const SpsParser::SpsState& sps,
76 rtc::BitBuffer* source,
77 rtc::BitBufferWriter* destination,
78 const webrtc::ColorSpace* color_space,
79 SpsVuiRewriter::ParseResult* out_vui_rewritten);
80 bool CopyHrdParameters(rtc::BitBuffer* source,
81 rtc::BitBufferWriter* destination);
82 bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
83 uint32_t max_num_ref_frames);
84 bool IsDefaultColorSpace(const ColorSpace& color_space);
85 bool AddVideoSignalTypeInfo(rtc::BitBufferWriter* destination,
86 const ColorSpace& color_space);
87 bool CopyOrRewriteVideoSignalTypeInfo(
88 rtc::BitBuffer* source,
89 rtc::BitBufferWriter* destination,
90 const ColorSpace* color_space,
91 SpsVuiRewriter::ParseResult* out_vui_rewritten);
92 bool CopyRemainingBits(rtc::BitBuffer* source,
93 rtc::BitBufferWriter* destination);
94 } // namespace
95
UpdateStats(ParseResult result,Direction direction)96 void SpsVuiRewriter::UpdateStats(ParseResult result, Direction direction) {
97 switch (result) {
98 case SpsVuiRewriter::ParseResult::kVuiRewritten:
99 RTC_HISTOGRAM_ENUMERATION(
100 kSpsValidHistogramName,
101 direction == SpsVuiRewriter::Direction::kIncoming
102 ? SpsValidEvent::kReceivedSpsRewritten
103 : SpsValidEvent::kSentSpsRewritten,
104 SpsValidEvent::kSpsRewrittenMax);
105 break;
106 case SpsVuiRewriter::ParseResult::kVuiOk:
107 RTC_HISTOGRAM_ENUMERATION(
108 kSpsValidHistogramName,
109 direction == SpsVuiRewriter::Direction::kIncoming
110 ? SpsValidEvent::kReceivedSpsVuiOk
111 : SpsValidEvent::kSentSpsVuiOk,
112 SpsValidEvent::kSpsRewrittenMax);
113 break;
114 case SpsVuiRewriter::ParseResult::kFailure:
115 RTC_HISTOGRAM_ENUMERATION(
116 kSpsValidHistogramName,
117 direction == SpsVuiRewriter::Direction::kIncoming
118 ? SpsValidEvent::kReceivedSpsParseFailure
119 : SpsValidEvent::kSentSpsParseFailure,
120 SpsValidEvent::kSpsRewrittenMax);
121 break;
122 }
123 }
124
ParseAndRewriteSps(const uint8_t * buffer,size_t length,absl::optional<SpsParser::SpsState> * sps,const webrtc::ColorSpace * color_space,rtc::Buffer * destination)125 SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps(
126 const uint8_t* buffer,
127 size_t length,
128 absl::optional<SpsParser::SpsState>* sps,
129 const webrtc::ColorSpace* color_space,
130 rtc::Buffer* destination) {
131 // Create temporary RBSP decoded buffer of the payload (exlcuding the
132 // leading nalu type header byte (the SpsParser uses only the payload).
133 std::vector<uint8_t> rbsp_buffer = H264::ParseRbsp(buffer, length);
134 rtc::BitBuffer source_buffer(rbsp_buffer.data(), rbsp_buffer.size());
135 absl::optional<SpsParser::SpsState> sps_state =
136 SpsParser::ParseSpsUpToVui(&source_buffer);
137 if (!sps_state)
138 return ParseResult::kFailure;
139
140 *sps = sps_state;
141
142 // We're going to completely muck up alignment, so we need a BitBuffer to
143 // write with.
144 rtc::Buffer out_buffer(length + kMaxVuiSpsIncrease);
145 rtc::BitBufferWriter sps_writer(out_buffer.data(), out_buffer.size());
146
147 // Check how far the SpsParser has read, and copy that data in bulk.
148 size_t byte_offset;
149 size_t bit_offset;
150 source_buffer.GetCurrentOffset(&byte_offset, &bit_offset);
151 memcpy(out_buffer.data(), rbsp_buffer.data(),
152 byte_offset + (bit_offset > 0 ? 1 : 0)); // OK to copy the last bits.
153
154 // SpsParser will have read the vui_params_present flag, which we want to
155 // modify, so back off a bit;
156 if (bit_offset == 0) {
157 --byte_offset;
158 bit_offset = 7;
159 } else {
160 --bit_offset;
161 }
162 sps_writer.Seek(byte_offset, bit_offset);
163
164 ParseResult vui_updated;
165 if (!CopyAndRewriteVui(*sps_state, &source_buffer, &sps_writer, color_space,
166 &vui_updated)) {
167 RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
168 return ParseResult::kFailure;
169 }
170
171 if (vui_updated == ParseResult::kVuiOk) {
172 // No update necessary after all, just return.
173 return vui_updated;
174 }
175
176 if (!CopyRemainingBits(&source_buffer, &sps_writer)) {
177 RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
178 return ParseResult::kFailure;
179 }
180
181 // Pad up to next byte with zero bits.
182 sps_writer.GetCurrentOffset(&byte_offset, &bit_offset);
183 if (bit_offset > 0) {
184 sps_writer.WriteBits(0, 8 - bit_offset);
185 ++byte_offset;
186 bit_offset = 0;
187 }
188
189 RTC_DCHECK(byte_offset <= length + kMaxVuiSpsIncrease);
190 RTC_CHECK(destination != nullptr);
191
192 out_buffer.SetSize(byte_offset);
193
194 // Write updates SPS to destination with added RBSP
195 H264::WriteRbsp(out_buffer.data(), out_buffer.size(), destination);
196
197 return ParseResult::kVuiRewritten;
198 }
199
ParseAndRewriteSps(const uint8_t * buffer,size_t length,absl::optional<SpsParser::SpsState> * sps,const webrtc::ColorSpace * color_space,rtc::Buffer * destination,Direction direction)200 SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps(
201 const uint8_t* buffer,
202 size_t length,
203 absl::optional<SpsParser::SpsState>* sps,
204 const webrtc::ColorSpace* color_space,
205 rtc::Buffer* destination,
206 Direction direction) {
207 ParseResult result =
208 ParseAndRewriteSps(buffer, length, sps, color_space, destination);
209 UpdateStats(result, direction);
210 return result;
211 }
212
ParseOutgoingBitstreamAndRewriteSps(rtc::ArrayView<const uint8_t> buffer,size_t num_nalus,const size_t * nalu_offsets,const size_t * nalu_lengths,const webrtc::ColorSpace * color_space,rtc::Buffer * output_buffer,size_t * output_nalu_offsets,size_t * output_nalu_lengths)213 void SpsVuiRewriter::ParseOutgoingBitstreamAndRewriteSps(
214 rtc::ArrayView<const uint8_t> buffer,
215 size_t num_nalus,
216 const size_t* nalu_offsets,
217 const size_t* nalu_lengths,
218 const webrtc::ColorSpace* color_space,
219 rtc::Buffer* output_buffer,
220 size_t* output_nalu_offsets,
221 size_t* output_nalu_lengths) {
222 // Allocate some extra space for potentially adding a missing VUI.
223 output_buffer->EnsureCapacity(buffer.size() + num_nalus * kMaxVuiSpsIncrease);
224
225 const uint8_t* prev_nalu_ptr = buffer.data();
226 size_t prev_nalu_length = 0;
227
228 for (size_t i = 0; i < num_nalus; ++i) {
229 const uint8_t* nalu_ptr = buffer.data() + nalu_offsets[i];
230 const size_t nalu_length = nalu_lengths[i];
231
232 // Copy NAL unit start code.
233 const uint8_t* start_code_ptr = prev_nalu_ptr + prev_nalu_length;
234 const size_t start_code_length =
235 (nalu_ptr - prev_nalu_ptr) - prev_nalu_length;
236 output_buffer->AppendData(start_code_ptr, start_code_length);
237
238 bool updated_sps = false;
239
240 if (H264::ParseNaluType(nalu_ptr[0]) == H264::NaluType::kSps) {
241 // Check if stream uses picture order count type 0, and if so rewrite it
242 // to enable faster decoding. Streams in that format incur additional
243 // delay because it allows decode order to differ from render order.
244 // The mechanism used is to rewrite (edit or add) the SPS's VUI to contain
245 // restrictions on the maximum number of reordered pictures. This reduces
246 // latency significantly, though it still adds about a frame of latency to
247 // decoding.
248 // Note that we do this rewriting both here (send side, in order to
249 // protect legacy receive clients) in RtpDepacketizerH264::ParseSingleNalu
250 // (receive side, in orderer to protect us from unknown or legacy send
251 // clients).
252 absl::optional<SpsParser::SpsState> sps;
253 rtc::Buffer output_nalu;
254
255 // Add the type header to the output buffer first, so that the rewriter
256 // can append modified payload on top of that.
257 output_nalu.AppendData(nalu_ptr[0]);
258
259 ParseResult result = ParseAndRewriteSps(
260 nalu_ptr + H264::kNaluTypeSize, nalu_length - H264::kNaluTypeSize,
261 &sps, color_space, &output_nalu, Direction::kOutgoing);
262 if (result == ParseResult::kVuiRewritten) {
263 updated_sps = true;
264 output_nalu_offsets[i] = output_buffer->size();
265 output_nalu_lengths[i] = output_nalu.size();
266 output_buffer->AppendData(output_nalu.data(), output_nalu.size());
267 }
268 }
269
270 if (!updated_sps) {
271 output_nalu_offsets[i] = output_buffer->size();
272 output_nalu_lengths[i] = nalu_length;
273 output_buffer->AppendData(nalu_ptr, nalu_length);
274 }
275
276 prev_nalu_ptr = nalu_ptr;
277 prev_nalu_length = nalu_length;
278 }
279 }
280
281 namespace {
CopyAndRewriteVui(const SpsParser::SpsState & sps,rtc::BitBuffer * source,rtc::BitBufferWriter * destination,const webrtc::ColorSpace * color_space,SpsVuiRewriter::ParseResult * out_vui_rewritten)282 bool CopyAndRewriteVui(const SpsParser::SpsState& sps,
283 rtc::BitBuffer* source,
284 rtc::BitBufferWriter* destination,
285 const webrtc::ColorSpace* color_space,
286 SpsVuiRewriter::ParseResult* out_vui_rewritten) {
287 uint32_t golomb_tmp;
288 uint32_t bits_tmp;
289
290 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiOk;
291
292 //
293 // vui_parameters_present_flag: u(1)
294 //
295 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
296
297 // ********* IMPORTANT! **********
298 // Now we're at the VUI, so we want to (1) add it if it isn't present, and
299 // (2) rewrite frame reordering values so no reordering is allowed.
300 if (!sps.vui_params_present) {
301 // Write a simple VUI with the parameters we want and 0 for all other flags.
302
303 // aspect_ratio_info_present_flag, overscan_info_present_flag. Both u(1).
304 RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 2));
305
306 uint32_t video_signal_type_present_flag =
307 (color_space && !IsDefaultColorSpace(*color_space)) ? 1 : 0;
308 RETURN_FALSE_ON_FAIL(
309 destination->WriteBits(video_signal_type_present_flag, 1));
310 if (video_signal_type_present_flag) {
311 RETURN_FALSE_ON_FAIL(AddVideoSignalTypeInfo(destination, *color_space));
312 }
313 // chroma_loc_info_present_flag, timing_info_present_flag,
314 // nal_hrd_parameters_present_flag, vcl_hrd_parameters_present_flag,
315 // pic_struct_present_flag, All u(1)
316 RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 5));
317 // bitstream_restriction_flag: u(1)
318 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
319 RETURN_FALSE_ON_FAIL(
320 AddBitstreamRestriction(destination, sps.max_num_ref_frames));
321
322 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
323 } else {
324 // Parse out the full VUI.
325 // aspect_ratio_info_present_flag: u(1)
326 COPY_BITS(source, destination, bits_tmp, 1);
327 if (bits_tmp == 1) {
328 // aspect_ratio_idc: u(8)
329 COPY_BITS(source, destination, bits_tmp, 8);
330 if (bits_tmp == 255u) { // Extended_SAR
331 // sar_width/sar_height: u(16) each.
332 COPY_BITS(source, destination, bits_tmp, 32);
333 }
334 }
335 // overscan_info_present_flag: u(1)
336 COPY_BITS(source, destination, bits_tmp, 1);
337 if (bits_tmp == 1) {
338 // overscan_appropriate_flag: u(1)
339 COPY_BITS(source, destination, bits_tmp, 1);
340 }
341
342 CopyOrRewriteVideoSignalTypeInfo(source, destination, color_space,
343 out_vui_rewritten);
344
345 // chroma_loc_info_present_flag: u(1)
346 COPY_BITS(source, destination, bits_tmp, 1);
347 if (bits_tmp == 1) {
348 // chroma_sample_loc_type_(top|bottom)_field: ue(v) each.
349 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
350 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
351 }
352 // timing_info_present_flag: u(1)
353 COPY_BITS(source, destination, bits_tmp, 1);
354 if (bits_tmp == 1) {
355 // num_units_in_tick, time_scale: u(32) each
356 COPY_BITS(source, destination, bits_tmp, 32);
357 COPY_BITS(source, destination, bits_tmp, 32);
358 // fixed_frame_rate_flag: u(1)
359 COPY_BITS(source, destination, bits_tmp, 1);
360 }
361 // nal_hrd_parameters_present_flag: u(1)
362 uint32_t nal_hrd_parameters_present_flag;
363 COPY_BITS(source, destination, nal_hrd_parameters_present_flag, 1);
364 if (nal_hrd_parameters_present_flag == 1) {
365 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
366 }
367 // vcl_hrd_parameters_present_flag: u(1)
368 uint32_t vcl_hrd_parameters_present_flag;
369 COPY_BITS(source, destination, vcl_hrd_parameters_present_flag, 1);
370 if (vcl_hrd_parameters_present_flag == 1) {
371 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
372 }
373 if (nal_hrd_parameters_present_flag == 1 ||
374 vcl_hrd_parameters_present_flag == 1) {
375 // low_delay_hrd_flag: u(1)
376 COPY_BITS(source, destination, bits_tmp, 1);
377 }
378 // pic_struct_present_flag: u(1)
379 COPY_BITS(source, destination, bits_tmp, 1);
380
381 // bitstream_restriction_flag: u(1)
382 uint32_t bitstream_restriction_flag;
383 RETURN_FALSE_ON_FAIL(source->ReadBits(&bitstream_restriction_flag, 1));
384 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
385 if (bitstream_restriction_flag == 0) {
386 // We're adding one from scratch.
387 RETURN_FALSE_ON_FAIL(
388 AddBitstreamRestriction(destination, sps.max_num_ref_frames));
389 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
390 } else {
391 // We're replacing.
392 // motion_vectors_over_pic_boundaries_flag: u(1)
393 COPY_BITS(source, destination, bits_tmp, 1);
394 // max_bytes_per_pic_denom: ue(v)
395 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
396 // max_bits_per_mb_denom: ue(v)
397 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
398 // log2_max_mv_length_horizontal: ue(v)
399 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
400 // log2_max_mv_length_vertical: ue(v)
401 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
402 // ********* IMPORTANT! **********
403 // The next two are the ones we need to set to low numbers:
404 // max_num_reorder_frames: ue(v)
405 // max_dec_frame_buffering: ue(v)
406 // However, if they are already set to no greater than the numbers we
407 // want, then we don't need to be rewriting.
408 uint32_t max_num_reorder_frames, max_dec_frame_buffering;
409 RETURN_FALSE_ON_FAIL(
410 source->ReadExponentialGolomb(&max_num_reorder_frames));
411 RETURN_FALSE_ON_FAIL(
412 source->ReadExponentialGolomb(&max_dec_frame_buffering));
413 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
414 RETURN_FALSE_ON_FAIL(
415 destination->WriteExponentialGolomb(sps.max_num_ref_frames));
416 if (max_num_reorder_frames != 0 ||
417 max_dec_frame_buffering > sps.max_num_ref_frames) {
418 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
419 }
420 }
421 }
422 return true;
423 }
424
425 // Copies a VUI HRD parameters segment.
CopyHrdParameters(rtc::BitBuffer * source,rtc::BitBufferWriter * destination)426 bool CopyHrdParameters(rtc::BitBuffer* source,
427 rtc::BitBufferWriter* destination) {
428 uint32_t golomb_tmp;
429 uint32_t bits_tmp;
430
431 // cbp_cnt_minus1: ue(v)
432 uint32_t cbp_cnt_minus1;
433 COPY_EXP_GOLOMB(source, destination, cbp_cnt_minus1);
434 // bit_rate_scale and cbp_size_scale: u(4) each
435 COPY_BITS(source, destination, bits_tmp, 8);
436 for (size_t i = 0; i <= cbp_cnt_minus1; ++i) {
437 // bit_rate_value_minus1 and cbp_size_value_minus1: ue(v) each
438 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
439 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
440 // cbr_flag: u(1)
441 COPY_BITS(source, destination, bits_tmp, 1);
442 }
443 // initial_cbp_removal_delay_length_minus1: u(5)
444 COPY_BITS(source, destination, bits_tmp, 5);
445 // cbp_removal_delay_length_minus1: u(5)
446 COPY_BITS(source, destination, bits_tmp, 5);
447 // dbp_output_delay_length_minus1: u(5)
448 COPY_BITS(source, destination, bits_tmp, 5);
449 // time_offset_length: u(5)
450 COPY_BITS(source, destination, bits_tmp, 5);
451 return true;
452 }
453
454 // These functions are similar to webrtc::H264SpsParser::Parse, and based on the
455 // same version of the H.264 standard. You can find it here:
456 // http://www.itu.int/rec/T-REC-H.264
457
458 // Adds a bitstream restriction VUI segment.
AddBitstreamRestriction(rtc::BitBufferWriter * destination,uint32_t max_num_ref_frames)459 bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
460 uint32_t max_num_ref_frames) {
461 // motion_vectors_over_pic_boundaries_flag: u(1)
462 // Default is 1 when not present.
463 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
464 // max_bytes_per_pic_denom: ue(v)
465 // Default is 2 when not present.
466 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(2));
467 // max_bits_per_mb_denom: ue(v)
468 // Default is 1 when not present.
469 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(1));
470 // log2_max_mv_length_horizontal: ue(v)
471 // log2_max_mv_length_vertical: ue(v)
472 // Both default to 16 when not present.
473 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
474 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
475
476 // ********* IMPORTANT! **********
477 // max_num_reorder_frames: ue(v)
478 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
479 // max_dec_frame_buffering: ue(v)
480 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(max_num_ref_frames));
481 return true;
482 }
483
IsDefaultColorSpace(const ColorSpace & color_space)484 bool IsDefaultColorSpace(const ColorSpace& color_space) {
485 return color_space.range() != ColorSpace::RangeID::kFull &&
486 color_space.primaries() == ColorSpace::PrimaryID::kUnspecified &&
487 color_space.transfer() == ColorSpace::TransferID::kUnspecified &&
488 color_space.matrix() == ColorSpace::MatrixID::kUnspecified;
489 }
490
AddVideoSignalTypeInfo(rtc::BitBufferWriter * destination,const ColorSpace & color_space)491 bool AddVideoSignalTypeInfo(rtc::BitBufferWriter* destination,
492 const ColorSpace& color_space) {
493 // video_format: u(3).
494 RETURN_FALSE_ON_FAIL(destination->WriteBits(5, 3)); // 5 = Unspecified
495 // video_full_range_flag: u(1)
496 RETURN_FALSE_ON_FAIL(destination->WriteBits(
497 color_space.range() == ColorSpace::RangeID::kFull ? 1 : 0, 1));
498 // colour_description_present_flag: u(1)
499 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
500 // colour_primaries: u(8)
501 RETURN_FALSE_ON_FAIL(
502 destination->WriteUInt8(static_cast<uint8_t>(color_space.primaries())));
503 // transfer_characteristics: u(8)
504 RETURN_FALSE_ON_FAIL(
505 destination->WriteUInt8(static_cast<uint8_t>(color_space.transfer())));
506 // matrix_coefficients: u(8)
507 RETURN_FALSE_ON_FAIL(
508 destination->WriteUInt8(static_cast<uint8_t>(color_space.matrix())));
509 return true;
510 }
511
CopyOrRewriteVideoSignalTypeInfo(rtc::BitBuffer * source,rtc::BitBufferWriter * destination,const ColorSpace * color_space,SpsVuiRewriter::ParseResult * out_vui_rewritten)512 bool CopyOrRewriteVideoSignalTypeInfo(
513 rtc::BitBuffer* source,
514 rtc::BitBufferWriter* destination,
515 const ColorSpace* color_space,
516 SpsVuiRewriter::ParseResult* out_vui_rewritten) {
517 // Read.
518 uint32_t video_signal_type_present_flag;
519 uint32_t video_format = 5; // H264 default: unspecified
520 uint32_t video_full_range_flag = 0; // H264 default: limited
521 uint32_t colour_description_present_flag = 0;
522 uint8_t colour_primaries = 3; // H264 default: unspecified
523 uint8_t transfer_characteristics = 3; // H264 default: unspecified
524 uint8_t matrix_coefficients = 3; // H264 default: unspecified
525 RETURN_FALSE_ON_FAIL(source->ReadBits(&video_signal_type_present_flag, 1));
526 if (video_signal_type_present_flag) {
527 RETURN_FALSE_ON_FAIL(source->ReadBits(&video_format, 3));
528 RETURN_FALSE_ON_FAIL(source->ReadBits(&video_full_range_flag, 1));
529 RETURN_FALSE_ON_FAIL(source->ReadBits(&colour_description_present_flag, 1));
530 if (colour_description_present_flag) {
531 RETURN_FALSE_ON_FAIL(source->ReadUInt8(&colour_primaries));
532 RETURN_FALSE_ON_FAIL(source->ReadUInt8(&transfer_characteristics));
533 RETURN_FALSE_ON_FAIL(source->ReadUInt8(&matrix_coefficients));
534 }
535 }
536
537 // Update.
538 uint32_t video_signal_type_present_flag_override =
539 video_signal_type_present_flag;
540 uint32_t video_format_override = video_format;
541 uint32_t video_full_range_flag_override = video_full_range_flag;
542 uint32_t colour_description_present_flag_override =
543 colour_description_present_flag;
544 uint8_t colour_primaries_override = colour_primaries;
545 uint8_t transfer_characteristics_override = transfer_characteristics;
546 uint8_t matrix_coefficients_override = matrix_coefficients;
547 if (color_space) {
548 if (IsDefaultColorSpace(*color_space)) {
549 video_signal_type_present_flag_override = 0;
550 } else {
551 video_signal_type_present_flag_override = 1;
552 video_format_override = 5; // unspecified
553
554 if (color_space->range() == ColorSpace::RangeID::kFull) {
555 video_full_range_flag_override = 1;
556 } else {
557 // ColorSpace::RangeID::kInvalid and kDerived are treated as limited.
558 video_full_range_flag_override = 0;
559 }
560
561 colour_description_present_flag_override =
562 color_space->primaries() != ColorSpace::PrimaryID::kUnspecified ||
563 color_space->transfer() != ColorSpace::TransferID::kUnspecified ||
564 color_space->matrix() != ColorSpace::MatrixID::kUnspecified;
565 colour_primaries_override =
566 static_cast<uint8_t>(color_space->primaries());
567 transfer_characteristics_override =
568 static_cast<uint8_t>(color_space->transfer());
569 matrix_coefficients_override =
570 static_cast<uint8_t>(color_space->matrix());
571 }
572 }
573
574 // Write.
575 RETURN_FALSE_ON_FAIL(
576 destination->WriteBits(video_signal_type_present_flag_override, 1));
577 if (video_signal_type_present_flag_override) {
578 RETURN_FALSE_ON_FAIL(destination->WriteBits(video_format_override, 3));
579 RETURN_FALSE_ON_FAIL(
580 destination->WriteBits(video_full_range_flag_override, 1));
581 RETURN_FALSE_ON_FAIL(
582 destination->WriteBits(colour_description_present_flag_override, 1));
583 if (colour_description_present_flag_override) {
584 RETURN_FALSE_ON_FAIL(destination->WriteUInt8(colour_primaries_override));
585 RETURN_FALSE_ON_FAIL(
586 destination->WriteUInt8(transfer_characteristics_override));
587 RETURN_FALSE_ON_FAIL(
588 destination->WriteUInt8(matrix_coefficients_override));
589 }
590 }
591
592 if (video_signal_type_present_flag_override !=
593 video_signal_type_present_flag ||
594 video_format_override != video_format ||
595 video_full_range_flag_override != video_full_range_flag ||
596 colour_description_present_flag_override !=
597 colour_description_present_flag ||
598 colour_primaries_override != colour_primaries ||
599 transfer_characteristics_override != transfer_characteristics ||
600 matrix_coefficients_override != matrix_coefficients) {
601 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
602 }
603
604 return true;
605 }
606
CopyRemainingBits(rtc::BitBuffer * source,rtc::BitBufferWriter * destination)607 bool CopyRemainingBits(rtc::BitBuffer* source,
608 rtc::BitBufferWriter* destination) {
609 uint32_t bits_tmp;
610 // Try to get at least the destination aligned.
611 if (source->RemainingBitCount() > 0 && source->RemainingBitCount() % 8 != 0) {
612 size_t misaligned_bits = source->RemainingBitCount() % 8;
613 COPY_BITS(source, destination, bits_tmp, misaligned_bits);
614 }
615 while (source->RemainingBitCount() > 0) {
616 auto count = rtc::SafeMin<size_t>(32u, source->RemainingBitCount());
617 COPY_BITS(source, destination, bits_tmp, count);
618 }
619 // TODO(noahric): The last byte could be all zeroes now, which we should just
620 // strip.
621 return true;
622 }
623
624 } // namespace
625
626 } // namespace webrtc
627