tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

sps_vui_rewriter.cc (23674B)


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