xref: /aosp_15_r20/external/webrtc/common_video/h264/sps_vui_rewriter.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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