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