niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 1 | /* |
pwestin@webrtc.org | 5dad00b | 2012-01-30 13:05:29 +0000 | [diff] [blame] | 2 | * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 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 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #include "modules/rtp_rtcp/source/ulpfec_receiver_impl.h" |
andrew@webrtc.org | 7fe219f | 2012-02-01 02:40:37 +0000 | [diff] [blame] | 12 | |
kwiberg | 84be511 | 2016-04-27 01:19:58 -0700 | [diff] [blame] | 13 | #include <memory> |
brandtr | 35c480c | 2016-08-09 01:23:23 -0700 | [diff] [blame] | 14 | #include <utility> |
kwiberg | 84be511 | 2016-04-27 01:19:58 -0700 | [diff] [blame] | 15 | |
Mirko Bonadei | d970807 | 2019-01-25 20:26:48 +0100 | [diff] [blame] | 16 | #include "api/scoped_refptr.h" |
Ilya Nikolaevskiy | 2d821c3 | 2019-06-26 14:39:36 +0200 | [diff] [blame] | 17 | #include "modules/rtp_rtcp/source/rtp_packet_received.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 18 | #include "rtc_base/logging.h" |
Niels Möller | b599787 | 2019-01-23 08:45:57 +0100 | [diff] [blame] | 19 | #include "rtc_base/time_utils.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 20 | |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 21 | namespace webrtc { |
stefan@webrtc.org | 7bb8f02 | 2013-09-06 13:40:11 +0000 | [diff] [blame] | 22 | |
Ilya Nikolaevskiy | 2d821c3 | 2019-06-26 14:39:36 +0200 | [diff] [blame] | 23 | std::unique_ptr<UlpfecReceiver> UlpfecReceiver::Create( |
| 24 | uint32_t ssrc, |
| 25 | RecoveredPacketReceiver* callback, |
| 26 | rtc::ArrayView<const RtpExtension> extensions) { |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 27 | return std::make_unique<UlpfecReceiverImpl>(ssrc, callback, extensions); |
stefan@webrtc.org | 7bb8f02 | 2013-09-06 13:40:11 +0000 | [diff] [blame] | 28 | } |
| 29 | |
Ilya Nikolaevskiy | 2d821c3 | 2019-06-26 14:39:36 +0200 | [diff] [blame] | 30 | UlpfecReceiverImpl::UlpfecReceiverImpl( |
| 31 | uint32_t ssrc, |
| 32 | RecoveredPacketReceiver* callback, |
| 33 | rtc::ArrayView<const RtpExtension> extensions) |
brandtr | d726a3f | 2017-06-29 02:45:35 -0700 | [diff] [blame] | 34 | : ssrc_(ssrc), |
Ilya Nikolaevskiy | 2d821c3 | 2019-06-26 14:39:36 +0200 | [diff] [blame] | 35 | extensions_(extensions), |
brandtr | d726a3f | 2017-06-29 02:45:35 -0700 | [diff] [blame] | 36 | recovered_packet_callback_(callback), |
| 37 | fec_(ForwardErrorCorrection::CreateUlpfec(ssrc_)) {} |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 38 | |
brandtr | d55c3f6 | 2016-10-31 04:51:33 -0700 | [diff] [blame] | 39 | UlpfecReceiverImpl::~UlpfecReceiverImpl() { |
Tomas Gunnarsson | 1e75df2 | 2021-01-19 13:30:23 +0100 | [diff] [blame] | 40 | RTC_DCHECK_RUN_ON(&sequence_checker_); |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 41 | received_packets_.clear(); |
Rasmus Brandt | 78db158 | 2016-09-21 09:19:34 +0200 | [diff] [blame] | 42 | fec_->ResetState(&recovered_packets_); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 43 | } |
| 44 | |
brandtr | d55c3f6 | 2016-10-31 04:51:33 -0700 | [diff] [blame] | 45 | FecPacketCounter UlpfecReceiverImpl::GetPacketCounter() const { |
Tomas Gunnarsson | 1e75df2 | 2021-01-19 13:30:23 +0100 | [diff] [blame] | 46 | RTC_DCHECK_RUN_ON(&sequence_checker_); |
asapersson@webrtc.org | 0800db7 | 2015-01-15 07:40:20 +0000 | [diff] [blame] | 47 | return packet_counter_; |
| 48 | } |
| 49 | |
phoglund@webrtc.org | 9919ad5 | 2013-05-16 15:06:28 +0000 | [diff] [blame] | 50 | // 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.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 77 | |
Artem Titov | 6817394 | 2020-03-11 12:59:07 +0100 | [diff] [blame] | 78 | bool UlpfecReceiverImpl::AddReceivedRedPacket( |
| 79 | const RtpPacketReceived& rtp_packet, |
| 80 | uint8_t ulpfec_payload_type) { |
Tomas Gunnarsson | 1e75df2 | 2021-01-19 13:30:23 +0100 | [diff] [blame] | 81 | 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 Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 85 | if (rtp_packet.Ssrc() != ssrc_) { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 86 | RTC_LOG(LS_WARNING) |
brandtr | d726a3f | 2017-06-29 02:45:35 -0700 | [diff] [blame] | 87 | << "Received RED packet with different SSRC than expected; dropping."; |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 88 | return false; |
brandtr | d726a3f | 2017-06-29 02:45:35 -0700 | [diff] [blame] | 89 | } |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 90 | if (rtp_packet.size() > IP_PACKET_SIZE) { |
Ying Wang | 7a84fcf | 2018-05-18 13:48:58 +0200 | [diff] [blame] | 91 | RTC_LOG(LS_WARNING) << "Received RED packet with length exceeds maximum IP " |
| 92 | "packet size; dropping."; |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 93 | return false; |
Ying Wang | 7a84fcf | 2018-05-18 13:48:58 +0200 | [diff] [blame] | 94 | } |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 95 | |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 96 | static constexpr uint8_t kRedHeaderLength = 1; |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 97 | |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 98 | if (rtp_packet.payload_size() == 0) { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 99 | RTC_LOG(LS_WARNING) << "Corrupt/truncated FEC packet."; |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 100 | return false; |
pbos | 70d5c47 | 2015-06-29 07:22:04 -0700 | [diff] [blame] | 101 | } |
| 102 | |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 103 | // Remove RED header of incoming packet and store as a virtual RTP packet. |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 104 | auto received_packet = |
| 105 | std::make_unique<ForwardErrorCorrection::ReceivedPacket>(); |
Rasmus Brandt | ae4f767 | 2016-07-07 09:40:51 +0200 | [diff] [blame] | 106 | received_packet->pkt = new ForwardErrorCorrection::Packet(); |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 107 | |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 108 | // Get payload type from RED header and sequence number from RTP header. |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 109 | uint8_t payload_type = rtp_packet.payload()[0] & 0x7f; |
stefan@webrtc.org | 7bb8f02 | 2013-09-06 13:40:11 +0000 | [diff] [blame] | 110 | received_packet->is_fec = payload_type == ulpfec_payload_type; |
Artem Titov | 6817394 | 2020-03-11 12:59:07 +0100 | [diff] [blame] | 111 | received_packet->is_recovered = rtp_packet.recovered(); |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 112 | received_packet->ssrc = rtp_packet.Ssrc(); |
| 113 | received_packet->seq_num = rtp_packet.SequenceNumber(); |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 114 | |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 115 | if (rtp_packet.payload()[0] & 0x80) { |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 116 | // f bit set in RED header, i.e. there are more than one RED header blocks. |
Ilya Nikolaevskiy | 36c8ef6 | 2019-06-27 10:08:50 +0200 | [diff] [blame] | 117 | // 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 Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 119 | return false; |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 120 | } |
Ilya Nikolaevskiy | 36c8ef6 | 2019-06-27 10:08:50 +0200 | [diff] [blame] | 121 | |
asapersson@webrtc.org | 0800db7 | 2015-01-15 07:40:20 +0000 | [diff] [blame] | 122 | ++packet_counter_.num_packets; |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 123 | packet_counter_.num_bytes += rtp_packet.size(); |
asapersson | 0c43f77 | 2016-11-30 01:42:26 -0800 | [diff] [blame] | 124 | if (packet_counter_.first_packet_time_ms == -1) { |
Niels Möller | b599787 | 2019-01-23 08:45:57 +0100 | [diff] [blame] | 125 | packet_counter_.first_packet_time_ms = rtc::TimeMillis(); |
asapersson | 0c43f77 | 2016-11-30 01:42:26 -0800 | [diff] [blame] | 126 | } |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 127 | |
Ilya Nikolaevskiy | 36c8ef6 | 2019-06-27 10:08:50 +0200 | [diff] [blame] | 128 | if (received_packet->is_fec) { |
asapersson@webrtc.org | 0800db7 | 2015-01-15 07:40:20 +0000 | [diff] [blame] | 129 | ++packet_counter_.num_fec_packets; |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 130 | // everything behind the RED header |
Ilya Nikolaevskiy | 741bab0 | 2019-09-25 14:37:10 +0200 | [diff] [blame] | 131 | received_packet->pkt->data = |
| 132 | rtp_packet.Buffer().Slice(rtp_packet.headers_size() + kRedHeaderLength, |
| 133 | rtp_packet.payload_size() - kRedHeaderLength); |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 134 | } else { |
Erik Språng | f19aec8 | 2021-03-16 19:24:58 +0100 | [diff] [blame] | 135 | received_packet->pkt->data.EnsureCapacity(rtp_packet.size() - |
| 136 | kRedHeaderLength); |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 137 | // Copy RTP header. |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 138 | received_packet->pkt->data.SetData(rtp_packet.data(), |
| 139 | rtp_packet.headers_size()); |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 140 | // Set payload type. |
Danil Chapovalov | e15dc58 | 2021-01-07 15:24:05 +0100 | [diff] [blame] | 141 | 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ång | f19aec8 | 2021-03-16 19:24:58 +0100 | [diff] [blame] | 144 | // 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.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 148 | } |
| 149 | |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 150 | if (received_packet->pkt->data.size() > 0) { |
| 151 | received_packets_.push_back(std::move(received_packet)); |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 152 | } |
Danil Chapovalov | 04fd215 | 2019-09-20 11:40:12 +0200 | [diff] [blame] | 153 | return true; |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 154 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 155 | |
nisse | a5f043f | 2017-09-18 07:58:59 -0700 | [diff] [blame] | 156 | // TODO(nisse): Drop always-zero return value. |
brandtr | d55c3f6 | 2016-10-31 04:51:33 -0700 | [diff] [blame] | 157 | int32_t UlpfecReceiverImpl::ProcessReceivedFec() { |
Tomas Gunnarsson | 1e75df2 | 2021-01-19 13:30:23 +0100 | [diff] [blame] | 158 | RTC_DCHECK_RUN_ON(&sequence_checker_); |
philipel | d8f6c16 | 2018-01-19 14:41:41 +0100 | [diff] [blame] | 159 | |
| 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 Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 168 | received_packets; |
philipel | d8f6c16 | 2018-01-19 14:41:41 +0100 | [diff] [blame] | 169 | received_packets.swap(received_packets_); |
| 170 | |
| 171 | for (const auto& received_packet : received_packets) { |
marpan@webrtc.org | 3a6080d | 2012-03-30 16:16:21 +0000 | [diff] [blame] | 172 | // Send received media packet to VCM. |
nisse | a5f043f | 2017-09-18 07:58:59 -0700 | [diff] [blame] | 173 | if (!received_packet->is_fec) { |
| 174 | ForwardErrorCorrection::Packet* packet = received_packet->pkt; |
Ilya Nikolaevskiy | a5d952f | 2019-09-03 11:07:37 +0200 | [diff] [blame] | 175 | recovered_packet_callback_->OnRecoveredPacket(packet->data.data(), |
| 176 | packet->data.size()); |
Ilya Nikolaevskiy | a5d952f | 2019-09-03 11:07:37 +0200 | [diff] [blame] | 177 | // Create a packet with the buffer to modify it. |
Ilya Nikolaevskiy | 2d821c3 | 2019-06-26 14:39:36 +0200 | [diff] [blame] | 178 | RtpPacketReceived rtp_packet; |
Ilya Nikolaevskiy | 741bab0 | 2019-09-25 14:37:10 +0200 | [diff] [blame] | 179 | const uint8_t* const original_data = packet->data.cdata(); |
Ilya Nikolaevskiy | e7314cd | 2019-09-30 11:36:42 +0200 | [diff] [blame] | 180 | 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.org | 3a6080d | 2012-03-30 16:16:21 +0000 | [diff] [blame] | 192 | } |
Artem Titov | 6817394 | 2020-03-11 12:59:07 +0100 | [diff] [blame] | 193 | 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.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 200 | } |
nisse | a5f043f | 2017-09-18 07:58:59 -0700 | [diff] [blame] | 201 | |
marpan@webrtc.org | 3a6080d | 2012-03-30 16:16:21 +0000 | [diff] [blame] | 202 | // Send any recovered media packets to VCM. |
brandtr | 74811e5 | 2016-08-10 00:51:50 -0700 | [diff] [blame] | 203 | for (const auto& recovered_packet : recovered_packets_) { |
Rasmus Brandt | ae4f767 | 2016-07-07 09:40:51 +0200 | [diff] [blame] | 204 | if (recovered_packet->returned) { |
| 205 | // Already sent to the VCM and the jitter buffer. |
stefan@webrtc.org | 7adab09 | 2012-02-09 12:34:52 +0000 | [diff] [blame] | 206 | continue; |
Rasmus Brandt | ae4f767 | 2016-07-07 09:40:51 +0200 | [diff] [blame] | 207 | } |
| 208 | ForwardErrorCorrection::Packet* packet = recovered_packet->pkt; |
asapersson@webrtc.org | 0800db7 | 2015-01-15 07:40:20 +0000 | [diff] [blame] | 209 | ++packet_counter_.num_recovered_packets; |
nisse | 41476e0 | 2017-08-25 09:08:44 -0700 | [diff] [blame] | 210 | // 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 Nikolaevskiy | a5d952f | 2019-09-03 11:07:37 +0200 | [diff] [blame] | 213 | recovered_packet_callback_->OnRecoveredPacket(packet->data.data(), |
| 214 | packet->data.size()); |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 +0000 | [diff] [blame] | 215 | } |
philipel | d8f6c16 | 2018-01-19 14:41:41 +0100 | [diff] [blame] | 216 | |
marpan@webrtc.org | 57353a3 | 2011-12-16 17:21:09 +0000 | [diff] [blame] | 217 | return 0; |
| 218 | } |
stefan@webrtc.org | 7adab09 | 2012-02-09 12:34:52 +0000 | [diff] [blame] | 219 | |
phoglund@webrtc.org | 9919ad5 | 2013-05-16 15:06:28 +0000 | [diff] [blame] | 220 | } // namespace webrtc |