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