tor-browser

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

rtp_format_h264.cc (11294B)


      1 /*
      2 *  Copyright (c) 2014 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/rtp_rtcp/source/rtp_format_h264.h"
     12 
     13 #include <cstddef>
     14 #include <cstdint>
     15 #include <cstring>
     16 #include <vector>
     17 
     18 #include "absl/algorithm/container.h"
     19 #include "api/array_view.h"
     20 #include "common_video/h264/h264_common.h"
     21 #include "modules/rtp_rtcp/source/byte_io.h"
     22 #include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
     23 #include "modules/video_coding/codecs/h264/include/h264_globals.h"
     24 #include "rtc_base/checks.h"
     25 #include "rtc_base/logging.h"
     26 
     27 namespace webrtc {
     28 namespace {
     29 
     30 constexpr size_t kNalHeaderSize = 1;
     31 constexpr size_t kFuAHeaderSize = 2;
     32 constexpr size_t kLengthFieldSize = 2;
     33 
     34 }  // namespace
     35 
     36 RtpPacketizerH264::RtpPacketizerH264(ArrayView<const uint8_t> payload,
     37                                     PayloadSizeLimits limits,
     38                                     H264PacketizationMode packetization_mode)
     39    : limits_(limits), num_packets_left_(0) {
     40  // Guard against uninitialized memory in packetization_mode.
     41  RTC_CHECK(packetization_mode == H264PacketizationMode::NonInterleaved ||
     42            packetization_mode == H264PacketizationMode::SingleNalUnit);
     43 
     44  for (const auto& nalu : H264::FindNaluIndices(payload)) {
     45    input_fragments_.push_back(
     46        payload.subview(nalu.payload_start_offset, nalu.payload_size));
     47  }
     48  bool has_empty_fragments = absl::c_any_of(
     49      input_fragments_,
     50      [](const ArrayView<const uint8_t> fragment) { return fragment.empty(); });
     51  if (has_empty_fragments || !GeneratePackets(packetization_mode)) {
     52    // If empty fragments were found or we failed to generate all the packets,
     53    // discard already generated packets in case the caller would ignore the
     54    // return value and still try to call NextPacket().
     55    num_packets_left_ = 0;
     56    while (!packets_.empty()) {
     57      packets_.pop();
     58    }
     59  }
     60 }
     61 
     62 RtpPacketizerH264::~RtpPacketizerH264() = default;
     63 
     64 size_t RtpPacketizerH264::NumPackets() const {
     65  return num_packets_left_;
     66 }
     67 
     68 bool RtpPacketizerH264::GeneratePackets(
     69    H264PacketizationMode packetization_mode) {
     70  for (size_t i = 0; i < input_fragments_.size();) {
     71    RTC_DCHECK(!input_fragments_[i].empty());
     72    switch (packetization_mode) {
     73      case H264PacketizationMode::SingleNalUnit:
     74        if (!PacketizeSingleNalu(i))
     75          return false;
     76        ++i;
     77        break;
     78      case H264PacketizationMode::NonInterleaved:
     79        int fragment_len = input_fragments_[i].size();
     80        int single_packet_capacity = limits_.max_payload_len;
     81        if (input_fragments_.size() == 1)
     82          single_packet_capacity -= limits_.single_packet_reduction_len;
     83        else if (i == 0)
     84          single_packet_capacity -= limits_.first_packet_reduction_len;
     85        else if (i + 1 == input_fragments_.size())
     86          single_packet_capacity -= limits_.last_packet_reduction_len;
     87 
     88        if (fragment_len > single_packet_capacity) {
     89          if (!PacketizeFuA(i))
     90            return false;
     91          ++i;
     92        } else {
     93          i = PacketizeStapA(i);
     94        }
     95        break;
     96    }
     97  }
     98  return true;
     99 }
    100 
    101 bool RtpPacketizerH264::PacketizeFuA(size_t fragment_index) {
    102  // Fragment payload into packets (FU-A).
    103  ArrayView<const uint8_t> fragment = input_fragments_[fragment_index];
    104 
    105  PayloadSizeLimits limits = limits_;
    106  // Leave room for the FU-A header.
    107  limits.max_payload_len -= kFuAHeaderSize;
    108  // Update single/first/last packet reductions unless it is single/first/last
    109  // fragment.
    110  if (input_fragments_.size() != 1) {
    111    // if this fragment is put into a single packet, it might still be the
    112    // first or the last packet in the whole sequence of packets.
    113    if (fragment_index == input_fragments_.size() - 1) {
    114      limits.single_packet_reduction_len = limits_.last_packet_reduction_len;
    115    } else if (fragment_index == 0) {
    116      limits.single_packet_reduction_len = limits_.first_packet_reduction_len;
    117    } else {
    118      limits.single_packet_reduction_len = 0;
    119    }
    120  }
    121  if (fragment_index != 0)
    122    limits.first_packet_reduction_len = 0;
    123  if (fragment_index != input_fragments_.size() - 1)
    124    limits.last_packet_reduction_len = 0;
    125 
    126  // Strip out the original header.
    127  size_t payload_left = fragment.size() - kNalHeaderSize;
    128  int offset = kNalHeaderSize;
    129 
    130  std::vector<int> payload_sizes = SplitAboutEqually(payload_left, limits);
    131  if (payload_sizes.empty())
    132    return false;
    133 
    134  for (size_t i = 0; i < payload_sizes.size(); ++i) {
    135    int packet_length = payload_sizes[i];
    136    RTC_CHECK_GT(packet_length, 0);
    137    packets_.push(PacketUnit(fragment.subview(offset, packet_length),
    138                             /*first_fragment=*/i == 0,
    139                             /*last_fragment=*/i == payload_sizes.size() - 1,
    140                             false, fragment[0]));
    141    offset += packet_length;
    142    payload_left -= packet_length;
    143  }
    144  num_packets_left_ += payload_sizes.size();
    145  RTC_CHECK_EQ(0, payload_left);
    146  return true;
    147 }
    148 
    149 size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) {
    150  // Aggregate fragments into one packet (STAP-A).
    151  size_t payload_size_left = limits_.max_payload_len;
    152  int aggregated_fragments = 0;
    153  size_t fragment_headers_length = 0;
    154  ArrayView<const uint8_t> fragment = input_fragments_[fragment_index];
    155  RTC_CHECK_GE(payload_size_left, fragment.size());
    156  ++num_packets_left_;
    157 
    158  const bool has_first_fragment = fragment_index == 0;
    159  auto payload_size_needed = [&] {
    160    size_t fragment_size = fragment.size() + fragment_headers_length;
    161    bool has_last_fragment = fragment_index == input_fragments_.size() - 1;
    162    if (has_first_fragment && has_last_fragment) {
    163      return fragment_size + limits_.single_packet_reduction_len;
    164    } else if (has_first_fragment) {
    165      return fragment_size + limits_.first_packet_reduction_len;
    166    } else if (has_last_fragment) {
    167      return fragment_size + limits_.last_packet_reduction_len;
    168    } else {
    169      return fragment_size;
    170    }
    171  };
    172  while (payload_size_left >= payload_size_needed()) {
    173    RTC_CHECK_GT(fragment.size(), 0);
    174 
    175    packets_.push(PacketUnit(fragment, /*first=*/aggregated_fragments == 0,
    176                             /*last=*/false, /*aggregated=*/true, fragment[0]));
    177    payload_size_left -= fragment.size();
    178    payload_size_left -= fragment_headers_length;
    179 
    180    fragment_headers_length = kLengthFieldSize;
    181    // If we are going to try to aggregate more fragments into this packet
    182    // we need to add the STAP-A NALU header and a length field for the first
    183    // NALU of this packet.
    184    if (aggregated_fragments == 0)
    185      fragment_headers_length += kNalHeaderSize + kLengthFieldSize;
    186    ++aggregated_fragments;
    187 
    188    // Next fragment.
    189    ++fragment_index;
    190    if (fragment_index == input_fragments_.size())
    191      break;
    192    fragment = input_fragments_[fragment_index];
    193  }
    194  RTC_CHECK_GT(aggregated_fragments, 0);
    195  packets_.back().last_fragment = true;
    196  return fragment_index;
    197 }
    198 
    199 bool RtpPacketizerH264::PacketizeSingleNalu(size_t fragment_index) {
    200  // Add a single NALU to the queue, no aggregation.
    201  size_t payload_size_left = limits_.max_payload_len;
    202  if (input_fragments_.size() == 1)
    203    payload_size_left -= limits_.single_packet_reduction_len;
    204  else if (fragment_index == 0)
    205    payload_size_left -= limits_.first_packet_reduction_len;
    206  else if (fragment_index + 1 == input_fragments_.size())
    207    payload_size_left -= limits_.last_packet_reduction_len;
    208  ArrayView<const uint8_t> fragment = input_fragments_[fragment_index];
    209  if (payload_size_left < fragment.size()) {
    210    RTC_LOG(LS_ERROR) << "Failed to fit a fragment to packet in SingleNalu "
    211                         "packetization mode. Payload size left "
    212                      << payload_size_left << ", fragment length "
    213                      << fragment.size() << ", packet capacity "
    214                      << limits_.max_payload_len;
    215    return false;
    216  }
    217  RTC_CHECK(!fragment.empty());
    218  packets_.push(PacketUnit(fragment, /*first=*/true, /*last=*/true,
    219                           /*aggregated=*/false, fragment[0]));
    220  ++num_packets_left_;
    221  return true;
    222 }
    223 
    224 bool RtpPacketizerH264::NextPacket(RtpPacketToSend* rtp_packet) {
    225  RTC_DCHECK(rtp_packet);
    226  if (packets_.empty()) {
    227    return false;
    228  }
    229 
    230  PacketUnit packet = packets_.front();
    231  if (packet.first_fragment && packet.last_fragment) {
    232    // Single NAL unit packet.
    233    rtp_packet->SetPayload(packet.source_fragment);
    234    packets_.pop();
    235    input_fragments_.pop_front();
    236  } else if (packet.aggregated) {
    237    NextAggregatePacket(rtp_packet);
    238  } else {
    239    NextFragmentPacket(rtp_packet);
    240  }
    241  rtp_packet->SetMarker(packets_.empty());
    242  --num_packets_left_;
    243  return true;
    244 }
    245 
    246 void RtpPacketizerH264::NextAggregatePacket(RtpPacketToSend* rtp_packet) {
    247  // Reserve maximum available payload, set actual payload size later.
    248  size_t payload_capacity = rtp_packet->FreeCapacity();
    249  RTC_CHECK_GE(payload_capacity, kNalHeaderSize);
    250  uint8_t* buffer = rtp_packet->AllocatePayload(payload_capacity);
    251  RTC_DCHECK(buffer);
    252  PacketUnit* packet = &packets_.front();
    253  RTC_CHECK(packet->first_fragment);
    254  // STAP-A NALU header.
    255  buffer[0] =
    256      (packet->header & (kH264FBit | kH264NriMask)) | H264::NaluType::kStapA;
    257  size_t index = kNalHeaderSize;
    258  bool is_last_fragment = packet->last_fragment;
    259  while (packet->aggregated) {
    260    ArrayView<const uint8_t> fragment = packet->source_fragment;
    261    RTC_CHECK_LE(index + kLengthFieldSize + fragment.size(), payload_capacity);
    262    // Add NAL unit length field.
    263    ByteWriter<uint16_t>::WriteBigEndian(&buffer[index], fragment.size());
    264    index += kLengthFieldSize;
    265    // Add NAL unit.
    266    memcpy(&buffer[index], fragment.data(), fragment.size());
    267    index += fragment.size();
    268    packets_.pop();
    269    input_fragments_.pop_front();
    270    if (is_last_fragment)
    271      break;
    272    packet = &packets_.front();
    273    is_last_fragment = packet->last_fragment;
    274  }
    275  RTC_CHECK(is_last_fragment);
    276  rtp_packet->SetPayloadSize(index);
    277 }
    278 
    279 void RtpPacketizerH264::NextFragmentPacket(RtpPacketToSend* rtp_packet) {
    280  PacketUnit* packet = &packets_.front();
    281  // NAL unit fragmented over multiple packets (FU-A).
    282  // We do not send original NALU header, so it will be replaced by the
    283  // FU indicator header of the first packet.
    284  uint8_t fu_indicator =
    285      (packet->header & (kH264FBit | kH264NriMask)) | H264::NaluType::kFuA;
    286  uint8_t fu_header = 0;
    287 
    288  // S | E | R | 5 bit type.
    289  fu_header |= (packet->first_fragment ? kH264SBit : 0);
    290  fu_header |= (packet->last_fragment ? kH264EBit : 0);
    291  uint8_t type = packet->header & kH264TypeMask;
    292  fu_header |= type;
    293  ArrayView<const uint8_t> fragment = packet->source_fragment;
    294  uint8_t* buffer =
    295      rtp_packet->AllocatePayload(kFuAHeaderSize + fragment.size());
    296  buffer[0] = fu_indicator;
    297  buffer[1] = fu_header;
    298  memcpy(buffer + kFuAHeaderSize, fragment.data(), fragment.size());
    299  if (packet->last_fragment)
    300    input_fragments_.pop_front();
    301  packets_.pop();
    302 }
    303 
    304 }  // namespace webrtc