• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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