blob: 480e7642068516cbd70f27b8b4cce5cf97d79f77 [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 Bonadei92ea95e2017-09-15 06:47:31 +020016#include "modules/rtp_rtcp/source/byte_io.h"
17#include "modules/rtp_rtcp/source/rtp_receiver_video.h"
18#include "rtc_base/checks.h"
19#include "rtc_base/logging.h"
20#include "system_wrappers/include/clock.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000021
niklase@google.com470e71d2011-07-07 08:21:25 +000022namespace webrtc {
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +000023
brandtrd726a3f2017-06-29 02:45:35 -070024UlpfecReceiver* UlpfecReceiver::Create(uint32_t ssrc,
25 RecoveredPacketReceiver* callback) {
26 return new UlpfecReceiverImpl(ssrc, callback);
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +000027}
28
brandtrd726a3f2017-06-29 02:45:35 -070029UlpfecReceiverImpl::UlpfecReceiverImpl(uint32_t ssrc,
30 RecoveredPacketReceiver* callback)
31 : ssrc_(ssrc),
32 recovered_packet_callback_(callback),
33 fec_(ForwardErrorCorrection::CreateUlpfec(ssrc_)) {}
niklase@google.com470e71d2011-07-07 08:21:25 +000034
brandtrd55c3f62016-10-31 04:51:33 -070035UlpfecReceiverImpl::~UlpfecReceiverImpl() {
brandtr74811e52016-08-10 00:51:50 -070036 received_packets_.clear();
Rasmus Brandt78db1582016-09-21 09:19:34 +020037 fec_->ResetState(&recovered_packets_);
niklase@google.com470e71d2011-07-07 08:21:25 +000038}
39
brandtrd55c3f62016-10-31 04:51:33 -070040FecPacketCounter UlpfecReceiverImpl::GetPacketCounter() const {
danilchap7c9426c2016-04-14 03:05:31 -070041 rtc::CritScope cs(&crit_sect_);
asapersson@webrtc.org0800db72015-01-15 07:40:20 +000042 return packet_counter_;
43}
44
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +000045// 0 1 2 3
46// 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
47// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48// |F| block PT | timestamp offset | block length |
49// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50//
51//
52// RFC 2198 RTP Payload for Redundant Audio Data September 1997
53//
54// The bits in the header are specified as follows:
55//
56// F: 1 bit First bit in header indicates whether another header block
57// follows. If 1 further header blocks follow, if 0 this is the
58// last header block.
59// If 0 there is only 1 byte RED header
60//
61// block PT: 7 bits RTP payload type for this block.
62//
63// timestamp offset: 14 bits Unsigned offset of timestamp of this block
64// relative to timestamp given in RTP header. The use of an unsigned
65// offset implies that redundant data must be sent after the primary
66// data, and is hence a time to be subtracted from the current
67// timestamp to determine the timestamp of the data for which this
68// block is the redundancy.
69//
70// block length: 10 bits Length in bytes of the corresponding data
71// block excluding header.
niklase@google.com470e71d2011-07-07 08:21:25 +000072
brandtrd55c3f62016-10-31 04:51:33 -070073int32_t UlpfecReceiverImpl::AddReceivedRedPacket(
74 const RTPHeader& header,
75 const uint8_t* incoming_rtp_packet,
76 size_t packet_length,
77 uint8_t ulpfec_payload_type) {
brandtrd726a3f2017-06-29 02:45:35 -070078 if (header.ssrc != ssrc_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010079 RTC_LOG(LS_WARNING)
brandtrd726a3f2017-06-29 02:45:35 -070080 << "Received RED packet with different SSRC than expected; dropping.";
81 return -1;
82 }
83
danilchap7c9426c2016-04-14 03:05:31 -070084 rtc::CritScope cs(&crit_sect_);
brandtr74811e52016-08-10 00:51:50 -070085
86 uint8_t red_header_length = 1;
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000087 size_t payload_data_length = packet_length - header.headerLength;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +000088
pbos70d5c472015-06-29 07:22:04 -070089 if (payload_data_length == 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010090 RTC_LOG(LS_WARNING) << "Corrupt/truncated FEC packet.";
pbos70d5c472015-06-29 07:22:04 -070091 return -1;
92 }
93
brandtr74811e52016-08-10 00:51:50 -070094 // Remove RED header of incoming packet and store as a virtual RTP packet.
kwiberg84be5112016-04-27 01:19:58 -070095 std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet(
Rasmus Brandtae4f7672016-07-07 09:40:51 +020096 new ForwardErrorCorrection::ReceivedPacket());
97 received_packet->pkt = new ForwardErrorCorrection::Packet();
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +000098
brandtr74811e52016-08-10 00:51:50 -070099 // Get payload type from RED header and sequence number from RTP header.
100 uint8_t payload_type = incoming_rtp_packet[header.headerLength] & 0x7f;
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000101 received_packet->is_fec = payload_type == ulpfec_payload_type;
brandtrd726a3f2017-06-29 02:45:35 -0700102 received_packet->ssrc = header.ssrc;
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000103 received_packet->seq_num = header.sequenceNumber;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000104
brandtr74811e52016-08-10 00:51:50 -0700105 uint16_t block_length = 0;
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000106 if (incoming_rtp_packet[header.headerLength] & 0x80) {
brandtr74811e52016-08-10 00:51:50 -0700107 // f bit set in RED header, i.e. there are more than one RED header blocks.
108 red_header_length = 4;
109 if (payload_data_length < red_header_length + 1u) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100110 RTC_LOG(LS_WARNING) << "Corrupt/truncated FEC packet.";
pbos70d5c472015-06-29 07:22:04 -0700111 return -1;
112 }
113
brandtr74811e52016-08-10 00:51:50 -0700114 uint16_t timestamp_offset = incoming_rtp_packet[header.headerLength + 1]
115 << 8;
brandtrd55c3f62016-10-31 04:51:33 -0700116 timestamp_offset += incoming_rtp_packet[header.headerLength + 2];
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000117 timestamp_offset = timestamp_offset >> 2;
118 if (timestamp_offset != 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100119 RTC_LOG(LS_WARNING) << "Corrupt payload found.";
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000120 return -1;
121 }
punyabrata@webrtc.org6da8eeb2012-01-24 00:48:36 +0000122
brandtr74811e52016-08-10 00:51:50 -0700123 block_length = (0x3 & incoming_rtp_packet[header.headerLength + 2]) << 8;
124 block_length += incoming_rtp_packet[header.headerLength + 3];
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000125
brandtr74811e52016-08-10 00:51:50 -0700126 // Check next RED header block.
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000127 if (incoming_rtp_packet[header.headerLength + 4] & 0x80) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100128 RTC_LOG(LS_WARNING) << "More than 2 blocks in packet not supported.";
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000129 return -1;
130 }
pbos2e43b262015-06-30 01:32:40 -0700131 // Check that the packet is long enough to contain data in the following
132 // block.
brandtr74811e52016-08-10 00:51:50 -0700133 if (block_length > payload_data_length - (red_header_length + 1)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100134 RTC_LOG(LS_WARNING) << "Block length longer than packet.";
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000135 return -1;
136 }
137 }
asapersson@webrtc.org0800db72015-01-15 07:40:20 +0000138 ++packet_counter_.num_packets;
asapersson0c43f772016-11-30 01:42:26 -0800139 if (packet_counter_.first_packet_time_ms == -1) {
140 packet_counter_.first_packet_time_ms =
141 Clock::GetRealTimeClock()->TimeInMilliseconds();
142 }
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000143
kwiberg84be5112016-04-27 01:19:58 -0700144 std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>
pbos70d5c472015-06-29 07:22:04 -0700145 second_received_packet;
brandtr74811e52016-08-10 00:51:50 -0700146 if (block_length > 0) {
147 // Handle block length, split into two packets.
148 red_header_length = 5;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000149
brandtr74811e52016-08-10 00:51:50 -0700150 // Copy RTP header.
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000151 memcpy(received_packet->pkt->data, incoming_rtp_packet,
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000152 header.headerLength);
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000153
brandtr74811e52016-08-10 00:51:50 -0700154 // Set payload type.
155 received_packet->pkt->data[1] &= 0x80; // Reset RED payload type.
156 received_packet->pkt->data[1] += payload_type; // Set media payload type.
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000157
brandtr74811e52016-08-10 00:51:50 -0700158 // Copy payload data.
159 memcpy(received_packet->pkt->data + header.headerLength,
160 incoming_rtp_packet + header.headerLength + red_header_length,
161 block_length);
162 received_packet->pkt->length = block_length;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000163
brandtr74811e52016-08-10 00:51:50 -0700164 second_received_packet.reset(new ForwardErrorCorrection::ReceivedPacket);
165 second_received_packet->pkt = new ForwardErrorCorrection::Packet;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000166
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000167 second_received_packet->is_fec = true;
brandtrd726a3f2017-06-29 02:45:35 -0700168 second_received_packet->ssrc = header.ssrc;
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000169 second_received_packet->seq_num = header.sequenceNumber;
asapersson@webrtc.org0800db72015-01-15 07:40:20 +0000170 ++packet_counter_.num_fec_packets;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000171
brandtr74811e52016-08-10 00:51:50 -0700172 // Copy FEC payload data.
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000173 memcpy(second_received_packet->pkt->data,
brandtr74811e52016-08-10 00:51:50 -0700174 incoming_rtp_packet + header.headerLength + red_header_length +
175 block_length,
176 payload_data_length - red_header_length - block_length);
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000177
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000178 second_received_packet->pkt->length =
brandtr74811e52016-08-10 00:51:50 -0700179 payload_data_length - red_header_length - block_length;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000180
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000181 } else if (received_packet->is_fec) {
asapersson@webrtc.org0800db72015-01-15 07:40:20 +0000182 ++packet_counter_.num_fec_packets;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000183 // everything behind the RED header
brandtr74811e52016-08-10 00:51:50 -0700184 memcpy(received_packet->pkt->data,
185 incoming_rtp_packet + header.headerLength + red_header_length,
186 payload_data_length - red_header_length);
187 received_packet->pkt->length = payload_data_length - red_header_length;
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000188 received_packet->ssrc =
sprang@webrtc.org779c3d12015-03-17 16:42:49 +0000189 ByteReader<uint32_t>::ReadBigEndian(&incoming_rtp_packet[8]);
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000190
191 } else {
brandtr74811e52016-08-10 00:51:50 -0700192 // Copy RTP header.
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000193 memcpy(received_packet->pkt->data, incoming_rtp_packet,
stefan@webrtc.org7bb8f022013-09-06 13:40:11 +0000194 header.headerLength);
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000195
brandtr74811e52016-08-10 00:51:50 -0700196 // Set payload type.
197 received_packet->pkt->data[1] &= 0x80; // Reset RED payload type.
198 received_packet->pkt->data[1] += payload_type; // Set media payload type.
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000199
brandtr74811e52016-08-10 00:51:50 -0700200 // Copy payload data.
201 memcpy(received_packet->pkt->data + header.headerLength,
202 incoming_rtp_packet + header.headerLength + red_header_length,
203 payload_data_length - red_header_length);
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000204 received_packet->pkt->length =
brandtr74811e52016-08-10 00:51:50 -0700205 header.headerLength + payload_data_length - red_header_length;
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000206 }
207
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000208 if (received_packet->pkt->length == 0) {
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000209 return 0;
210 }
211
brandtr74811e52016-08-10 00:51:50 -0700212 received_packets_.push_back(std::move(received_packet));
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000213 if (second_received_packet) {
brandtr74811e52016-08-10 00:51:50 -0700214 received_packets_.push_back(std::move(second_received_packet));
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000215 }
216 return 0;
217}
niklase@google.com470e71d2011-07-07 08:21:25 +0000218
nissea5f043f2017-09-18 07:58:59 -0700219// TODO(nisse): Drop always-zero return value.
brandtrd55c3f62016-10-31 04:51:33 -0700220int32_t UlpfecReceiverImpl::ProcessReceivedFec() {
danilchap7c9426c2016-04-14 03:05:31 -0700221 crit_sect_.Enter();
philipeld8f6c162018-01-19 14:41:41 +0100222
223 // If we iterate over |received_packets_| and it contains a packet that cause
224 // us to recurse back to this function (for example a RED packet encapsulating
225 // a RED packet), then we will recurse forever. To avoid this we swap
226 // |received_packets_| with an empty vector so that the next recursive call
227 // wont iterate over the same packet again. This also solves the problem of
228 // not modifying the vector we are currently iterating over (packets are added
229 // in AddReceivedRedPacket).
230 std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
231 received_packets;
232 received_packets.swap(received_packets_);
233
234 for (const auto& received_packet : received_packets) {
marpan@webrtc.org3a6080d2012-03-30 16:16:21 +0000235 // Send received media packet to VCM.
nissea5f043f2017-09-18 07:58:59 -0700236 if (!received_packet->is_fec) {
237 ForwardErrorCorrection::Packet* packet = received_packet->pkt;
danilchap7c9426c2016-04-14 03:05:31 -0700238 crit_sect_.Leave();
nisse30e89312017-05-29 08:16:37 -0700239 recovered_packet_callback_->OnRecoveredPacket(packet->data,
240 packet->length);
danilchap7c9426c2016-04-14 03:05:31 -0700241 crit_sect_.Enter();
marpan@webrtc.org3a6080d2012-03-30 16:16:21 +0000242 }
nissea5f043f2017-09-18 07:58:59 -0700243 fec_->DecodeFec(*received_packet, &recovered_packets_);
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000244 }
nissea5f043f2017-09-18 07:58:59 -0700245
marpan@webrtc.org3a6080d2012-03-30 16:16:21 +0000246 // Send any recovered media packets to VCM.
brandtr74811e52016-08-10 00:51:50 -0700247 for (const auto& recovered_packet : recovered_packets_) {
Rasmus Brandtae4f7672016-07-07 09:40:51 +0200248 if (recovered_packet->returned) {
249 // Already sent to the VCM and the jitter buffer.
stefan@webrtc.org7adab092012-02-09 12:34:52 +0000250 continue;
Rasmus Brandtae4f7672016-07-07 09:40:51 +0200251 }
252 ForwardErrorCorrection::Packet* packet = recovered_packet->pkt;
asapersson@webrtc.org0800db72015-01-15 07:40:20 +0000253 ++packet_counter_.num_recovered_packets;
nisse41476e02017-08-25 09:08:44 -0700254 // Set this flag first; in case the recovered packet carries a RED
255 // header, OnRecoveredPacket will recurse back here.
256 recovered_packet->returned = true;
danilchap7c9426c2016-04-14 03:05:31 -0700257 crit_sect_.Leave();
nisse30e89312017-05-29 08:16:37 -0700258 recovered_packet_callback_->OnRecoveredPacket(packet->data,
259 packet->length);
danilchap7c9426c2016-04-14 03:05:31 -0700260 crit_sect_.Enter();
pwestin@webrtc.org95cf4792012-01-20 06:59:06 +0000261 }
philipeld8f6c162018-01-19 14:41:41 +0100262
danilchap7c9426c2016-04-14 03:05:31 -0700263 crit_sect_.Leave();
marpan@webrtc.org57353a32011-12-16 17:21:09 +0000264 return 0;
265}
stefan@webrtc.org7adab092012-02-09 12:34:52 +0000266
phoglund@webrtc.org9919ad52013-05-16 15:06:28 +0000267} // namespace webrtc