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
ParseOutgoingBitstreamAndRewrite(rtc::ArrayView<const uint8_t> buffer,const webrtc::ColorSpace * color_space)213 rtc::Buffer SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite(
214 rtc::ArrayView<const uint8_t> buffer,
215 const webrtc::ColorSpace* color_space) {
216 std::vector<H264::NaluIndex> nalus =
217 H264::FindNaluIndices(buffer.data(), buffer.size());
218
219 // Allocate some extra space for potentially adding a missing VUI.
220 rtc::Buffer output_buffer(/*size=*/0, /*capacity=*/buffer.size() +
221 nalus.size() * kMaxVuiSpsIncrease);
222
223 for (const H264::NaluIndex& nalu : nalus) {
224 // Copy NAL unit start code.
225 const uint8_t* start_code_ptr = buffer.data() + nalu.start_offset;
226 const size_t start_code_length =
227 nalu.payload_start_offset - nalu.start_offset;
228 const uint8_t* nalu_ptr = buffer.data() + nalu.payload_start_offset;
229 const size_t nalu_length = nalu.payload_size;
230
231 if (H264::ParseNaluType(nalu_ptr[0]) == H264::NaluType::kSps) {
232 // Check if stream uses picture order count type 0, and if so rewrite it
233 // to enable faster decoding. Streams in that format incur additional
234 // delay because it allows decode order to differ from render order.
235 // The mechanism used is to rewrite (edit or add) the SPS's VUI to contain
236 // restrictions on the maximum number of reordered pictures. This reduces
237 // latency significantly, though it still adds about a frame of latency to
238 // decoding.
239 // Note that we do this rewriting both here (send side, in order to
240 // protect legacy receive clients) in RtpDepacketizerH264::ParseSingleNalu
241 // (receive side, in orderer to protect us from unknown or legacy send
242 // clients).
243 absl::optional<SpsParser::SpsState> sps;
244 rtc::Buffer output_nalu;
245
246 // Add the type header to the output buffer first, so that the rewriter
247 // can append modified payload on top of that.
248 output_nalu.AppendData(nalu_ptr[0]);
249
250 ParseResult result = ParseAndRewriteSps(
251 nalu_ptr + H264::kNaluTypeSize, nalu_length - H264::kNaluTypeSize,
252 &sps, color_space, &output_nalu, Direction::kOutgoing);
253 if (result == ParseResult::kVuiRewritten) {
254 output_buffer.AppendData(start_code_ptr, start_code_length);
255 output_buffer.AppendData(output_nalu.data(), output_nalu.size());
256 continue;
257 }
258 } else if (H264::ParseNaluType(nalu_ptr[0]) == H264::NaluType::kAud) {
259 // Skip the access unit delimiter copy.
260 continue;
261 }
262
263 // vui wasn't rewritten and it is not aud, copy the nal unit as is.
264 output_buffer.AppendData(start_code_ptr, start_code_length);
265 output_buffer.AppendData(nalu_ptr, nalu_length);
266 }
267 return output_buffer;
268 }
269
270 namespace {
CopyAndRewriteVui(const SpsParser::SpsState & sps,rtc::BitBuffer * source,rtc::BitBufferWriter * destination,const webrtc::ColorSpace * color_space,SpsVuiRewriter::ParseResult * out_vui_rewritten)271 bool CopyAndRewriteVui(const SpsParser::SpsState& sps,
272 rtc::BitBuffer* source,
273 rtc::BitBufferWriter* destination,
274 const webrtc::ColorSpace* color_space,
275 SpsVuiRewriter::ParseResult* out_vui_rewritten) {
276 uint32_t golomb_tmp;
277 uint32_t bits_tmp;
278
279 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiOk;
280
281 //
282 // vui_parameters_present_flag: u(1)
283 //
284 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
285
286 // ********* IMPORTANT! **********
287 // Now we're at the VUI, so we want to (1) add it if it isn't present, and
288 // (2) rewrite frame reordering values so no reordering is allowed.
289 if (!sps.vui_params_present) {
290 // Write a simple VUI with the parameters we want and 0 for all other flags.
291
292 // aspect_ratio_info_present_flag, overscan_info_present_flag. Both u(1).
293 RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 2));
294
295 uint32_t video_signal_type_present_flag =
296 (color_space && !IsDefaultColorSpace(*color_space)) ? 1 : 0;
297 RETURN_FALSE_ON_FAIL(
298 destination->WriteBits(video_signal_type_present_flag, 1));
299 if (video_signal_type_present_flag) {
300 RETURN_FALSE_ON_FAIL(AddVideoSignalTypeInfo(destination, *color_space));
301 }
302 // chroma_loc_info_present_flag, timing_info_present_flag,
303 // nal_hrd_parameters_present_flag, vcl_hrd_parameters_present_flag,
304 // pic_struct_present_flag, All u(1)
305 RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 5));
306 // bitstream_restriction_flag: u(1)
307 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
308 RETURN_FALSE_ON_FAIL(
309 AddBitstreamRestriction(destination, sps.max_num_ref_frames));
310
311 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
312 } else {
313 // Parse out the full VUI.
314 // aspect_ratio_info_present_flag: u(1)
315 COPY_BITS(source, destination, bits_tmp, 1);
316 if (bits_tmp == 1) {
317 // aspect_ratio_idc: u(8)
318 COPY_BITS(source, destination, bits_tmp, 8);
319 if (bits_tmp == 255u) { // Extended_SAR
320 // sar_width/sar_height: u(16) each.
321 COPY_BITS(source, destination, bits_tmp, 32);
322 }
323 }
324 // overscan_info_present_flag: u(1)
325 COPY_BITS(source, destination, bits_tmp, 1);
326 if (bits_tmp == 1) {
327 // overscan_appropriate_flag: u(1)
328 COPY_BITS(source, destination, bits_tmp, 1);
329 }
330
331 CopyOrRewriteVideoSignalTypeInfo(source, destination, color_space,
332 out_vui_rewritten);
333
334 // chroma_loc_info_present_flag: u(1)
335 COPY_BITS(source, destination, bits_tmp, 1);
336 if (bits_tmp == 1) {
337 // chroma_sample_loc_type_(top|bottom)_field: ue(v) each.
338 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
339 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
340 }
341 // timing_info_present_flag: u(1)
342 COPY_BITS(source, destination, bits_tmp, 1);
343 if (bits_tmp == 1) {
344 // num_units_in_tick, time_scale: u(32) each
345 COPY_BITS(source, destination, bits_tmp, 32);
346 COPY_BITS(source, destination, bits_tmp, 32);
347 // fixed_frame_rate_flag: u(1)
348 COPY_BITS(source, destination, bits_tmp, 1);
349 }
350 // nal_hrd_parameters_present_flag: u(1)
351 uint32_t nal_hrd_parameters_present_flag;
352 COPY_BITS(source, destination, nal_hrd_parameters_present_flag, 1);
353 if (nal_hrd_parameters_present_flag == 1) {
354 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
355 }
356 // vcl_hrd_parameters_present_flag: u(1)
357 uint32_t vcl_hrd_parameters_present_flag;
358 COPY_BITS(source, destination, vcl_hrd_parameters_present_flag, 1);
359 if (vcl_hrd_parameters_present_flag == 1) {
360 RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
361 }
362 if (nal_hrd_parameters_present_flag == 1 ||
363 vcl_hrd_parameters_present_flag == 1) {
364 // low_delay_hrd_flag: u(1)
365 COPY_BITS(source, destination, bits_tmp, 1);
366 }
367 // pic_struct_present_flag: u(1)
368 COPY_BITS(source, destination, bits_tmp, 1);
369
370 // bitstream_restriction_flag: u(1)
371 uint32_t bitstream_restriction_flag;
372 RETURN_FALSE_ON_FAIL(source->ReadBits(&bitstream_restriction_flag, 1));
373 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
374 if (bitstream_restriction_flag == 0) {
375 // We're adding one from scratch.
376 RETURN_FALSE_ON_FAIL(
377 AddBitstreamRestriction(destination, sps.max_num_ref_frames));
378 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
379 } else {
380 // We're replacing.
381 // motion_vectors_over_pic_boundaries_flag: u(1)
382 COPY_BITS(source, destination, bits_tmp, 1);
383 // max_bytes_per_pic_denom: ue(v)
384 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
385 // max_bits_per_mb_denom: ue(v)
386 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
387 // log2_max_mv_length_horizontal: ue(v)
388 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
389 // log2_max_mv_length_vertical: ue(v)
390 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
391 // ********* IMPORTANT! **********
392 // The next two are the ones we need to set to low numbers:
393 // max_num_reorder_frames: ue(v)
394 // max_dec_frame_buffering: ue(v)
395 // However, if they are already set to no greater than the numbers we
396 // want, then we don't need to be rewriting.
397 uint32_t max_num_reorder_frames, max_dec_frame_buffering;
398 RETURN_FALSE_ON_FAIL(
399 source->ReadExponentialGolomb(&max_num_reorder_frames));
400 RETURN_FALSE_ON_FAIL(
401 source->ReadExponentialGolomb(&max_dec_frame_buffering));
402 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
403 RETURN_FALSE_ON_FAIL(
404 destination->WriteExponentialGolomb(sps.max_num_ref_frames));
405 if (max_num_reorder_frames != 0 ||
406 max_dec_frame_buffering > sps.max_num_ref_frames) {
407 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
408 }
409 }
410 }
411 return true;
412 }
413
414 // Copies a VUI HRD parameters segment.
CopyHrdParameters(rtc::BitBuffer * source,rtc::BitBufferWriter * destination)415 bool CopyHrdParameters(rtc::BitBuffer* source,
416 rtc::BitBufferWriter* destination) {
417 uint32_t golomb_tmp;
418 uint32_t bits_tmp;
419
420 // cbp_cnt_minus1: ue(v)
421 uint32_t cbp_cnt_minus1;
422 COPY_EXP_GOLOMB(source, destination, cbp_cnt_minus1);
423 // bit_rate_scale and cbp_size_scale: u(4) each
424 COPY_BITS(source, destination, bits_tmp, 8);
425 for (size_t i = 0; i <= cbp_cnt_minus1; ++i) {
426 // bit_rate_value_minus1 and cbp_size_value_minus1: ue(v) each
427 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
428 COPY_EXP_GOLOMB(source, destination, golomb_tmp);
429 // cbr_flag: u(1)
430 COPY_BITS(source, destination, bits_tmp, 1);
431 }
432 // initial_cbp_removal_delay_length_minus1: u(5)
433 COPY_BITS(source, destination, bits_tmp, 5);
434 // cbp_removal_delay_length_minus1: u(5)
435 COPY_BITS(source, destination, bits_tmp, 5);
436 // dbp_output_delay_length_minus1: u(5)
437 COPY_BITS(source, destination, bits_tmp, 5);
438 // time_offset_length: u(5)
439 COPY_BITS(source, destination, bits_tmp, 5);
440 return true;
441 }
442
443 // These functions are similar to webrtc::H264SpsParser::Parse, and based on the
444 // same version of the H.264 standard. You can find it here:
445 // http://www.itu.int/rec/T-REC-H.264
446
447 // Adds a bitstream restriction VUI segment.
AddBitstreamRestriction(rtc::BitBufferWriter * destination,uint32_t max_num_ref_frames)448 bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
449 uint32_t max_num_ref_frames) {
450 // motion_vectors_over_pic_boundaries_flag: u(1)
451 // Default is 1 when not present.
452 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
453 // max_bytes_per_pic_denom: ue(v)
454 // Default is 2 when not present.
455 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(2));
456 // max_bits_per_mb_denom: ue(v)
457 // Default is 1 when not present.
458 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(1));
459 // log2_max_mv_length_horizontal: ue(v)
460 // log2_max_mv_length_vertical: ue(v)
461 // Both default to 16 when not present.
462 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
463 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
464
465 // ********* IMPORTANT! **********
466 // max_num_reorder_frames: ue(v)
467 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
468 // max_dec_frame_buffering: ue(v)
469 RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(max_num_ref_frames));
470 return true;
471 }
472
IsDefaultColorSpace(const ColorSpace & color_space)473 bool IsDefaultColorSpace(const ColorSpace& color_space) {
474 return color_space.range() != ColorSpace::RangeID::kFull &&
475 color_space.primaries() == ColorSpace::PrimaryID::kUnspecified &&
476 color_space.transfer() == ColorSpace::TransferID::kUnspecified &&
477 color_space.matrix() == ColorSpace::MatrixID::kUnspecified;
478 }
479
AddVideoSignalTypeInfo(rtc::BitBufferWriter * destination,const ColorSpace & color_space)480 bool AddVideoSignalTypeInfo(rtc::BitBufferWriter* destination,
481 const ColorSpace& color_space) {
482 // video_format: u(3).
483 RETURN_FALSE_ON_FAIL(destination->WriteBits(5, 3)); // 5 = Unspecified
484 // video_full_range_flag: u(1)
485 RETURN_FALSE_ON_FAIL(destination->WriteBits(
486 color_space.range() == ColorSpace::RangeID::kFull ? 1 : 0, 1));
487 // colour_description_present_flag: u(1)
488 RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
489 // colour_primaries: u(8)
490 RETURN_FALSE_ON_FAIL(
491 destination->WriteUInt8(static_cast<uint8_t>(color_space.primaries())));
492 // transfer_characteristics: u(8)
493 RETURN_FALSE_ON_FAIL(
494 destination->WriteUInt8(static_cast<uint8_t>(color_space.transfer())));
495 // matrix_coefficients: u(8)
496 RETURN_FALSE_ON_FAIL(
497 destination->WriteUInt8(static_cast<uint8_t>(color_space.matrix())));
498 return true;
499 }
500
CopyOrRewriteVideoSignalTypeInfo(rtc::BitBuffer * source,rtc::BitBufferWriter * destination,const ColorSpace * color_space,SpsVuiRewriter::ParseResult * out_vui_rewritten)501 bool CopyOrRewriteVideoSignalTypeInfo(
502 rtc::BitBuffer* source,
503 rtc::BitBufferWriter* destination,
504 const ColorSpace* color_space,
505 SpsVuiRewriter::ParseResult* out_vui_rewritten) {
506 // Read.
507 uint32_t video_signal_type_present_flag;
508 uint32_t video_format = 5; // H264 default: unspecified
509 uint32_t video_full_range_flag = 0; // H264 default: limited
510 uint32_t colour_description_present_flag = 0;
511 uint8_t colour_primaries = 3; // H264 default: unspecified
512 uint8_t transfer_characteristics = 3; // H264 default: unspecified
513 uint8_t matrix_coefficients = 3; // H264 default: unspecified
514 RETURN_FALSE_ON_FAIL(source->ReadBits(&video_signal_type_present_flag, 1));
515 if (video_signal_type_present_flag) {
516 RETURN_FALSE_ON_FAIL(source->ReadBits(&video_format, 3));
517 RETURN_FALSE_ON_FAIL(source->ReadBits(&video_full_range_flag, 1));
518 RETURN_FALSE_ON_FAIL(source->ReadBits(&colour_description_present_flag, 1));
519 if (colour_description_present_flag) {
520 RETURN_FALSE_ON_FAIL(source->ReadUInt8(&colour_primaries));
521 RETURN_FALSE_ON_FAIL(source->ReadUInt8(&transfer_characteristics));
522 RETURN_FALSE_ON_FAIL(source->ReadUInt8(&matrix_coefficients));
523 }
524 }
525
526 // Update.
527 uint32_t video_signal_type_present_flag_override =
528 video_signal_type_present_flag;
529 uint32_t video_format_override = video_format;
530 uint32_t video_full_range_flag_override = video_full_range_flag;
531 uint32_t colour_description_present_flag_override =
532 colour_description_present_flag;
533 uint8_t colour_primaries_override = colour_primaries;
534 uint8_t transfer_characteristics_override = transfer_characteristics;
535 uint8_t matrix_coefficients_override = matrix_coefficients;
536 if (color_space) {
537 if (IsDefaultColorSpace(*color_space)) {
538 video_signal_type_present_flag_override = 0;
539 } else {
540 video_signal_type_present_flag_override = 1;
541 video_format_override = 5; // unspecified
542
543 if (color_space->range() == ColorSpace::RangeID::kFull) {
544 video_full_range_flag_override = 1;
545 } else {
546 // ColorSpace::RangeID::kInvalid and kDerived are treated as limited.
547 video_full_range_flag_override = 0;
548 }
549
550 colour_description_present_flag_override =
551 color_space->primaries() != ColorSpace::PrimaryID::kUnspecified ||
552 color_space->transfer() != ColorSpace::TransferID::kUnspecified ||
553 color_space->matrix() != ColorSpace::MatrixID::kUnspecified;
554 colour_primaries_override =
555 static_cast<uint8_t>(color_space->primaries());
556 transfer_characteristics_override =
557 static_cast<uint8_t>(color_space->transfer());
558 matrix_coefficients_override =
559 static_cast<uint8_t>(color_space->matrix());
560 }
561 }
562
563 // Write.
564 RETURN_FALSE_ON_FAIL(
565 destination->WriteBits(video_signal_type_present_flag_override, 1));
566 if (video_signal_type_present_flag_override) {
567 RETURN_FALSE_ON_FAIL(destination->WriteBits(video_format_override, 3));
568 RETURN_FALSE_ON_FAIL(
569 destination->WriteBits(video_full_range_flag_override, 1));
570 RETURN_FALSE_ON_FAIL(
571 destination->WriteBits(colour_description_present_flag_override, 1));
572 if (colour_description_present_flag_override) {
573 RETURN_FALSE_ON_FAIL(destination->WriteUInt8(colour_primaries_override));
574 RETURN_FALSE_ON_FAIL(
575 destination->WriteUInt8(transfer_characteristics_override));
576 RETURN_FALSE_ON_FAIL(
577 destination->WriteUInt8(matrix_coefficients_override));
578 }
579 }
580
581 if (video_signal_type_present_flag_override !=
582 video_signal_type_present_flag ||
583 video_format_override != video_format ||
584 video_full_range_flag_override != video_full_range_flag ||
585 colour_description_present_flag_override !=
586 colour_description_present_flag ||
587 colour_primaries_override != colour_primaries ||
588 transfer_characteristics_override != transfer_characteristics ||
589 matrix_coefficients_override != matrix_coefficients) {
590 *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
591 }
592
593 return true;
594 }
595
CopyRemainingBits(rtc::BitBuffer * source,rtc::BitBufferWriter * destination)596 bool CopyRemainingBits(rtc::BitBuffer* source,
597 rtc::BitBufferWriter* destination) {
598 uint32_t bits_tmp;
599 // Try to get at least the destination aligned.
600 if (source->RemainingBitCount() > 0 && source->RemainingBitCount() % 8 != 0) {
601 size_t misaligned_bits = source->RemainingBitCount() % 8;
602 COPY_BITS(source, destination, bits_tmp, misaligned_bits);
603 }
604 while (source->RemainingBitCount() > 0) {
605 auto count = rtc::SafeMin<size_t>(32u, source->RemainingBitCount());
606 COPY_BITS(source, destination, bits_tmp, count);
607 }
608 // TODO(noahric): The last byte could be all zeroes now, which we should just
609 // strip.
610 return true;
611 }
612
613 } // namespace
614
615 } // namespace webrtc
616