tor-browser

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

ulpfec_receiver.cc (10817B)


      1 /*
      2 *  Copyright (c) 2012 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/ulpfec_receiver.h"
     12 
     13 #include <cstddef>
     14 #include <cstdint>
     15 #include <memory>
     16 #include <utility>
     17 #include <vector>
     18 
     19 #include "api/scoped_refptr.h"
     20 #include "api/sequence_checker.h"
     21 #include "api/units/time_delta.h"
     22 #include "api/units/timestamp.h"
     23 #include "modules/rtp_rtcp/include/recovered_packet_receiver.h"
     24 #include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
     25 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
     26 #include "modules/rtp_rtcp/source/forward_error_correction.h"
     27 #include "modules/rtp_rtcp/source/rtp_packet_received.h"
     28 #include "rtc_base/checks.h"
     29 #include "rtc_base/logging.h"
     30 #include "system_wrappers/include/clock.h"
     31 #include "system_wrappers/include/metrics.h"
     32 
     33 namespace webrtc {
     34 
     35 UlpfecReceiver::UlpfecReceiver(uint32_t ssrc,
     36                               int ulpfec_payload_type,
     37                               RecoveredPacketReceiver* callback,
     38                               Clock* clock)
     39    : ssrc_(ssrc),
     40      ulpfec_payload_type_(ulpfec_payload_type),
     41      clock_(clock),
     42      recovered_packet_callback_(callback),
     43      fec_(ForwardErrorCorrection::CreateUlpfec(ssrc_)) {
     44  // TODO(tommi, brandtr): Once considerations for red have been split
     45  // away from this implementation, we can require the ulpfec payload type
     46  // to always be valid and use uint8 for storage (as is done elsewhere).
     47  RTC_DCHECK_GE(ulpfec_payload_type_, -1);
     48 }
     49 
     50 UlpfecReceiver::~UlpfecReceiver() {
     51  RTC_DCHECK_RUN_ON(&sequence_checker_);
     52 
     53  if (packet_counter_.first_packet_time != Timestamp::MinusInfinity()) {
     54    const Timestamp now = clock_->CurrentTime();
     55    TimeDelta elapsed = (now - packet_counter_.first_packet_time);
     56    if (elapsed >= metrics::kMinRunTime) {
     57      if (packet_counter_.num_packets > 0) {
     58        RTC_HISTOGRAM_PERCENTAGE(
     59            "WebRTC.Video.ReceivedFecPacketsInPercent",
     60            static_cast<int>(packet_counter_.num_fec_packets * 100 /
     61                             packet_counter_.num_packets));
     62      }
     63      if (packet_counter_.num_fec_packets > 0) {
     64        RTC_HISTOGRAM_PERCENTAGE(
     65            "WebRTC.Video.RecoveredMediaPacketsInPercentOfFec",
     66            static_cast<int>(packet_counter_.num_recovered_packets * 100 /
     67                             packet_counter_.num_fec_packets));
     68      }
     69      if (ulpfec_payload_type_ != -1) {
     70        RTC_HISTOGRAM_COUNTS_10000(
     71            "WebRTC.Video.FecBitrateReceivedInKbps",
     72            static_cast<int>(packet_counter_.num_bytes * 8 / elapsed.seconds() /
     73                             1000));
     74      }
     75    }
     76  }
     77 
     78  received_packets_.clear();
     79  fec_->ResetState(&recovered_packets_);
     80 }
     81 
     82 FecPacketCounter UlpfecReceiver::GetPacketCounter() const {
     83  RTC_DCHECK_RUN_ON(&sequence_checker_);
     84  return packet_counter_;
     85 }
     86 
     87 //     0                   1                    2                   3
     88 //     0 1 2 3 4 5 6 7 8 9 0 1 2 3  4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     89 //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     90 //    |F|   block PT  |  timestamp offset         |   block length    |
     91 //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     92 //
     93 //
     94 // RFC 2198          RTP Payload for Redundant Audio Data    September 1997
     95 //
     96 //    The bits in the header are specified as follows:
     97 //
     98 //    F: 1 bit First bit in header indicates whether another header block
     99 //        follows.  If 1 further header blocks follow, if 0 this is the
    100 //        last header block.
    101 //        If 0 there is only 1 byte RED header
    102 //
    103 //    block PT: 7 bits RTP payload type for this block.
    104 //
    105 //    timestamp offset:  14 bits Unsigned offset of timestamp of this block
    106 //        relative to timestamp given in RTP header.  The use of an unsigned
    107 //        offset implies that redundant data must be sent after the primary
    108 //        data, and is hence a time to be subtracted from the current
    109 //        timestamp to determine the timestamp of the data for which this
    110 //        block is the redundancy.
    111 //
    112 //    block length:  10 bits Length in bytes of the corresponding data
    113 //        block excluding header.
    114 
    115 bool UlpfecReceiver::AddReceivedRedPacket(const RtpPacketReceived& rtp_packet) {
    116  RTC_DCHECK_RUN_ON(&sequence_checker_);
    117  // TODO(bugs.webrtc.org/11993): We get here via Call::DeliverRtp, so should be
    118  // moved to the network thread.
    119 
    120  if (rtp_packet.Ssrc() != ssrc_) {
    121    RTC_LOG(LS_WARNING)
    122        << "Received RED packet with different SSRC than expected; dropping.";
    123    return false;
    124  }
    125  if (rtp_packet.size() > IP_PACKET_SIZE) {
    126    RTC_LOG(LS_WARNING) << "Received RED packet with length exceeds maximum IP "
    127                           "packet size; dropping.";
    128    return false;
    129  }
    130 
    131  static constexpr uint8_t kRedHeaderLength = 1;
    132 
    133  if (rtp_packet.payload_size() == 0) {
    134    RTC_LOG(LS_WARNING) << "Corrupt/truncated FEC packet.";
    135    return false;
    136  }
    137 
    138  // Remove RED header of incoming packet and store as a virtual RTP packet.
    139  auto received_packet =
    140      std::make_unique<ForwardErrorCorrection::ReceivedPacket>();
    141  received_packet->pkt = new ForwardErrorCorrection::Packet();
    142 
    143  // Get payload type from RED header and sequence number from RTP header.
    144  uint8_t payload_type = rtp_packet.payload()[0] & 0x7f;
    145  received_packet->is_fec = payload_type == ulpfec_payload_type_;
    146  received_packet->is_recovered = rtp_packet.recovered();
    147  received_packet->ssrc = rtp_packet.Ssrc();
    148  received_packet->seq_num = rtp_packet.SequenceNumber();
    149  received_packet->extensions = rtp_packet.extension_manager();
    150 
    151  if (rtp_packet.payload()[0] & 0x80) {
    152    // f bit set in RED header, i.e. there are more than one RED header blocks.
    153    // WebRTC never generates multiple blocks in a RED packet for FEC.
    154    RTC_LOG(LS_WARNING) << "More than 1 block in RED packet is not supported.";
    155    return false;
    156  }
    157 
    158  ++packet_counter_.num_packets;
    159  packet_counter_.num_bytes += rtp_packet.size();
    160  if (packet_counter_.first_packet_time == Timestamp::MinusInfinity()) {
    161    packet_counter_.first_packet_time = clock_->CurrentTime();
    162  }
    163 
    164  if (received_packet->is_fec) {
    165    ++packet_counter_.num_fec_packets;
    166    // everything behind the RED header
    167    received_packet->pkt->data =
    168        rtp_packet.Buffer().Slice(rtp_packet.headers_size() + kRedHeaderLength,
    169                                  rtp_packet.payload_size() - kRedHeaderLength);
    170  } else {
    171    received_packet->pkt->data.EnsureCapacity(rtp_packet.size() -
    172                                              kRedHeaderLength);
    173    // Copy RTP header.
    174    received_packet->pkt->data.SetData(rtp_packet.data(),
    175                                       rtp_packet.headers_size());
    176    // Set payload type.
    177    uint8_t& payload_type_byte = received_packet->pkt->data.MutableData()[1];
    178    payload_type_byte &= 0x80;          // Reset RED payload type.
    179    payload_type_byte += payload_type;  // Set media payload type.
    180    // Copy payload and padding data, after the RED header.
    181    received_packet->pkt->data.AppendData(
    182        rtp_packet.data() + rtp_packet.headers_size() + kRedHeaderLength,
    183        rtp_packet.size() - rtp_packet.headers_size() - kRedHeaderLength);
    184  }
    185 
    186  if (!received_packet->pkt->data.empty()) {
    187    received_packets_.push_back(std::move(received_packet));
    188  }
    189  return true;
    190 }
    191 
    192 void UlpfecReceiver::ProcessReceivedFec() {
    193  RTC_DCHECK_RUN_ON(&sequence_checker_);
    194 
    195  // If we iterate over `received_packets_` and it contains a packet that cause
    196  // us to recurse back to this function (for example a RED packet encapsulating
    197  // a RED packet), then we will recurse forever. To avoid this we swap
    198  // `received_packets_` with an empty vector so that the next recursive call
    199  // wont iterate over the same packet again. This also solves the problem of
    200  // not modifying the vector we are currently iterating over (packets are added
    201  // in AddReceivedRedPacket).
    202  std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
    203      received_packets;
    204  received_packets.swap(received_packets_);
    205  RtpHeaderExtensionMap* last_recovered_extension_map = nullptr;
    206  size_t num_recovered_packets = 0;
    207 
    208  for (const auto& received_packet : received_packets) {
    209    // Send received media packet to VCM.
    210    if (!received_packet->is_fec) {
    211      ForwardErrorCorrection::Packet* packet = received_packet->pkt.get();
    212 
    213      RtpPacketReceived rtp_packet(&received_packet->extensions);
    214      if (!rtp_packet.Parse(std::move(packet->data))) {
    215        RTC_LOG(LS_WARNING) << "Corrupted media packet";
    216        continue;
    217      }
    218      recovered_packet_callback_->OnRecoveredPacket(rtp_packet);
    219      // Some header extensions need to be zeroed in `received_packet` since
    220      // they are written to the packet after FEC encoding. We try to do it
    221      // without a copy of the underlying Copy-On-Write buffer, but if a
    222      // reference is held by `recovered_packet_callback_->OnRecoveredPacket` a
    223      // copy will still be made in 'rtp_packet.ZeroMutableExtensions()'.
    224      rtp_packet.ZeroMutableExtensions();
    225      packet->data = rtp_packet.Buffer();
    226    }
    227    if (!received_packet->is_recovered) {
    228      // Do not pass recovered packets to FEC. Recovered packet might have
    229      // different set of the RTP header extensions and thus different byte
    230      // representation than the original packet, That will corrupt
    231      // FEC calculation.
    232      ForwardErrorCorrection::DecodeFecResult decode_result =
    233          fec_->DecodeFec(*received_packet, &recovered_packets_);
    234      last_recovered_extension_map = &received_packet->extensions;
    235      num_recovered_packets += decode_result.num_recovered_packets;
    236    }
    237  }
    238 
    239  if (num_recovered_packets == 0) {
    240    return;
    241  }
    242 
    243  // Send any recovered media packets to VCM.
    244  for (const auto& recovered_packet : recovered_packets_) {
    245    if (recovered_packet->returned) {
    246      // Already sent to the VCM and the jitter buffer.
    247      continue;
    248    }
    249    ForwardErrorCorrection::Packet* packet = recovered_packet->pkt.get();
    250    ++packet_counter_.num_recovered_packets;
    251    // Set this flag first; in case the recovered packet carries a RED
    252    // header, OnRecoveredPacket will recurse back here.
    253    recovered_packet->returned = true;
    254    RtpPacketReceived parsed_packet(last_recovered_extension_map);
    255    if (!parsed_packet.Parse(packet->data)) {
    256      continue;
    257    }
    258    parsed_packet.set_recovered(true);
    259    recovered_packet_callback_->OnRecoveredPacket(parsed_packet);
    260  }
    261 }
    262 
    263 }  // namespace webrtc