void UlpfecReceiver::ProcessReceivedFec() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
// If we iterate over `received_packets_` and it contains a packet that cause
// us to recurse back to this function (for example a RED packet encapsulating
// a RED packet), then we will recurse forever. To avoid this we swap
// `received_packets_` with an empty vector so that the next recursive call
// wont iterate over the same packet again. This also solves the problem of
// not modifying the vector we are currently iterating over (packets are added
// in AddReceivedRedPacket).
std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>> received_packets;
received_packets.swap(received_packets_);
RtpHeaderExtensionMap* last_recovered_extension_map =nullptr;
size_t num_recovered_packets =0;
for (constauto&received_packet : received_packets) {
// Send received media packet to VCM.
if (!received_packet->is_fec) {
ForwardErrorCorrection::Packet* packet = received_packet->pkt.get();
RtpPacketReceived rtp_packet(&received_packet->extensions);
if (!rtp_packet.Parse(std::move(packet->data))) {
RTC_LOG(LS_WARNING) <<"Corrupted media packet";
continue;
}
recovered_packet_callback_->OnRecoveredPacket(rtp_packet);
// Some header extensions need to be zeroed in `received_packet` since
// they are written to the packet after FEC encoding. We try to do it
// without a copy of the underlying Copy-On-Write buffer, but if a
// reference is held by `recovered_packet_callback_->OnRecoveredPacket` a
// copy will still be made in 'rtp_packet.ZeroMutableExtensions()'.
rtp_packet.ZeroMutableExtensions();
packet->data = rtp_packet.Buffer();
}
if (!received_packet->is_recovered) {
// Do not pass recovered packets to FEC. Recovered packet might have
// different set of the RTP header extensions and thus different byte
// representation than the original packet, That will corrupt
// FEC calculation.
ForwardErrorCorrection::DecodeFecResult decode_result = fec_->DecodeFec(*received_packet, &recovered_packets_);
last_recovered_extension_map =&received_packet->extensions;
num_recovered_packets += decode_result.num_recovered_packets;
}
}
if (num_recovered_packets ==0) {
return;
}
// Send any recovered media packets to VCM.
for (constauto&recovered_packet : recovered_packets_) {
if (recovered_packet->returned) {
// Already sent to the VCM and the jitter buffer.
continue;
}
ForwardErrorCorrection::Packet* packet = recovered_packet->pkt.get();
++packet_counter_.num_recovered_packets;
// Set this flag first; in case the recovered packet carries a RED
// header, OnRecoveredPacket will recurse back here.
recovered_packet->returned =true;
RtpPacketReceived parsed_packet(last_recovered_extension_map);
if (!parsed_packet.Parse(packet->data)) {
continue;
}
parsed_packet.set_recovered(true);
recovered_packet_callback_->OnRecoveredPacket(parsed_packet);
}
}