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