tor-browser

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

h264_sps_pps_tracker.cc (8037B)


      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 #include "modules/video_coding/h264_sps_pps_tracker.h"
     12 
     13 #include <cstddef>
     14 #include <cstdint>
     15 #include <optional>
     16 #include <utility>
     17 #include <vector>
     18 
     19 #include "api/array_view.h"
     20 #include "api/video/video_codec_type.h"
     21 #include "common_video/h264/h264_common.h"
     22 #include "common_video/h264/pps_parser.h"
     23 #include "common_video/h264/sps_parser.h"
     24 #include "modules/rtp_rtcp/source/rtp_video_header.h"
     25 #include "modules/video_coding/codecs/h264/include/h264_globals.h"
     26 #include "rtc_base/byte_buffer.h"
     27 #include "rtc_base/checks.h"
     28 #include "rtc_base/logging.h"
     29 
     30 namespace webrtc {
     31 namespace video_coding {
     32 
     33 namespace {
     34 const uint8_t start_code_h264[] = {0, 0, 0, 1};
     35 }  // namespace
     36 
     37 H264SpsPpsTracker::FixedBitstream H264SpsPpsTracker::CopyAndFixBitstream(
     38    ArrayView<const uint8_t> bitstream,
     39    RTPVideoHeader* video_header) {
     40  RTC_DCHECK(video_header);
     41  RTC_DCHECK(video_header->codec == kVideoCodecH264);
     42  RTC_DCHECK_GT(bitstream.size(), 0);
     43 
     44  auto& h264_header =
     45      std::get<RTPVideoHeaderH264>(video_header->video_type_header);
     46 
     47  bool append_sps_pps = false;
     48  auto sps = sps_data_.end();
     49  auto pps = pps_data_.end();
     50 
     51  for (const NaluInfo& nalu : h264_header.nalus) {
     52    switch (nalu.type) {
     53      case H264::NaluType::kSps: {
     54        SpsInfo& sps_info = sps_data_[nalu.sps_id];
     55        sps_info.width = video_header->width;
     56        sps_info.height = video_header->height;
     57        break;
     58      }
     59      case H264::NaluType::kPps: {
     60        pps_data_[nalu.pps_id].sps_id = nalu.sps_id;
     61        break;
     62      }
     63      case H264::NaluType::kIdr: {
     64        // If this is the first packet of an IDR, make sure we have the required
     65        // SPS/PPS and also calculate how much extra space we need in the buffer
     66        // to prepend the SPS/PPS to the bitstream with start codes.
     67        if (video_header->is_first_packet_in_frame) {
     68          if (nalu.pps_id == -1) {
     69            RTC_LOG(LS_WARNING) << "No PPS id in IDR nalu.";
     70            return {.action = kRequestKeyframe};
     71          }
     72 
     73          pps = pps_data_.find(nalu.pps_id);
     74          if (pps == pps_data_.end()) {
     75            RTC_LOG(LS_WARNING)
     76                << "No PPS with id << " << nalu.pps_id << " received";
     77            return {.action = kRequestKeyframe};
     78          }
     79 
     80          sps = sps_data_.find(pps->second.sps_id);
     81          if (sps == sps_data_.end()) {
     82            RTC_LOG(LS_WARNING)
     83                << "No SPS with id << " << pps->second.sps_id << " received";
     84            return {.action = kRequestKeyframe};
     85          }
     86 
     87          // Since the first packet of every keyframe should have its width and
     88          // height set we set it here in the case of it being supplied out of
     89          // band.
     90          video_header->width = sps->second.width;
     91          video_header->height = sps->second.height;
     92 
     93          // If the SPS/PPS was supplied out of band then we will have saved
     94          // the actual bitstream in `data`.
     95          if (!sps->second.data.empty() && !pps->second.data.empty()) {
     96            append_sps_pps = true;
     97          }
     98        }
     99        break;
    100      }
    101      default:
    102        break;
    103    }
    104  }
    105 
    106  RTC_CHECK(!append_sps_pps ||
    107            (sps != sps_data_.end() && pps != pps_data_.end()));
    108 
    109  // Calculate how much space we need for the rest of the bitstream.
    110  size_t required_size = 0;
    111 
    112  if (append_sps_pps) {
    113    required_size += sps->second.data.size() + sizeof(start_code_h264);
    114    required_size += pps->second.data.size() + sizeof(start_code_h264);
    115  }
    116 
    117  if (h264_header.packetization_type == kH264StapA) {
    118    ByteBufferReader nalu(bitstream.subview(1));
    119    while (nalu.Length() > 0) {
    120      required_size += sizeof(start_code_h264);
    121 
    122      // The first two bytes describe the length of a segment.
    123      uint16_t segment_length;
    124      if (!nalu.ReadUInt16(&segment_length))
    125        return {.action = kDrop};
    126      if (segment_length == 0 || segment_length > nalu.Length()) {
    127        return {.action = kDrop};
    128      }
    129      required_size += segment_length;
    130      nalu.Consume(segment_length);
    131    }
    132  } else {
    133    if (!h264_header.nalus.empty()) {
    134      required_size += sizeof(start_code_h264);
    135    }
    136    required_size += bitstream.size();
    137  }
    138 
    139  // Then we copy to the new buffer.
    140  H264SpsPpsTracker::FixedBitstream fixed;
    141  fixed.bitstream.EnsureCapacity(required_size);
    142 
    143  if (append_sps_pps) {
    144    // Insert SPS.
    145    fixed.bitstream.AppendData(start_code_h264);
    146    fixed.bitstream.AppendData(sps->second.data);
    147 
    148    // Insert PPS.
    149    fixed.bitstream.AppendData(start_code_h264);
    150    fixed.bitstream.AppendData(pps->second.data);
    151 
    152    // Update codec header to reflect the newly added SPS and PPS.
    153    h264_header.nalus.push_back(
    154        {.type = H264::NaluType::kSps, .sps_id = sps->first, .pps_id = -1});
    155    h264_header.nalus.push_back({.type = H264::NaluType::kPps,
    156                                 .sps_id = sps->first,
    157                                 .pps_id = pps->first});
    158  }
    159 
    160  // Copy the rest of the bitstream and insert start codes.
    161  if (h264_header.packetization_type == kH264StapA) {
    162    ByteBufferReader nalu(bitstream.subview(1));
    163    while (nalu.Length() > 0) {
    164      fixed.bitstream.AppendData(start_code_h264);
    165 
    166      // The first two bytes describe the length of a segment.
    167      uint16_t segment_length;
    168      if (!nalu.ReadUInt16(&segment_length))
    169        return {.action = kDrop};
    170      if (segment_length == 0 || segment_length > nalu.Length()) {
    171        return {.action = kDrop};
    172      }
    173      fixed.bitstream.AppendData(nalu.Data(), segment_length);
    174      nalu.Consume(segment_length);
    175    }
    176  } else {
    177    if (!h264_header.nalus.empty()) {
    178      fixed.bitstream.AppendData(start_code_h264);
    179    }
    180    fixed.bitstream.AppendData(bitstream.data(), bitstream.size());
    181  }
    182 
    183  fixed.action = kInsert;
    184  return fixed;
    185 }
    186 
    187 void H264SpsPpsTracker::InsertSpsPpsNalus(const std::vector<uint8_t>& sps,
    188                                          const std::vector<uint8_t>& pps) {
    189  constexpr size_t kNaluHeaderOffset = 1;
    190  if (sps.size() < kNaluHeaderOffset) {
    191    RTC_LOG(LS_WARNING) << "SPS size  " << sps.size() << " is smaller than "
    192                        << kNaluHeaderOffset;
    193    return;
    194  }
    195  if ((sps[0] & 0x1f) != H264::NaluType::kSps) {
    196    RTC_LOG(LS_WARNING) << "SPS Nalu header missing";
    197    return;
    198  }
    199  if (pps.size() < kNaluHeaderOffset) {
    200    RTC_LOG(LS_WARNING) << "PPS size  " << pps.size() << " is smaller than "
    201                        << kNaluHeaderOffset;
    202    return;
    203  }
    204  if ((pps[0] & 0x1f) != H264::NaluType::kPps) {
    205    RTC_LOG(LS_WARNING) << "SPS Nalu header missing";
    206    return;
    207  }
    208  std::optional<SpsParser::SpsState> parsed_sps = SpsParser::ParseSps(
    209      ArrayView<const uint8_t>(sps).subview(kNaluHeaderOffset));
    210  std::optional<PpsParser::PpsState> parsed_pps = PpsParser::ParsePps(
    211      ArrayView<const uint8_t>(pps).subview(kNaluHeaderOffset));
    212 
    213  if (!parsed_sps) {
    214    RTC_LOG(LS_WARNING) << "Failed to parse SPS.";
    215  }
    216 
    217  if (!parsed_pps) {
    218    RTC_LOG(LS_WARNING) << "Failed to parse PPS.";
    219  }
    220 
    221  if (!parsed_pps || !parsed_sps) {
    222    return;
    223  }
    224 
    225  SpsInfo sps_info;
    226  sps_info.width = parsed_sps->width;
    227  sps_info.height = parsed_sps->height;
    228  sps_info.data.SetData(sps);
    229  sps_data_[parsed_sps->id] = std::move(sps_info);
    230 
    231  PpsInfo pps_info;
    232  pps_info.sps_id = parsed_pps->sps_id;
    233  pps_info.data.SetData(pps);
    234  pps_data_[parsed_pps->id] = std::move(pps_info);
    235 
    236  RTC_LOG(LS_INFO) << "Inserted SPS id " << parsed_sps->id << " and PPS id "
    237                   << parsed_pps->id << " (referencing SPS "
    238                   << parsed_pps->sps_id << ")";
    239 }
    240 
    241 }  // namespace video_coding
    242 }  // namespace webrtc