blob: fdfa475186c3583e4b89912d6dab7e787d897143 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
pwestin@webrtc.org5dad00b2012-01-30 13:05:29 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/rtp_rtcp/source/ulpfec_receiver_impl.h"
andrew@webrtc.org7fe219f2012-02-01 02:40:37 +000012
kwiberg84be5112016-04-27 01:19:58 -070013#include <memory>
brandtr35c480c2016-08-09 01:23:23 -070014#include <utility>
kwiberg84be5112016-04-27 01:19:58 -070015
Mirko Bonadeid9708072019-01-25 20:26:48 +010016#include "api/scoped_refptr.h"
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +020017#include "modules/rtp_rtcp/source/rtp_packet_received.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/logging.h"
Niels Möllerb5997872019-01-23 08:45:57 +010019#include "rtc_base/time_utils.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000020
niklase@google.com470e71d2011-07-07 08:21:25 +000021namespace webrtc {
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +000022
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +020023std::unique_ptr<UlpfecReceiver> UlpfecReceiver::Create(
24 uint32_t ssrc,
25 RecoveredPacketReceiver* callback,
26 rtc::ArrayView<const RtpExtension> extensions) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020027 return std::make_unique<UlpfecReceiverImpl>(ssrc, callback, extensions);
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +000028}
29
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +020030UlpfecReceiverImpl::UlpfecReceiverImpl(
31 uint32_t ssrc,
32 RecoveredPacketReceiver* callback,
33 rtc::ArrayView<const RtpExtension> extensions)
brandtrd726a3f2017-06-29 02:45:35 -070034 : ssrc_(ssrc),
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +020035 extensions_(extensions),
brandtrd726a3f2017-06-29 02:45:35 -070036 recovered_packet_callback_(callback),
37 fec_(ForwardErrorCorrection::CreateUlpfec(ssrc_)) {}
niklase@google.com470e71d2011-07-07 08:21:25 +000038
brandtrd55c3f62016-10-31 04:51:33 -070039UlpfecReceiverImpl::~UlpfecReceiverImpl() {
Tomas Gunnarsson1e75df22021-01-19 13:30:23 +010040 RTC_DCHECK_RUN_ON(&sequence_checker_);
brandtr74811e52016-08-10 00:51:50 -070041 received_packets_.clear();
Rasmus Brandt78db1582016-09-21 09:19:34 +020042 fec_->ResetState(&recovered_packets_);
niklase@google.com470e71d2011-07-07 08:21:25 +000043}
44
brandtrd55c3f62016-10-31 04:51:33 -070045FecPacketCounter UlpfecReceiverImpl::GetPacketCounter() const {
Tomas Gunnarsson1e75df22021-01-19 13:30:23 +010046 RTC_DCHECK_RUN_ON(&sequence_checker_);
asapersson@webrtc.org0800db72015-01-15 07:40:20 +000047 return packet_counter_;
48}
49
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +000050// 0 1 2 3
51// 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
52// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53// |F| block PT | timestamp offset | block length |
54// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55//
56//
57// RFC 2198 RTP Payload for Redundant Audio Data September 1997
58//
59// The bits in the header are specified as follows:
60//
61// F: 1 bit First bit in header indicates whether another header block
62// follows. If 1 further header blocks follow, if 0 this is the
63// last header block.
64// If 0 there is only 1 byte RED header
65//
66// block PT: 7 bits RTP payload type for this block.
67//
68// timestamp offset: 14 bits Unsigned offset of timestamp of this block
69// relative to timestamp given in RTP header. The use of an unsigned
70// offset implies that redundant data must be sent after the primary
71// data, and is hence a time to be subtracted from the current
72// timestamp to determine the timestamp of the data for which this
73// block is the redundancy.
74//
75// block length: 10 bits Length in bytes of the corresponding data
76// block excluding header.
niklase@google.com470e71d2011-07-07 08:21:25 +000077
Artem Titov68173942020-03-11 12:59:07 +010078bool UlpfecReceiverImpl::AddReceivedRedPacket(
79 const RtpPacketReceived& rtp_packet,
80 uint8_t ulpfec_payload_type) {
Tomas Gunnarsson1e75df22021-01-19 13:30:23 +010081 RTC_DCHECK_RUN_ON(&sequence_checker_);
82 // TODO(bugs.webrtc.org/11993): We get here via Call::DeliverRtp, so should be
83 // moved to the network thread.
84
Danil Chapovalov04fd2152019-09-20 11:40:12 +020085 if (rtp_packet.Ssrc() != ssrc_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010086 RTC_LOG(LS_WARNING)
brandtrd726a3f2017-06-29 02:45:35 -070087 << "Received RED packet with different SSRC than expected; dropping.";
Danil Chapovalov04fd2152019-09-20 11:40:12 +020088 return false;
brandtrd726a3f2017-06-29 02:45:35 -070089 }
Danil Chapovalov04fd2152019-09-20 11:40:12 +020090 if (rtp_packet.size() > IP_PACKET_SIZE) {
Ying Wang7a84fcf2018-05-18 13:48:58 +020091 RTC_LOG(LS_WARNING) << "Received RED packet with length exceeds maximum IP "
92 "packet size; dropping.";
Danil Chapovalov04fd2152019-09-20 11:40:12 +020093 return false;
Ying Wang7a84fcf2018-05-18 13:48:58 +020094 }
brandtr74811e52016-08-10 00:51:50 -070095
Danil Chapovalov04fd2152019-09-20 11:40:12 +020096 static constexpr uint8_t kRedHeaderLength = 1;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +000097
Danil Chapovalov04fd2152019-09-20 11:40:12 +020098 if (rtp_packet.payload_size() == 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010099 RTC_LOG(LS_WARNING) << "Corrupt/truncated FEC packet.";
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200100 return false;
pbos70d5c472015-06-29 07:22:04 -0700101 }
102
brandtr74811e52016-08-10 00:51:50 -0700103 // Remove RED header of incoming packet and store as a virtual RTP packet.
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200104 auto received_packet =
105 std::make_unique<ForwardErrorCorrection::ReceivedPacket>();
Rasmus Brandtae4f7672016-07-07 09:40:51 +0200106 received_packet->pkt = new ForwardErrorCorrection::Packet();
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000107
brandtr74811e52016-08-10 00:51:50 -0700108 // Get payload type from RED header and sequence number from RTP header.
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200109 uint8_t payload_type = rtp_packet.payload()[0] & 0x7f;
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000110 received_packet->is_fec = payload_type == ulpfec_payload_type;
Artem Titov68173942020-03-11 12:59:07 +0100111 received_packet->is_recovered = rtp_packet.recovered();
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200112 received_packet->ssrc = rtp_packet.Ssrc();
113 received_packet->seq_num = rtp_packet.SequenceNumber();
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000114
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200115 if (rtp_packet.payload()[0] & 0x80) {
brandtr74811e52016-08-10 00:51:50 -0700116 // f bit set in RED header, i.e. there are more than one RED header blocks.
Ilya Nikolaevskiy36c8ef62019-06-27 10:08:50 +0200117 // WebRTC never generates multiple blocks in a RED packet for FEC.
118 RTC_LOG(LS_WARNING) << "More than 1 block in RED packet is not supported.";
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200119 return false;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000120 }
Ilya Nikolaevskiy36c8ef62019-06-27 10:08:50 +0200121
asapersson@webrtc.org0800db72015-01-15 07:40:20 +0000122 ++packet_counter_.num_packets;
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200123 packet_counter_.num_bytes += rtp_packet.size();
asapersson0c43f772016-11-30 01:42:26 -0800124 if (packet_counter_.first_packet_time_ms == -1) {
Niels Möllerb5997872019-01-23 08:45:57 +0100125 packet_counter_.first_packet_time_ms = rtc::TimeMillis();
asapersson0c43f772016-11-30 01:42:26 -0800126 }
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000127
Ilya Nikolaevskiy36c8ef62019-06-27 10:08:50 +0200128 if (received_packet->is_fec) {
asapersson@webrtc.org0800db72015-01-15 07:40:20 +0000129 ++packet_counter_.num_fec_packets;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000130 // everything behind the RED header
Ilya Nikolaevskiy741bab02019-09-25 14:37:10 +0200131 received_packet->pkt->data =
132 rtp_packet.Buffer().Slice(rtp_packet.headers_size() + kRedHeaderLength,
133 rtp_packet.payload_size() - kRedHeaderLength);
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000134 } else {
Erik Språngf19aec82021-03-16 19:24:58 +0100135 received_packet->pkt->data.EnsureCapacity(rtp_packet.size() -
136 kRedHeaderLength);
brandtr74811e52016-08-10 00:51:50 -0700137 // Copy RTP header.
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200138 received_packet->pkt->data.SetData(rtp_packet.data(),
139 rtp_packet.headers_size());
brandtr74811e52016-08-10 00:51:50 -0700140 // Set payload type.
Danil Chapovalove15dc582021-01-07 15:24:05 +0100141 uint8_t& payload_type_byte = received_packet->pkt->data.MutableData()[1];
142 payload_type_byte &= 0x80; // Reset RED payload type.
143 payload_type_byte += payload_type; // Set media payload type.
Erik Språngf19aec82021-03-16 19:24:58 +0100144 // Copy payload and padding data, after the RED header.
145 received_packet->pkt->data.AppendData(
146 rtp_packet.data() + rtp_packet.headers_size() + kRedHeaderLength,
147 rtp_packet.size() - rtp_packet.headers_size() - kRedHeaderLength);
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000148 }
149
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200150 if (received_packet->pkt->data.size() > 0) {
151 received_packets_.push_back(std::move(received_packet));
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000152 }
Danil Chapovalov04fd2152019-09-20 11:40:12 +0200153 return true;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000154}
niklase@google.com470e71d2011-07-07 08:21:25 +0000155
nissea5f043f2017-09-18 07:58:59 -0700156// TODO(nisse): Drop always-zero return value.
brandtrd55c3f62016-10-31 04:51:33 -0700157int32_t UlpfecReceiverImpl::ProcessReceivedFec() {
Tomas Gunnarsson1e75df22021-01-19 13:30:23 +0100158 RTC_DCHECK_RUN_ON(&sequence_checker_);
philipeld8f6c162018-01-19 14:41:41 +0100159
160 // If we iterate over |received_packets_| and it contains a packet that cause
161 // us to recurse back to this function (for example a RED packet encapsulating
162 // a RED packet), then we will recurse forever. To avoid this we swap
163 // |received_packets_| with an empty vector so that the next recursive call
164 // wont iterate over the same packet again. This also solves the problem of
165 // not modifying the vector we are currently iterating over (packets are added
166 // in AddReceivedRedPacket).
167 std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
Yves Gerey665174f2018-06-19 15:03:05 +0200168 received_packets;
philipeld8f6c162018-01-19 14:41:41 +0100169 received_packets.swap(received_packets_);
170
171 for (const auto& received_packet : received_packets) {
marpan@webrtc.org3a6080d2012-03-30 16:16:21 +0000172 // Send received media packet to VCM.
nissea5f043f2017-09-18 07:58:59 -0700173 if (!received_packet->is_fec) {
174 ForwardErrorCorrection::Packet* packet = received_packet->pkt;
Ilya Nikolaevskiya5d952f2019-09-03 11:07:37 +0200175 recovered_packet_callback_->OnRecoveredPacket(packet->data.data(),
176 packet->data.size());
Ilya Nikolaevskiya5d952f2019-09-03 11:07:37 +0200177 // Create a packet with the buffer to modify it.
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +0200178 RtpPacketReceived rtp_packet;
Ilya Nikolaevskiy741bab02019-09-25 14:37:10 +0200179 const uint8_t* const original_data = packet->data.cdata();
Ilya Nikolaevskiye7314cd2019-09-30 11:36:42 +0200180 if (!rtp_packet.Parse(packet->data)) {
181 RTC_LOG(LS_WARNING) << "Corrupted media packet";
182 } else {
183 rtp_packet.IdentifyExtensions(extensions_);
184 // Reset buffer reference, so zeroing would work on a buffer with a
185 // single reference.
186 packet->data = rtc::CopyOnWriteBuffer(0);
187 rtp_packet.ZeroMutableExtensions();
188 packet->data = rtp_packet.Buffer();
189 // Ensure that zeroing of extensions was done in place.
190 RTC_DCHECK_EQ(packet->data.cdata(), original_data);
191 }
marpan@webrtc.org3a6080d2012-03-30 16:16:21 +0000192 }
Artem Titov68173942020-03-11 12:59:07 +0100193 if (!received_packet->is_recovered) {
194 // Do not pass recovered packets to FEC. Recovered packet might have
195 // different set of the RTP header extensions and thus different byte
196 // representation than the original packet, That will corrupt
197 // FEC calculation.
198 fec_->DecodeFec(*received_packet, &recovered_packets_);
199 }
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000200 }
nissea5f043f2017-09-18 07:58:59 -0700201
marpan@webrtc.org3a6080d2012-03-30 16:16:21 +0000202 // Send any recovered media packets to VCM.
brandtr74811e52016-08-10 00:51:50 -0700203 for (const auto& recovered_packet : recovered_packets_) {
Rasmus Brandtae4f7672016-07-07 09:40:51 +0200204 if (recovered_packet->returned) {
205 // Already sent to the VCM and the jitter buffer.
stefan@webrtc.org7adab092012-02-09 12:34:52 +0000206 continue;
Rasmus Brandtae4f7672016-07-07 09:40:51 +0200207 }
208 ForwardErrorCorrection::Packet* packet = recovered_packet->pkt;
asapersson@webrtc.org0800db72015-01-15 07:40:20 +0000209 ++packet_counter_.num_recovered_packets;
nisse41476e02017-08-25 09:08:44 -0700210 // Set this flag first; in case the recovered packet carries a RED
211 // header, OnRecoveredPacket will recurse back here.
212 recovered_packet->returned = true;
Ilya Nikolaevskiya5d952f2019-09-03 11:07:37 +0200213 recovered_packet_callback_->OnRecoveredPacket(packet->data.data(),
214 packet->data.size());
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000215 }
philipeld8f6c162018-01-19 14:41:41 +0100216
marpan@webrtc.org57353a32011-12-16 17:21:09 +0000217 return 0;
218}
stefan@webrtc.org7adab092012-02-09 12:34:52 +0000219
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000220} // namespace webrtc