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