flexfec_receiver.cc (7998B)
1 /* 2 * Copyright (c) 2016 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/include/flexfec_receiver.h" 12 13 #include <cstdint> 14 #include <cstring> 15 #include <memory> 16 17 #include "api/scoped_refptr.h" 18 #include "api/sequence_checker.h" 19 #include "api/units/time_delta.h" 20 #include "api/units/timestamp.h" 21 #include "modules/rtp_rtcp/include/recovered_packet_receiver.h" 22 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" 23 #include "modules/rtp_rtcp/source/forward_error_correction.h" 24 #include "modules/rtp_rtcp/source/rtp_packet_received.h" 25 #include "modules/rtp_rtcp/source/ulpfec_receiver.h" 26 #include "rtc_base/checks.h" 27 #include "rtc_base/logging.h" 28 29 namespace webrtc { 30 31 namespace { 32 33 // Minimum header size (in bytes) of a well-formed non-singular FlexFEC packet. 34 constexpr size_t kMinFlexfecHeaderSize = 20; 35 36 // How often to log the recovered packets to the text log. 37 constexpr TimeDelta kPacketLogInterval = TimeDelta::Seconds(10); 38 39 } // namespace 40 41 /* Mozilla: Avoid this since it could use GetRealTimeClock(). 42 FlexfecReceiver::FlexfecReceiver( 43 uint32_t ssrc, 44 uint32_t protected_media_ssrc, 45 RecoveredPacketReceiver* recovered_packet_receiver) 46 : FlexfecReceiver(Clock::GetRealTimeClock(), 47 ssrc, 48 protected_media_ssrc, 49 recovered_packet_receiver) {} 50 */ 51 52 FlexfecReceiver::FlexfecReceiver( 53 Clock* clock, 54 uint32_t ssrc, 55 uint32_t protected_media_ssrc, 56 RecoveredPacketReceiver* recovered_packet_receiver) 57 : ssrc_(ssrc), 58 protected_media_ssrc_(protected_media_ssrc), 59 erasure_code_( 60 ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc)), 61 recovered_packet_receiver_(recovered_packet_receiver), 62 clock_(clock) { 63 // It's OK to create this object on a different thread/task queue than 64 // the one used during main operation. 65 sequence_checker_.Detach(); 66 } 67 68 FlexfecReceiver::~FlexfecReceiver() = default; 69 70 void FlexfecReceiver::OnRtpPacket(const RtpPacketReceived& packet) { 71 RTC_DCHECK_RUN_ON(&sequence_checker_); 72 73 // If this packet was recovered, it might be originating from 74 // ProcessReceivedPacket in this object. To avoid lifetime issues with 75 // `recovered_packets_`, we therefore break the cycle here. 76 // This might reduce decoding efficiency a bit, since we can't disambiguate 77 // recovered packets by RTX from recovered packets by FlexFEC. 78 if (packet.recovered()) 79 return; 80 81 std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet = 82 AddReceivedPacket(packet); 83 if (!received_packet) 84 return; 85 86 ProcessReceivedPacket(*received_packet); 87 } 88 89 FecPacketCounter FlexfecReceiver::GetPacketCounter() const { 90 RTC_DCHECK_RUN_ON(&sequence_checker_); 91 return packet_counter_; 92 } 93 94 // TODO(eladalon): Consider using packet.recovered() to avoid processing 95 // recovered packets here. 96 std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> 97 FlexfecReceiver::AddReceivedPacket(const RtpPacketReceived& packet) { 98 RTC_DCHECK_RUN_ON(&sequence_checker_); 99 100 // RTP packets with a full base header (12 bytes), but without payload, 101 // could conceivably be useful in the decoding. Therefore we check 102 // with a non-strict inequality here. 103 RTC_DCHECK_GE(packet.size(), kRtpHeaderSize); 104 105 // Demultiplex based on SSRC, and insert into erasure code decoder. 106 std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet( 107 new ForwardErrorCorrection::ReceivedPacket()); 108 received_packet->seq_num = packet.SequenceNumber(); 109 received_packet->ssrc = packet.Ssrc(); 110 received_packet->extensions = packet.extension_manager(); 111 if (received_packet->ssrc == ssrc_) { 112 // This is a FlexFEC packet. 113 if (packet.payload_size() < kMinFlexfecHeaderSize) { 114 RTC_LOG(LS_WARNING) << "Truncated FlexFEC packet, discarding."; 115 return nullptr; 116 } 117 received_packet->is_fec = true; 118 ++packet_counter_.num_fec_packets; 119 120 // Insert packet payload into erasure code. 121 received_packet->pkt = scoped_refptr<ForwardErrorCorrection::Packet>( 122 new ForwardErrorCorrection::Packet()); 123 received_packet->pkt->data = 124 packet.Buffer().Slice(packet.headers_size(), packet.payload_size()); 125 } else { 126 // This is a media packet, or a FlexFEC packet belonging to some 127 // other FlexFEC stream. 128 if (received_packet->ssrc != protected_media_ssrc_) { 129 return nullptr; 130 } 131 received_packet->is_fec = false; 132 133 // Insert entire packet into erasure code. 134 // Create a copy and fill with zeros all mutable extensions. 135 received_packet->pkt = scoped_refptr<ForwardErrorCorrection::Packet>( 136 new ForwardErrorCorrection::Packet()); 137 RtpPacketReceived packet_copy(packet); 138 packet_copy.ZeroMutableExtensions(); 139 received_packet->pkt->data = packet_copy.Buffer(); 140 } 141 142 ++packet_counter_.num_packets; 143 144 return received_packet; 145 } 146 147 // Note that the implementation of this member function and the implementation 148 // in UlpfecReceiver::ProcessReceivedFec() are slightly different. 149 // This implementation only returns _recovered_ media packets through the 150 // callback, whereas the implementation in UlpfecReceiver returns _all inserted_ 151 // media packets through the callback. The latter behaviour makes sense 152 // for ULPFEC, since the ULPFEC receiver is owned by the RtpVideoStreamReceiver. 153 // Here, however, the received media pipeline is more decoupled from the 154 // FlexFEC decoder, and we therefore do not interfere with the reception 155 // of non-recovered media packets. 156 void FlexfecReceiver::ProcessReceivedPacket( 157 const ForwardErrorCorrection::ReceivedPacket& received_packet) { 158 RTC_DCHECK_RUN_ON(&sequence_checker_); 159 160 // Decode. 161 ForwardErrorCorrection::DecodeFecResult decode_result = 162 erasure_code_->DecodeFec(received_packet, &recovered_packets_); 163 164 if (decode_result.num_recovered_packets == 0) { 165 return; 166 } 167 168 // Return recovered packets through callback. 169 for (const auto& recovered_packet : recovered_packets_) { 170 RTC_CHECK(recovered_packet); 171 if (recovered_packet->returned) { 172 continue; 173 } 174 ++packet_counter_.num_recovered_packets; 175 // Set this flag first, since OnRecoveredPacket may end up here 176 // again, with the same packet. 177 recovered_packet->returned = true; 178 RTC_CHECK_GE(recovered_packet->pkt->data.size(), kRtpHeaderSize); 179 180 RtpPacketReceived parsed_packet(&received_packet.extensions); 181 if (!parsed_packet.Parse(recovered_packet->pkt->data)) { 182 continue; 183 } 184 parsed_packet.set_recovered(true); 185 186 // TODO(brandtr): Update here when we support protecting audio packets too. 187 parsed_packet.set_payload_type_frequency(kVideoPayloadTypeFrequency); 188 recovered_packet_receiver_->OnRecoveredPacket(parsed_packet); 189 190 // Periodically log the incoming packets at LS_INFO. 191 Timestamp now = clock_->CurrentTime(); 192 bool should_log_periodically = 193 now - last_recovered_packet_ > kPacketLogInterval; 194 if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE) || should_log_periodically) { 195 LoggingSeverity level = should_log_periodically ? LS_INFO : LS_VERBOSE; 196 RTC_LOG_V(level) << "Recovered media packet with SSRC: " 197 << parsed_packet.Ssrc() << " seq " 198 << parsed_packet.SequenceNumber() << " recovered length " 199 << recovered_packet->pkt->data.size() 200 << " received length " 201 << received_packet.pkt->data.size() 202 << " from FlexFEC stream with SSRC: " << ssrc_; 203 if (should_log_periodically) { 204 last_recovered_packet_ = now; 205 } 206 } 207 } 208 } 209 210 } // namespace webrtc