| Index: webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| diff --git a/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc b/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| index 8c35f4000f97a848f3d9de5bc382b9b0d1b3ada0..a2d0e6dc9cb4067b3dcd6784df6e9ccdd33dff0e 100644
|
| --- a/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| +++ b/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| @@ -54,6 +54,7 @@ template <typename S, typename T>
|
| bool ForwardErrorCorrection::SortablePacket::LessThan::operator() (
|
| const S& first,
|
| const T& second) {
|
| + RTC_DCHECK_EQ(first->ssrc, second->ssrc);
|
| return IsNewerSequenceNumber(second->seq_num, first->seq_num);
|
| }
|
|
|
| @@ -71,27 +72,34 @@ ForwardErrorCorrection::ReceivedFecPacket::~ReceivedFecPacket() = default;
|
|
|
| ForwardErrorCorrection::ForwardErrorCorrection(
|
| std::unique_ptr<FecHeaderReader> fec_header_reader,
|
| - std::unique_ptr<FecHeaderWriter> fec_header_writer)
|
| - : fec_header_reader_(std::move(fec_header_reader)),
|
| + std::unique_ptr<FecHeaderWriter> fec_header_writer,
|
| + uint32_t ssrc,
|
| + uint32_t protected_media_ssrc)
|
| + : ssrc_(ssrc),
|
| + protected_media_ssrc_(protected_media_ssrc),
|
| + fec_header_reader_(std::move(fec_header_reader)),
|
| fec_header_writer_(std::move(fec_header_writer)),
|
| generated_fec_packets_(fec_header_writer_->MaxFecPackets()),
|
| packet_mask_size_(0) {}
|
|
|
| ForwardErrorCorrection::~ForwardErrorCorrection() = default;
|
|
|
| -std::unique_ptr<ForwardErrorCorrection> ForwardErrorCorrection::CreateUlpfec() {
|
| +std::unique_ptr<ForwardErrorCorrection> ForwardErrorCorrection::CreateUlpfec(
|
| + uint32_t ssrc) {
|
| std::unique_ptr<FecHeaderReader> fec_header_reader(new UlpfecHeaderReader());
|
| std::unique_ptr<FecHeaderWriter> fec_header_writer(new UlpfecHeaderWriter());
|
| return std::unique_ptr<ForwardErrorCorrection>(new ForwardErrorCorrection(
|
| - std::move(fec_header_reader), std::move(fec_header_writer)));
|
| + std::move(fec_header_reader), std::move(fec_header_writer), ssrc, ssrc));
|
| }
|
|
|
| -std::unique_ptr<ForwardErrorCorrection>
|
| -ForwardErrorCorrection::CreateFlexfec() {
|
| +std::unique_ptr<ForwardErrorCorrection> ForwardErrorCorrection::CreateFlexfec(
|
| + uint32_t ssrc,
|
| + uint32_t protected_media_ssrc) {
|
| std::unique_ptr<FecHeaderReader> fec_header_reader(new FlexfecHeaderReader());
|
| std::unique_ptr<FecHeaderWriter> fec_header_writer(new FlexfecHeaderWriter());
|
| return std::unique_ptr<ForwardErrorCorrection>(new ForwardErrorCorrection(
|
| - std::move(fec_header_reader), std::move(fec_header_writer)));
|
| + std::move(fec_header_reader), std::move(fec_header_writer), ssrc,
|
| + protected_media_ssrc));
|
| }
|
|
|
| int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets,
|
| @@ -335,20 +343,25 @@ void ForwardErrorCorrection::ResetState(
|
| void ForwardErrorCorrection::InsertMediaPacket(
|
| RecoveredPacketList* recovered_packets,
|
| ReceivedPacket* received_packet) {
|
| + RTC_DCHECK_EQ(received_packet->ssrc, protected_media_ssrc_);
|
| +
|
| // Search for duplicate packets.
|
| for (const auto& recovered_packet : *recovered_packets) {
|
| - if (received_packet->seq_num == recovered_packet->seq_num) {
|
| + RTC_DCHECK_EQ(recovered_packet->ssrc, received_packet->ssrc);
|
| + if (recovered_packet->seq_num == received_packet->seq_num) {
|
| // Duplicate packet, no need to add to list.
|
| // Delete duplicate media packet data.
|
| received_packet->pkt = nullptr;
|
| return;
|
| }
|
| }
|
| +
|
| std::unique_ptr<RecoveredPacket> recovered_packet(new RecoveredPacket());
|
| // This "recovered packet" was not recovered using parity packets.
|
| recovered_packet->was_recovered = false;
|
| // This media packet has already been passed on.
|
| recovered_packet->returned = true;
|
| + recovered_packet->ssrc = received_packet->ssrc;
|
| recovered_packet->seq_num = received_packet->seq_num;
|
| recovered_packet->pkt = received_packet->pkt;
|
| recovered_packet->pkt->length = received_packet->pkt->length;
|
| @@ -378,23 +391,35 @@ void ForwardErrorCorrection::UpdateCoveringFecPackets(
|
| void ForwardErrorCorrection::InsertFecPacket(
|
| const RecoveredPacketList& recovered_packets,
|
| ReceivedPacket* received_packet) {
|
| + RTC_DCHECK_EQ(received_packet->ssrc, ssrc_);
|
| +
|
| // Check for duplicate.
|
| for (const auto& existing_fec_packet : received_fec_packets_) {
|
| - if (received_packet->seq_num == existing_fec_packet->seq_num) {
|
| + RTC_DCHECK_EQ(existing_fec_packet->ssrc, received_packet->ssrc);
|
| + if (existing_fec_packet->seq_num == received_packet->seq_num) {
|
| // Delete duplicate FEC packet data.
|
| received_packet->pkt = nullptr;
|
| return;
|
| }
|
| }
|
| +
|
| std::unique_ptr<ReceivedFecPacket> fec_packet(new ReceivedFecPacket());
|
| fec_packet->pkt = received_packet->pkt;
|
| - fec_packet->seq_num = received_packet->seq_num;
|
| fec_packet->ssrc = received_packet->ssrc;
|
| + fec_packet->seq_num = received_packet->seq_num;
|
| // Parse ULPFEC/FlexFEC header specific info.
|
| bool ret = fec_header_reader_->ReadFecHeader(fec_packet.get());
|
| if (!ret) {
|
| return;
|
| }
|
| +
|
| + // TODO(brandtr): Update here when we support multistream protection.
|
| + if (fec_packet->protected_ssrc != protected_media_ssrc_) {
|
| + LOG(LS_INFO)
|
| + << "Received FEC packet is protecting an unknown media SSRC; dropping.";
|
| + return;
|
| + }
|
| +
|
| // Parse packet mask from header and represent as protected packets.
|
| for (uint16_t byte_idx = 0; byte_idx < fec_packet->packet_mask_size;
|
| ++byte_idx) {
|
| @@ -405,6 +430,7 @@ void ForwardErrorCorrection::InsertFecPacket(
|
| std::unique_ptr<ProtectedPacket> protected_packet(
|
| new ProtectedPacket());
|
| // This wraps naturally with the sequence number.
|
| + protected_packet->ssrc = protected_media_ssrc_;
|
| protected_packet->seq_num = static_cast<uint16_t>(
|
| fec_packet->seq_num_base + (byte_idx << 3) + bit_idx);
|
| protected_packet->pkt = nullptr;
|
| @@ -412,6 +438,7 @@ void ForwardErrorCorrection::InsertFecPacket(
|
| }
|
| }
|
| }
|
| +
|
| if (fec_packet->protected_packets.empty()) {
|
| // All-zero packet mask; we can discard this FEC packet.
|
| LOG(LS_WARNING) << "Received FEC packet has an all-zero packet mask.";
|
| @@ -419,11 +446,6 @@ void ForwardErrorCorrection::InsertFecPacket(
|
| AssignRecoveredPackets(recovered_packets, fec_packet.get());
|
| // TODO(holmer): Consider replacing this with a binary search for the right
|
| // position, and then just insert the new packet. Would get rid of the sort.
|
| - //
|
| - // For correct decoding, |received_fec_packets_| does not necessarily
|
| - // need to be sorted by sequence number (see decoding algorithm in
|
| - // AttemptRecover()). By keeping it sorted we try to recover the
|
| - // oldest lost packets first, however.
|
| received_fec_packets_.push_back(std::move(fec_packet));
|
| received_fec_packets_.sort(SortablePacket::LessThan());
|
| const size_t max_fec_packets = fec_header_reader_->MaxFecPackets();
|
| @@ -467,19 +489,31 @@ void ForwardErrorCorrection::InsertPackets(
|
| while (!received_packets->empty()) {
|
| ReceivedPacket* received_packet = received_packets->front().get();
|
|
|
| - // Check for discarding oldest FEC packet, to avoid wrong FEC decoding from
|
| - // sequence number wrap-around. Detection of old FEC packet is based on
|
| - // sequence number difference of received packet and oldest packet in FEC
|
| - // packet list.
|
| + // Discard old FEC packets such that the sequence numbers in
|
| + // |received_fec_packets_| span at most 1/2 of the sequence number space.
|
| + // This is important for keeping |received_fec_packets_| sorted, and may
|
| + // also reduce the possibility of incorrect decoding due to sequence number
|
| + // wrap-around.
|
| // TODO(marpan/holmer): We should be able to improve detection/discarding of
|
| // old FEC packets based on timestamp information or better sequence number
|
| // thresholding (e.g., to distinguish between wrap-around and reordering).
|
| - if (!received_fec_packets_.empty()) {
|
| - uint16_t seq_num_diff =
|
| - abs(static_cast<int>(received_packet->seq_num) -
|
| - static_cast<int>(received_fec_packets_.front()->seq_num));
|
| - if (seq_num_diff > 0x3fff) {
|
| - received_fec_packets_.pop_front();
|
| + if (!received_fec_packets_.empty() &&
|
| + received_packet->ssrc == received_fec_packets_.front()->ssrc) {
|
| + // It only makes sense to detect wrap-around when |received_packet|
|
| + // and |front_received_fec_packet| belong to the same sequence number
|
| + // space, i.e., the same SSRC. This happens when |received_packet|
|
| + // is a FEC packet, or if |received_packet| is a media packet and
|
| + // RED+ULPFEC is used.
|
| + auto it = received_fec_packets_.begin();
|
| + while (it != received_fec_packets_.end()) {
|
| + uint16_t seq_num_diff = abs(static_cast<int>(received_packet->seq_num) -
|
| + static_cast<int>((*it)->seq_num));
|
| + if (seq_num_diff > 0x3fff) {
|
| + it = received_fec_packets_.erase(it);
|
| + } else {
|
| + // No need to keep iterating, since |received_fec_packets_| is sorted.
|
| + break;
|
| + }
|
| }
|
| }
|
|
|
| @@ -549,6 +583,7 @@ bool ForwardErrorCorrection::FinishPacketRecovery(
|
| // Set the SSRC field.
|
| ByteWriter<uint32_t>::WriteBigEndian(&recovered_packet->pkt->data[8],
|
| fec_packet.protected_ssrc);
|
| + recovered_packet->ssrc = fec_packet.protected_ssrc;
|
| return true;
|
| }
|
|
|
| @@ -684,21 +719,34 @@ uint32_t ForwardErrorCorrection::ParseSsrc(uint8_t* packet) {
|
| int ForwardErrorCorrection::DecodeFec(
|
| ReceivedPacketList* received_packets,
|
| RecoveredPacketList* recovered_packets) {
|
| - // TODO(marpan/ajm): can we check for multiple ULP headers, and return an
|
| - // error?
|
| + RTC_DCHECK(received_packets);
|
| + RTC_DCHECK(recovered_packets);
|
| +
|
| const size_t max_media_packets = fec_header_reader_->MaxMediaPackets();
|
| if (recovered_packets->size() == max_media_packets) {
|
| - const unsigned int seq_num_diff =
|
| - abs(static_cast<int>(received_packets->front()->seq_num) -
|
| - static_cast<int>(recovered_packets->back()->seq_num));
|
| - if (seq_num_diff > max_media_packets) {
|
| - // A big gap in sequence numbers. The old recovered packets
|
| - // are now useless, so it's safe to do a reset.
|
| - ResetState(recovered_packets);
|
| + const RecoveredPacket* back_recovered_packet =
|
| + recovered_packets->back().get();
|
| + for (const auto& received_packet : *received_packets) {
|
| + if (received_packet->ssrc == back_recovered_packet->ssrc) {
|
| + const unsigned int seq_num_diff =
|
| + abs(static_cast<int>(received_packet->seq_num) -
|
| + static_cast<int>(back_recovered_packet->seq_num));
|
| + if (seq_num_diff > max_media_packets) {
|
| + // A big gap in sequence numbers. The old recovered packets
|
| + // are now useless, so it's safe to do a reset.
|
| + LOG(LS_INFO) << "Big gap in media/ULPFEC sequence numbers. No need "
|
| + "to keep the old packets in the FEC buffers, thus "
|
| + "resetting them.";
|
| + ResetState(recovered_packets);
|
| + break;
|
| + }
|
| + }
|
| }
|
| }
|
| +
|
| InsertPackets(received_packets, recovered_packets);
|
| AttemptRecovery(recovered_packets);
|
| +
|
| return 0;
|
| }
|
|
|
|
|