diff --git a/src/modules/rtp_rtcp/source/forward_error_correction.cc b/src/modules/rtp_rtcp/source/forward_error_correction.cc index 81db2a00fc..22e1a38081 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.cc +++ b/src/modules/rtp_rtcp/source/forward_error_correction.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source @@ -8,13 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include +#include -#include "forward_error_correction.h" -#include "forward_error_correction_internal.h" -#include "rtp_utility.h" -#include "trace.h" +#include "modules/rtp_rtcp/source/forward_error_correction.h" +#include "modules/rtp_rtcp/source/forward_error_correction_internal.h" +#include "modules/rtp_rtcp/source/rtp_utility.h" +#include "system_wrappers/interface/trace.h" namespace webrtc { @@ -33,42 +35,39 @@ const uint8_t kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear); // Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum. const uint8_t kTransportOverhead = 28; +enum { kMaxFecPackets = ForwardErrorCorrection::kMaxMediaPackets }; + // Used to link media packets to their protecting FEC packets. // -struct ProtectedPacket { - uint16_t seqNum; - ForwardErrorCorrection::Packet* pkt; +// TODO(holmer): Refactor into a proper class. +class ProtectedPacket : public ForwardErrorCorrection::SortablePacket { + public: + scoped_refptr pkt; }; +typedef std::list ProtectedPacketList; + // // Used for internal storage of FEC packets in a list. // -struct FecPacket { - std::list protectedPktList; - uint16_t seqNum; +// TODO(holmer): Refactor into a proper class. +class FecPacket : public ForwardErrorCorrection::SortablePacket { + public: + ProtectedPacketList protectedPktList; uint32_t ssrc; // SSRC of the current frame. - ForwardErrorCorrection::Packet* pkt; + scoped_refptr pkt; }; -bool ForwardErrorCorrection::CompareRecoveredPackets(RecoveredPacket* first, - RecoveredPacket* second) { - if ((first->seqNum > second->seqNum && - (first->seqNum - kMaxMediaPackets) < second->seqNum) || - // We have a wrap in sequence number if the first sequence number is low, - // defined and lower than kMaxMediaPackets and second sequence number is - // high defined as max sequence number (65535) - kMaxMediaPackets. - (first->seqNum < kMaxMediaPackets && // Wrap guard. - second->seqNum > (65535 - kMaxMediaPackets))) { - return false; - } - return true; +bool ForwardErrorCorrection::SortablePacket::LessThan( + const SortablePacket* first, + const SortablePacket* second) { + return (first->seqNum != second->seqNum && + LatestSequenceNumber(first->seqNum, second->seqNum) == second->seqNum); } ForwardErrorCorrection::ForwardErrorCorrection(int32_t id) : _id(id), _generatedFecPackets(kMaxMediaPackets), - _seqNumBase(0), - _lastMediaPacketReceived(false), _fecPacketReceived(false) { } @@ -93,11 +92,11 @@ ForwardErrorCorrection::~ForwardErrorCorrection() { // | | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ int32_t ForwardErrorCorrection::GenerateFEC( - const std::list& mediaPacketList, + const PacketList& mediaPacketList, uint8_t protectionFactor, int numImportantPackets, bool useUnequalProtection, - std::list* fecPacketList) { + PacketList* fecPacketList) { if (mediaPacketList.empty()) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s media packet list is empty", __FUNCTION__); @@ -135,7 +134,7 @@ int32_t ForwardErrorCorrection::GenerateFEC( return -1; } // Do some error checking on the media packets. - std::list::const_iterator mediaListIt = mediaPacketList.begin(); + PacketList::const_iterator mediaListIt = mediaPacketList.begin(); while (mediaListIt != mediaPacketList.end()) { Packet* mediaPacket = *mediaListIt; assert(mediaPacket); @@ -192,7 +191,7 @@ int32_t ForwardErrorCorrection::GenerateFEC( } void ForwardErrorCorrection::GenerateFecBitStrings( - const std::list& mediaPacketList, + const PacketList& mediaPacketList, uint8_t* packetMask, uint32_t numFecPackets) { uint8_t mediaPayloadLength[2]; @@ -204,7 +203,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings( const uint16_t fecRtpOffset = kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize; for (uint32_t i = 0; i < numFecPackets; i++) { - std::list::const_iterator mediaListIt = mediaPacketList.begin(); + PacketList::const_iterator mediaListIt = mediaPacketList.begin(); uint32_t pktMaskIdx = i * numMaskBytes; uint32_t mediaPktIdx = 0; uint16_t fecPacketLength = 0; @@ -271,7 +270,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings( } void ForwardErrorCorrection::GenerateFecUlpHeaders( - const std::list& mediaPacketList, + const PacketList& mediaPacketList, uint8_t* packetMask, uint32_t numFecPackets) { // -- Generate FEC and ULP headers -- @@ -295,7 +294,7 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders( // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | mask cont. (present only when L = 1) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - std::list::const_iterator mediaListIt = mediaPacketList.begin(); + PacketList::const_iterator mediaListIt = mediaPacketList.begin(); Packet* mediaPacket = *mediaListIt; assert(mediaPacket != NULL); const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0; @@ -330,35 +329,28 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders( } void ForwardErrorCorrection::ResetState( - std::list* recoveredPacketList) { - _seqNumBase = 0; - _lastMediaPacketReceived = false; + RecoveredPacketList* recoveredPacketList) { _fecPacketReceived = false; // Free the memory for any existing recovered packets, if the user hasn't. while (!recoveredPacketList->empty()) { - std::list::iterator recoveredPacketListIt = - recoveredPacketList->begin(); - RecoveredPacket* recPacket = *recoveredPacketListIt; - delete recPacket->pkt; - delete recPacket; + delete recoveredPacketList->front(); recoveredPacketList->pop_front(); } assert(recoveredPacketList->empty()); // Free the FEC packet list. while (!_fecPacketList.empty()) { - std::list::iterator fecPacketListIt = _fecPacketList.begin(); + FecPacketList::iterator fecPacketListIt = _fecPacketList.begin(); FecPacket* fecPacket = *fecPacketListIt; - std::list::iterator protectedPacketListIt; + ProtectedPacketList::iterator protectedPacketListIt; protectedPacketListIt = fecPacket->protectedPktList.begin(); while (protectedPacketListIt != fecPacket->protectedPktList.end()) { delete *protectedPacketListIt; - protectedPacketListIt++; - fecPacket->protectedPktList.pop_front(); + protectedPacketListIt = + fecPacket->protectedPktList.erase(protectedPacketListIt); } assert(fecPacket->protectedPktList.empty()); - delete fecPacket->pkt; delete fecPacket; _fecPacketList.pop_front(); } @@ -367,74 +359,75 @@ void ForwardErrorCorrection::ResetState( void ForwardErrorCorrection::InsertMediaPacket( ReceivedPacket* rxPacket, - std::list* recoveredPacketList) { - if (rxPacket->lastMediaPktInFrame) { - if (_lastMediaPacketReceived) { - // We already received the last packet. - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "%s last media packet marked more than once per frame", - __FUNCTION__); - } - _lastMediaPacketReceived = true; - } - bool duplicatePacket = false; - std::list::iterator recoveredPacketListIt = + RecoveredPacketList* recoveredPacketList) { + RecoveredPacketList::iterator recoveredPacketListIt = recoveredPacketList->begin(); + // Search for duplicate packets. while (recoveredPacketListIt != recoveredPacketList->end()) { - RecoveredPacket* recPacket = *recoveredPacketListIt; - if (rxPacket->seqNum == recPacket->seqNum) { + if (rxPacket->seqNum == (*recoveredPacketListIt)->seqNum) { // Duplicate packet, no need to add to list. - duplicatePacket = true; - break; + // Delete duplicate media packet data. + rxPacket->pkt = NULL; + return; } recoveredPacketListIt++; } - if (duplicatePacket) { - // Delete duplicate media packet data. - delete rxPacket->pkt; - return; - } RecoveredPacket* recoverdPacketToInsert = new RecoveredPacket; recoverdPacketToInsert->wasRecovered = false; + recoverdPacketToInsert->returned = false; recoverdPacketToInsert->seqNum = rxPacket->seqNum; recoverdPacketToInsert->pkt = rxPacket->pkt; recoverdPacketToInsert->pkt->length = rxPacket->pkt->length; + // 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. recoveredPacketList->push_back(recoverdPacketToInsert); + recoveredPacketList->sort(SortablePacket::LessThan); + UpdateCoveringFECPackets(recoverdPacketToInsert); } -void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) { +void ForwardErrorCorrection::UpdateCoveringFECPackets(RecoveredPacket* packet) { + for (FecPacketList::iterator it = _fecPacketList.begin(); + it != _fecPacketList.end(); ++it) { + // Is this FEC packet protecting the media packet |packet|? + ProtectedPacketList::iterator protected_it = std::lower_bound( + (*it)->protectedPktList.begin(), + (*it)->protectedPktList.end(), + packet, + SortablePacket::LessThan); + if (protected_it != (*it)->protectedPktList.end() && + (*protected_it)->seqNum == packet->seqNum) { + // Found an FEC packet which is protecting |packet|. + (*protected_it)->pkt = packet->pkt; + } + } +} + +void ForwardErrorCorrection::InsertFECPacket( + ReceivedPacket* rxPacket, + const RecoveredPacketList* recoveredPacketList) { _fecPacketReceived = true; // Check for duplicate. - bool duplicatePacket = false; - std::list::iterator fecPacketListIt = _fecPacketList.begin(); + FecPacketList::iterator fecPacketListIt = _fecPacketList.begin(); while (fecPacketListIt != _fecPacketList.end()) { - FecPacket* fecPacket = *fecPacketListIt; - if (rxPacket->seqNum == fecPacket->seqNum) { - duplicatePacket = true; - break; + if (rxPacket->seqNum == (*fecPacketListIt)->seqNum) { + // Delete duplicate FEC packet data. + rxPacket->pkt = NULL; + return; } fecPacketListIt++; } - if (duplicatePacket) { - // Delete duplicate FEC packet data. - delete rxPacket->pkt; - rxPacket->pkt = NULL; - return; - } FecPacket* fecPacket = new FecPacket; fecPacket->pkt = rxPacket->pkt; fecPacket->seqNum = rxPacket->seqNum; fecPacket->ssrc = rxPacket->ssrc; - // We store this for determining frame completion later. - _seqNumBase = ModuleRTPUtility::BufferToUWord16( + const uint16_t seqNumBase = ModuleRTPUtility::BufferToUWord16( &fecPacket->pkt->data[2]); - const uint16_t maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ? - kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set? + kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set? for (uint16_t byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++) { uint8_t packetMask = fecPacket->pkt->data[12 + byteIdx]; @@ -443,7 +436,7 @@ void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) { ProtectedPacket* protectedPacket = new ProtectedPacket; fecPacket->protectedPktList.push_back(protectedPacket); // This wraps naturally with the sequence number. - protectedPacket->seqNum = static_cast(_seqNumBase + + protectedPacket->seqNum = static_cast(seqNumBase + (byteIdx << 3) + bitIdx); protectedPacket->pkt = NULL; } @@ -451,24 +444,60 @@ void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) { } if (fecPacket->protectedPktList.empty()) { // All-zero packet mask; we can discard this FEC packet. - delete fecPacket->pkt; + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, + "FEC packet %u has an all-zero packet mask.", + fecPacket->seqNum, __FUNCTION__); delete fecPacket; } else { + AssignRecoveredPackets(fecPacket, + recoveredPacketList); + // 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. _fecPacketList.push_back(fecPacket); + _fecPacketList.sort(SortablePacket::LessThan); + if (_fecPacketList.size() > kMaxFecPackets) { + DiscardFECPacket(_fecPacketList.front()); + _fecPacketList.pop_front(); + } + assert(_fecPacketList.size() <= kMaxFecPackets); + } +} + +void ForwardErrorCorrection::AssignRecoveredPackets( + FecPacket* fec_packet, + const RecoveredPacketList* recovered_packets) { + // Search for missing packets which have arrived or have been recovered by + // another FEC packet. + ProtectedPacketList* not_recovered = &fec_packet->protectedPktList; + RecoveredPacketList already_recovered; + std::set_intersection( + recovered_packets->begin(), recovered_packets->end(), + not_recovered->begin(), not_recovered->end(), + std::inserter(already_recovered, already_recovered.end()), + SortablePacket::LessThan); + // Set the FEC pointers to all recovered packets so that we don't have to + // search for them when we are doing recovery. + ProtectedPacketList::iterator not_recovered_it = not_recovered->begin(); + for (RecoveredPacketList::iterator it = already_recovered.begin(); + it != already_recovered.end(); ++it) { + // Search for the next recovered packet in |not_recovered|. + while ((*not_recovered_it)->seqNum != (*it)->seqNum) + ++not_recovered_it; + (*not_recovered_it)->pkt = (*it)->pkt; } } void ForwardErrorCorrection::InsertPackets( - std::list* receivedPacketList, - std::list* recoveredPacketList) { + ReceivedPacketList* receivedPacketList, + RecoveredPacketList* recoveredPacketList) { while (!receivedPacketList->empty()) { ReceivedPacket* rxPacket = receivedPacketList->front(); if (rxPacket->isFec) { - InsertFECPacket(rxPacket); + InsertFECPacket(rxPacket, recoveredPacketList); } else { - // Insert packet in end of list. + // Insert packet at the end of |recoveredPacketList|. InsertMediaPacket(rxPacket, recoveredPacketList); } // Delete the received packet "wrapper", but not the packet data. @@ -476,247 +505,195 @@ void ForwardErrorCorrection::InsertPackets( receivedPacketList->pop_front(); } assert(receivedPacketList->empty()); - // Sort our recovered packet list. - recoveredPacketList->sort(CompareRecoveredPackets); + DiscardOldPackets(recoveredPacketList); +} + +void ForwardErrorCorrection::InitRecovery( + const FecPacket* fec_packet, + RecoveredPacket* recovered) { + // This is the first packet which we try to recover with. + const uint16_t ulpHeaderSize = fec_packet->pkt->data[0] & 0x40 ? + kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set? + recovered->pkt = new Packet; + memset(recovered->pkt->data, 0, IP_PACKET_SIZE); + recovered->returned = false; + recovered->wasRecovered = true; + uint8_t protectionLength[2]; + // Copy the protection length from the ULP header. + memcpy(protectionLength, &fec_packet->pkt->data[10], 2); + // Copy FEC payload, skipping the ULP header. + memcpy(&recovered->pkt->data[kRtpHeaderSize], + &fec_packet->pkt->data[kFecHeaderSize + ulpHeaderSize], + ModuleRTPUtility::BufferToUWord16(protectionLength)); + // Copy the length recovery field. + memcpy(recovered->length_recovery, &fec_packet->pkt->data[8], 2); + // Copy the first 2 bytes of the FEC header. + memcpy(recovered->pkt->data, fec_packet->pkt->data, 2); + // Copy the 5th to 8th bytes of the FEC header. + memcpy(&recovered->pkt->data[4], &fec_packet->pkt->data[4], 4); + // Set the SSRC field. + ModuleRTPUtility::AssignUWord32ToBuffer(&recovered->pkt->data[8], + fec_packet->ssrc); +} + +void ForwardErrorCorrection::FinishRecovery(RecoveredPacket* recovered) { + // Set the RTP version to 2. + recovered->pkt->data[0] |= 0x80; // Set the 1st bit. + recovered->pkt->data[0] &= 0xbf; // Clear the 2nd bit. + + // Set the SN field. + ModuleRTPUtility::AssignUWord16ToBuffer(&recovered->pkt->data[2], + recovered->seqNum); + // Recover the packet length. + recovered->pkt->length = ModuleRTPUtility::BufferToUWord16( + recovered->length_recovery) + kRtpHeaderSize; +} + +void ForwardErrorCorrection::XorPackets(const Packet* src_packet, + RecoveredPacket* dst_packet) { + // XOR with the first 2 bytes of the RTP header. + for (uint32_t i = 0; i < 2; i++) { + dst_packet->pkt->data[i] ^= src_packet->data[i]; + } + // XOR with the 5th to 8th bytes of the RTP header. + for (uint32_t i = 4; i < 8; i++) { + dst_packet->pkt->data[i] ^= src_packet->data[i]; + } + // XOR with the network-ordered payload size. + uint8_t mediaPayloadLength[2]; + ModuleRTPUtility::AssignUWord16ToBuffer( + mediaPayloadLength, + src_packet->length - kRtpHeaderSize); + dst_packet->length_recovery[0] ^= mediaPayloadLength[0]; + dst_packet->length_recovery[1] ^= mediaPayloadLength[1]; + + // XOR with RTP payload. + // TODO(marpan/ajm): Are we doing more XORs than required here? + for (int32_t i = kRtpHeaderSize; i < src_packet->length; i++) { + dst_packet->pkt->data[i] ^= src_packet->data[i]; + } } void ForwardErrorCorrection::RecoverPacket( - const FecPacket& fecPacket, + const FecPacket* fecPacket, RecoveredPacket* recPacketToInsert) { - uint8_t lengthRecovery[2]; - const uint16_t ulpHeaderSize = fecPacket.pkt->data[0] & 0x40 ? - kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set? - - recPacketToInsert->wasRecovered = true; - recPacketToInsert->pkt = new Packet; - memset(recPacketToInsert->pkt->data, 0, IP_PACKET_SIZE); - - uint8_t protectionLength[2]; - // Copy the protection length from the ULP header. - memcpy(&protectionLength, &fecPacket.pkt->data[10], 2); - - // Copy the first 2 bytes of the FEC header. - memcpy(recPacketToInsert->pkt->data, fecPacket.pkt->data, 2); - - // Copy the 5th to 8th bytes of the FEC header. - memcpy(&recPacketToInsert->pkt->data[4], &fecPacket.pkt->data[4], 4); - - // Set the SSRC field. - ModuleRTPUtility::AssignUWord32ToBuffer(&recPacketToInsert->pkt->data[8], - fecPacket.ssrc); - - // Copy the length recovery field. - memcpy(&lengthRecovery, &fecPacket.pkt->data[8], 2); - - // Copy FEC payload, skipping the ULP header. - memcpy(&recPacketToInsert->pkt->data[kRtpHeaderSize], - &fecPacket.pkt->data[kFecHeaderSize + ulpHeaderSize], - ModuleRTPUtility::BufferToUWord16(protectionLength)); - - std::list::const_iterator protectedPacketListIt = - fecPacket.protectedPktList.begin(); - - while (protectedPacketListIt != fecPacket.protectedPktList.end()) { - ProtectedPacket* protectedPacket = *protectedPacketListIt; - if (protectedPacket->pkt == NULL) { + InitRecovery(fecPacket, recPacketToInsert); + ProtectedPacketList::const_iterator protected_it = + fecPacket->protectedPktList.begin(); + while (protected_it != fecPacket->protectedPktList.end()) { + if ((*protected_it)->pkt == NULL) { // This is the packet we're recovering. - recPacketToInsert->seqNum = protectedPacket->seqNum; + recPacketToInsert->seqNum = (*protected_it)->seqNum; } else { - // XOR with the first 2 bytes of the RTP header. - for (uint32_t i = 0; i < 2; i++) { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } - // XOR with the 5th to 8th bytes of the RTP header. - for (uint32_t i = 4; i < 8; i++) { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } - // XOR with the network-ordered payload size. - uint8_t mediaPayloadLength[2]; - ModuleRTPUtility::AssignUWord16ToBuffer( - mediaPayloadLength, - protectedPacket->pkt->length - kRtpHeaderSize); - lengthRecovery[0] ^= mediaPayloadLength[0]; - lengthRecovery[1] ^= mediaPayloadLength[1]; - - // XOR with RTP payload. - // TODO: Are we doing more XORs than required here? - for (int32_t i = kRtpHeaderSize; - i < protectedPacket->pkt->length; - i++) { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } + XorPackets((*protected_it)->pkt, recPacketToInsert); } - protectedPacketListIt++; + ++protected_it; } - // Set the RTP version to 2. - recPacketToInsert->pkt->data[0] |= 0x80; // Set the 1st bit. - recPacketToInsert->pkt->data[0] &= 0xbf; // Clear the 2nd bit. - - // Assume a recovered marker bit indicates the last media packet in a frame. - if (recPacketToInsert->pkt->data[1] & 0x80) { - if (_lastMediaPacketReceived) { - // Multiple marker bits are illegal. - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "%s recovered media packet contains a marker bit, but the last " - "media packet in this frame has already been marked", - __FUNCTION__); - } - _lastMediaPacketReceived = true; - } - // Set the SN field. - ModuleRTPUtility::AssignUWord16ToBuffer(&recPacketToInsert->pkt->data[2], - recPacketToInsert->seqNum); - // Recover the packet length. - recPacketToInsert->pkt->length = - ModuleRTPUtility::BufferToUWord16(lengthRecovery) + kRtpHeaderSize; -} - -uint32_t ForwardErrorCorrection::NumberOfProtectedPackets( - const FecPacket& fecPacket, - std::list* recoveredPacketList) { - uint32_t protectedPacketsFound = 0; - std::list::const_iterator protectedPacketListIt = - fecPacket.protectedPktList.begin(); - - while (protectedPacketListIt != fecPacket.protectedPktList.end()) { - ProtectedPacket* protectedPacket = *protectedPacketListIt; - if (protectedPacket->pkt != NULL) { - // We already have the required packet. - protectedPacketsFound++; - } else { - // Search for the required packet. - std::list::iterator recoveredPacketListIt = - recoveredPacketList->begin(); - - while (recoveredPacketListIt != recoveredPacketList->end()) { - RecoveredPacket* recPacket = *recoveredPacketListIt; - recoveredPacketListIt++; - if (protectedPacket->seqNum == recPacket->seqNum) { - protectedPacket->pkt = recPacket->pkt; - protectedPacketsFound++; - break; - } - } - // Since the recovered packet list is already sorted, we don't need to - // restart at the beginning of the list unless the previous protected - // packet wasn't found. - if (protectedPacket->pkt == NULL) { - recoveredPacketListIt = recoveredPacketList->begin(); - } - } - protectedPacketListIt++; - } - return protectedPacketsFound; + FinishRecovery(recPacketToInsert); } void ForwardErrorCorrection::AttemptRecover( - std::list* recoveredPacketList) { - std::list::iterator fecPacketListIt = _fecPacketList.begin(); + RecoveredPacketList* recoveredPacketList) { + FecPacketList::iterator fecPacketListIt = _fecPacketList.begin(); while (fecPacketListIt != _fecPacketList.end()) { - // Store this in case a discard is required. - std::list::iterator fecPacketListItToDiscard = fecPacketListIt; - // Search for each FEC packet's protected media packets. - FecPacket* fecPacket = *fecPacketListIt; - uint32_t protectedPacketsFound = - NumberOfProtectedPackets(*fecPacket, recoveredPacketList); + int packets_missing = NumCoveredPacketsMissing(*fecPacketListIt); - if (protectedPacketsFound == fecPacket->protectedPktList.size() - 1) { + // We can only recover one packet with an FEC packet. + if (packets_missing == 1) { // Recovery possible. RecoveredPacket* packetToInsert = new RecoveredPacket; - RecoverPacket(*fecPacket, packetToInsert); + packetToInsert->pkt = NULL; + RecoverPacket(*fecPacketListIt, packetToInsert); - // Add recovered packet in back of list. + // Add recovered packet to the list of recovered packets and update any + // FEC packets covering this packet with a pointer to the data. + // 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. recoveredPacketList->push_back(packetToInsert); - - // Sort our recovered packet list. - recoveredPacketList->sort(CompareRecoveredPackets); - - protectedPacketsFound++; - assert(protectedPacketsFound == fecPacket->protectedPktList.size()); + recoveredPacketList->sort(SortablePacket::LessThan); + UpdateCoveringFECPackets(packetToInsert); + DiscardOldPackets(recoveredPacketList); + DiscardFECPacket(*fecPacketListIt); + fecPacketListIt = _fecPacketList.erase(fecPacketListIt); // A packet has been recovered. We need to check the FEC list again, as // this may allow additional packets to be recovered. // Restart for first FEC packet. fecPacketListIt = _fecPacketList.begin(); - if (_fecPacketList.begin() == fecPacketListItToDiscard) { - // If we're deleting the first item, we need to get the next first. - fecPacketListIt++; - } + } else if (packets_missing == 0) { + // Either all protected packets arrived or have been recovered. We can + // discard this FEC packet. + DiscardFECPacket(*fecPacketListIt); + fecPacketListIt = _fecPacketList.erase(fecPacketListIt); } else { fecPacketListIt++; } - if (protectedPacketsFound == fecPacket->protectedPktList.size()) { - // Either all protected packets arrived or have been recovered. - // We can discard this FEC packet. - std::list::iterator protectedPacketListIt = - fecPacket->protectedPktList.begin(); - while (protectedPacketListIt != fecPacket->protectedPktList.end()) { - delete *protectedPacketListIt; - protectedPacketListIt++; - fecPacket->protectedPktList.pop_front(); - } - assert(fecPacket->protectedPktList.empty()); - delete fecPacket->pkt; - delete fecPacket; - _fecPacketList.erase(fecPacketListItToDiscard); - } } } -int32_t ForwardErrorCorrection::DecodeFEC( - std::list* receivedPacketList, - std::list* recoveredPacketList, - uint16_t lastFECSeqNum, - bool& frameComplete) { - // TODO: can we check for multiple ULP headers, and return an error? - - // Allow an empty received packet list when complete is true as a teardown - // indicator. - if (receivedPacketList->empty() && !frameComplete) { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s received packet list is empty, but we're not tearing down here", - __FUNCTION__); - return -1; - } - - if (frameComplete) { - // We have a new frame. - ResetState(recoveredPacketList); - } - InsertPackets(receivedPacketList, recoveredPacketList); - - AttemptRecover(recoveredPacketList); - - // Check if we have a complete frame. - frameComplete = false; - - if (_lastMediaPacketReceived) { - frameComplete = true; - if(!_fecPacketReceived) { - // best estimate we have if we have not received a FEC packet - _seqNumBase = lastFECSeqNum + 1; - } - // With this we assume the user is attempting to decode a FEC stream. - uint16_t seqNumIdx = 0; - std::list::iterator recPacketListIt = - recoveredPacketList->begin(); - while (recPacketListIt != recoveredPacketList->end() && - frameComplete == true) { - RecoveredPacket* recPacket = *recPacketListIt; - if (recPacket->seqNum != - static_cast(_seqNumBase + seqNumIdx)) { - frameComplete = false; - break; +int ForwardErrorCorrection::NumCoveredPacketsMissing( + const FecPacket* fec_packet) { + int packets_missing = 0; + ProtectedPacketList::const_iterator it = fec_packet->protectedPktList.begin(); + for (; it != fec_packet->protectedPktList.end(); ++it) { + if ((*it)->pkt == NULL) { + ++packets_missing; + if (packets_missing > 1) { + break; // We can't recover more than one packet. } - recPacketListIt++; - seqNumIdx++; } } + return packets_missing; +} + +void ForwardErrorCorrection::DiscardFECPacket(FecPacket* fec_packet) { + while (!fec_packet->protectedPktList.empty()) { + delete fec_packet->protectedPktList.front(); + fec_packet->protectedPktList.pop_front(); + } + assert(fec_packet->protectedPktList.empty()); + delete fec_packet; +} + +void ForwardErrorCorrection::DiscardOldPackets( + RecoveredPacketList* recoveredPacketList) { + while (recoveredPacketList->size() > kMaxMediaPackets) { + ForwardErrorCorrection::RecoveredPacket* packet = + recoveredPacketList->front(); + delete packet; + recoveredPacketList->pop_front(); + } + assert(recoveredPacketList->size() <= kMaxMediaPackets); +} + +int32_t ForwardErrorCorrection::DecodeFEC( + ReceivedPacketList* receivedPacketList, + RecoveredPacketList* recoveredPacketList) { + // TODO(marpan/ajm): can we check for multiple ULP headers, and return an + // error? + InsertPackets(receivedPacketList, recoveredPacketList); + AttemptRecover(recoveredPacketList); return 0; } uint16_t ForwardErrorCorrection::PacketOverhead() { return kFecHeaderSize + kUlpHeaderSizeLBitSet; } + +uint16_t ForwardErrorCorrection::LatestSequenceNumber(uint16_t first, + uint16_t second) { + bool wrap = (first < 0x00ff && second > 0xff00) || + (first > 0xff00 && second < 0x00ff); + if (second > first && !wrap) + return second; + else if (second <= first && !wrap) + return first; + else if (second < first && wrap) + return second; + else + return first; +} + } // namespace webrtc diff --git a/src/modules/rtp_rtcp/source/forward_error_correction.h b/src/modules/rtp_rtcp/source/forward_error_correction.h index 777a11b1ce..1e6c7c1f6c 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.h +++ b/src/modules/rtp_rtcp/source/forward_error_correction.h @@ -14,13 +14,15 @@ #include #include +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "system_wrappers/interface/ref_count.h" +#include "system_wrappers/interface/scoped_refptr.h" #include "typedefs.h" -#include "rtp_rtcp_defines.h" namespace webrtc { // Forward declaration. -struct FecPacket; +class FecPacket; /** * Performs codec-independent forward error correction (FEC), based on RFC 5109. @@ -33,9 +35,45 @@ class ForwardErrorCorrection { // Maximum number of media packets we can protect static const int kMaxMediaPackets = 48; - struct Packet { - uint16_t length; /**> Length of packet in bytes. */ - uint8_t data[IP_PACKET_SIZE]; /**> Packet data. */ + // TODO(holmer): As a next step all these struct-like packet classes should be + // refactored into proper classes, and their members should be made private. + // This will require parts of the functionality in forward_error_correction.cc + // and receiver_fec.cc to be refactored into the packet classes. + class Packet { + public: + Packet() : ref_count_(0) {} + virtual ~Packet() {} + + // Add a reference. + virtual int32_t AddRef() { + return ++ref_count_; + } + + // Release a reference. Will delete the object if the reference count + // reaches zero. + virtual int32_t Release() { + int32_t ref_count; + ref_count = --ref_count_; + if (ref_count == 0) + delete this; + return ref_count; + } + + uint16_t length; // Length of packet in bytes. + uint8_t data[IP_PACKET_SIZE]; // Packet data. + + private: + int32_t ref_count_; // Counts the number of references to a packet. + }; + + // TODO(holmer): Refactor into a proper class. + class SortablePacket { + public: + // True if first is <= than second. + static bool LessThan(const SortablePacket* first, + const SortablePacket* second); + + uint16_t seqNum; }; /** @@ -56,31 +94,37 @@ class ForwardErrorCorrection { * media packets, but in the case of an FEC packet protecting a single * missing media packet, we have no other means of obtaining it. */ - struct ReceivedPacket { - uint16_t seqNum; /**> Sequence number of packet. */ - uint32_t ssrc; /**> SSRC of the current frame. Must be set for FEC - packets, but not required for media packets. */ - bool isFec; /**> Set to true if this is an FEC packet and false - otherwise. */ - bool lastMediaPktInFrame; /**> Set to true to mark the last media packet in - the frame and false otherwise. */ - Packet* pkt; /**> Pointer to the packet storage. */ + // TODO(holmer): Refactor into a proper class. + class ReceivedPacket : public SortablePacket { + public: + uint32_t ssrc; // SSRC of the current frame. Must be set for FEC + // packets, but not required for media packets. + bool isFec; // Set to true if this is an FEC packet and false + // otherwise. + scoped_refptr pkt; // Pointer to the packet storage. }; /** * The recovered list parameter of #DecodeFEC() will reference structs of * this type. */ - struct RecoveredPacket { - bool wasRecovered; /**> Will be true if this packet was recovered by - the FEC. Otherwise it was a media packet passed in - through the received packet list. */ - uint16_t seqNum; /**> Sequence number of the packet. This is mostly for - implementation convenience but could be utilized - by the user if so desired. */ - Packet* pkt; /**> Pointer to the packet storage. */ + // TODO(holmer): Refactor into a proper class. + class RecoveredPacket : public SortablePacket { + public: + bool wasRecovered; // Will be true if this packet was recovered by + // the FEC. Otherwise it was a media packet passed in + // through the received packet list. + bool returned; // True when the packet already has been returned to the + // caller through the callback. + uint8_t length_recovery[2]; // Two bytes used for recovering the packet + // length with XOR operations. + scoped_refptr pkt; // Pointer to the packet storage. }; + typedef std::list PacketList; + typedef std::list ReceivedPacketList; + typedef std::list RecoveredPacketList; + /** * \param[in] id Module ID */ @@ -118,17 +162,17 @@ class ForwardErrorCorrection { * * \return 0 on success, -1 on failure. */ - int32_t GenerateFEC(const std::list& mediaPacketList, + int32_t GenerateFEC(const PacketList& mediaPacketList, uint8_t protectionFactor, int numImportantPackets, bool useUnequalProtection, - std::list* fecPacketList); + PacketList* fecPacketList); /** * Decodes a list of media and FEC packets. It will parse the input received * packet list, storing FEC packets internally and inserting media packets to * the output recovered packet list. The recovered list will be sorted by - * as cending sequence number and have duplicates removed. The function + * ascending sequence number and have duplicates removed. The function * should be called as new packets arrive, with the recovered list being * progressively assembled with each call. The received packet list will be * empty at output.\n @@ -139,12 +183,8 @@ class ForwardErrorCorrection { * packets, in which case they must remove deleted packets from the * recovered list.\n * - * Before deleting an instance of the class, call the function with an empty - * received packet list and the completion parameter set to true. This will - * free any outstanding memory. - * * \param[in] receivedPacketList List of new received packets, of type - * #ReceivedPacket, beloning to a single + * #ReceivedPacket, belonging to a single * frame. At output the list will be empty, * with packets either stored internally, * or accessible through the recovered list. @@ -152,24 +192,12 @@ class ForwardErrorCorrection { * #RecoveredPacket, belonging to a single * frame. The memory available through the * list will be valid until the next call to - * DecodeFEC() in which the completion - * parameter is set to true. - * \param[in] lastFECSeqNum Estimated last seqNumber before this frame. - * \param[in,out] frameComplete Set to true on input to indicate the start - * of a new frame. On output, this will be - * set to true if all media packets in the - * frame have been recovered. Note that the - * frame may be complete without this - * parameter having been set, as it may not - * always be possible to determine frame - * completion. + * DecodeFEC(). * * \return 0 on success, -1 on failure. */ - int32_t DecodeFEC(std::list* receivedPacketList, - std::list* recoveredPacketList, - uint16_t lastFECSeqNum, - bool& frameComplete); + int32_t DecodeFEC(ReceivedPacketList* receivedPacketList, + RecoveredPacketList* recoveredPacketList); /** * Gets the size in bytes of the FEC/ULP headers, which must be accounted for * as packet overhead. @@ -177,55 +205,84 @@ class ForwardErrorCorrection { */ static uint16_t PacketOverhead(); - private: - // True if first is <= than second. - static bool CompareRecoveredPackets(RecoveredPacket* first, - RecoveredPacket* second); - - void GenerateFecUlpHeaders(const std::list& mediaPacketList, - uint8_t* packetMask, - uint32_t numFecPackets); - - void GenerateFecBitStrings(const std::list& mediaPacketList, - uint8_t* packetMask, - uint32_t numFecPackets); - // Reset internal states from last frame and clear the recoveredPacketList. - void ResetState(std::list* recoveredPacketList); + // Frees all memory allocated by this class. + void ResetState(RecoveredPacketList* recoveredPacketList); + + private: + typedef std::list FecPacketList; + + void GenerateFecUlpHeaders(const PacketList& mediaPacketList, + uint8_t* packetMask, + uint32_t numFecPackets); + + void GenerateFecBitStrings(const PacketList& mediaPacketList, + uint8_t* packetMask, + uint32_t numFecPackets); // Insert received packets into FEC or recovered list. - void InsertPackets(std::list* receivedPacketList, - std::list* recoveredPacketList); + void InsertPackets(ReceivedPacketList* receivedPacketList, + RecoveredPacketList* recoveredPacketList); // Insert media packet into recovered packet list. We delete duplicates. void InsertMediaPacket(ReceivedPacket* rxPacket, - std::list* recoveredPacketList); + RecoveredPacketList* recoveredPacketList); + + // Assigns pointers to the recovered packet from all FEC packets which cover + // it. + // Note: This reduces the complexity when we want to try to recover a packet + // since we don't have to find the intersection between recovered packets and + // packets covered by the FEC packet. + void UpdateCoveringFECPackets(RecoveredPacket* packet); // Insert packet into FEC list. We delete duplicates. - void InsertFECPacket(ReceivedPacket* rxPacket); + void InsertFECPacket(ReceivedPacket* rxPacket, + const RecoveredPacketList* recoveredPacketList); + + // Assigns pointers to already recovered packets covered by this FEC packet. + static void AssignRecoveredPackets( + FecPacket* fec_packet, + const RecoveredPacketList* recovered_packets); // Insert into recovered list in correct position. void InsertRecoveredPacket( RecoveredPacket* recPacketToInsert, - std::list* recoveredPacketList); + RecoveredPacketList* recoveredPacketList); // Attempt to recover missing packets. - void AttemptRecover(std::list* recoveredPacketList); + void AttemptRecover(RecoveredPacketList* recoveredPacketList); + + // Initializes the packet recovery using the FEC packet. + static void InitRecovery(const FecPacket* fec_packet, + RecoveredPacket* recovered); + + // Performs XOR between |src_packet| and |dst_packet| and stores the result + // in |dst_packet|. + static void XorPackets(const Packet* src_packet, + RecoveredPacket* dst_packet); + + // Finish up the recovery of a packet. + static void FinishRecovery(RecoveredPacket* recovered); // Recover a missing packet. - void RecoverPacket(const FecPacket& fecPacket, + void RecoverPacket(const FecPacket* fecPacket, RecoveredPacket* recPacketToInsert); - // Get number of protected packet in the fecPacket. - uint32_t NumberOfProtectedPackets( - const FecPacket& fecPacket, - std::list* recoveredPacketList); + // Get the number of missing media packets which are covered by this + // FEC packet. An FEC packet can recover at most one packet, and if zero + // packets are missing the FEC packet can be discarded. + // This function returns 2 when two or more packets are missing. + static int NumCoveredPacketsMissing(const FecPacket* fec_packet); + + static uint16_t LatestSequenceNumber(uint16_t first, + uint16_t second); + + static void DiscardFECPacket(FecPacket* fec_packet); + static void DiscardOldPackets(RecoveredPacketList* recoveredPacketList); int32_t _id; std::vector _generatedFecPackets; - std::list _fecPacketList; - uint16_t _seqNumBase; - bool _lastMediaPacketReceived; + FecPacketList _fecPacketList; bool _fecPacketReceived; }; } // namespace webrtc diff --git a/src/modules/rtp_rtcp/source/receiver_fec.cc b/src/modules/rtp_rtcp/source/receiver_fec.cc index f677ffde79..77a3465006 100644 --- a/src/modules/rtp_rtcp/source/receiver_fec.cc +++ b/src/modules/rtp_rtcp/source/receiver_fec.cc @@ -8,13 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "receiver_fec.h" +#include "modules/rtp_rtcp/source/receiver_fec.h" #include -#include "rtp_receiver_video.h" -#include "rtp_utility.h" -#include "trace.h" +#include "modules/rtp_rtcp/source/rtp_receiver_video.h" +#include "modules/rtp_rtcp/source/rtp_utility.h" +#include "system_wrappers/interface/scoped_ptr.h" +#include "system_wrappers/interface/trace.h" // RFC 5109 namespace webrtc { @@ -22,9 +23,7 @@ ReceiverFEC::ReceiverFEC(const WebRtc_Word32 id, RTPReceiverVideo* owner) : _id(id), _owner(owner), _fec(new ForwardErrorCorrection(id)), - _payloadTypeFEC(-1), - _lastFECSeqNum(0), - _frameComplete(true) { + _payloadTypeFEC(-1) { } ReceiverFEC::~ReceiverFEC() { @@ -32,16 +31,13 @@ ReceiverFEC::~ReceiverFEC() { while (!_receivedPacketList.empty()){ ForwardErrorCorrection::ReceivedPacket* receivedPacket = _receivedPacketList.front(); - delete receivedPacket->pkt; delete receivedPacket; _receivedPacketList.pop_front(); } assert(_receivedPacketList.empty()); if (_fec != NULL) { - bool frameComplete = true; - _fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList,_lastFECSeqNum, - frameComplete); + _fec->ResetState(&_recoveredPacketList); delete _fec; } } @@ -84,8 +80,7 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket( const WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* incomingRtpPacket, const WebRtc_UWord16 payloadDataLength, - bool& FECpacket, - bool oldPacket) { + bool& FECpacket) { if (_payloadTypeFEC == -1) { return -1; } @@ -107,19 +102,10 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket( if (_payloadTypeFEC == payloadType) { receivedPacket->isFec = true; FECpacket = true; - // We don't need to parse old FEC packets. - // Old FEC packets are sent to jitter buffer as empty packets in the - // callback in rtp_receiver_video. - if (oldPacket) { - delete receivedPacket->pkt; - delete receivedPacket; - return 0; - } } else { receivedPacket->isFec = false; FECpacket = false; } - receivedPacket->lastMediaPktInFrame = rtpHeader->header.markerBit; receivedPacket->seqNum = rtpHeader->header.sequenceNumber; WebRtc_UWord16 blockLength = 0; @@ -135,7 +121,6 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket( // location a corrupt payload can be caught, so don't assert. WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Corrupt payload found in %s", __FUNCTION__); - delete receivedPacket->pkt; delete receivedPacket; return -1; } @@ -147,14 +132,12 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket( // check next RED header if(incomingRtpPacket[rtpHeader->header.headerLength+4] & 0x80) { // more than 2 blocks in packet not supported - delete receivedPacket->pkt; delete receivedPacket; assert(false); return -1; } if(blockLength > payloadDataLength - REDHeaderLength) { // block length longer than packet - delete receivedPacket->pkt; delete receivedPacket; assert(false); return -1; @@ -186,7 +169,6 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket( secondReceivedPacket->pkt = new ForwardErrorCorrection::Packet; secondReceivedPacket->isFec = true; - secondReceivedPacket->lastMediaPktInFrame = false; secondReceivedPacket->seqNum = rtpHeader->header.sequenceNumber; // copy the FEC payload data @@ -226,105 +208,35 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket( payloadDataLength - REDHeaderLength; } - if(receivedPacket->isFec) { - AddReceivedFECInfo(rtpHeader, NULL, FECpacket); - } - if(receivedPacket->pkt->length == 0) { - if (secondReceivedPacket) { - delete secondReceivedPacket->pkt; - } delete secondReceivedPacket; - delete receivedPacket->pkt; delete receivedPacket; return 0; } - // Send any old media packets to jitter buffer, don't push them onto - // received list for FEC decoding (we don't do FEC decoding on old packets). - if (oldPacket && receivedPacket->isFec == false) { - if (ParseAndReceivePacket(receivedPacket->pkt) != 0) { - if (secondReceivedPacket) { - delete secondReceivedPacket->pkt; - } - delete secondReceivedPacket; - delete receivedPacket->pkt; - delete receivedPacket; - return -1; - } - if (secondReceivedPacket) { - delete secondReceivedPacket->pkt; - } - delete secondReceivedPacket; - delete receivedPacket->pkt; - delete receivedPacket; - } else { - _receivedPacketList.push_back(receivedPacket); - if (secondReceivedPacket) { - _receivedPacketList.push_back(secondReceivedPacket); - } + _receivedPacketList.push_back(receivedPacket); + if (secondReceivedPacket) { + _receivedPacketList.push_back(secondReceivedPacket); } return 0; } -void ReceiverFEC::AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - bool& FECpacket) { - // store the highest FEC seq num received - if (_lastFECSeqNum >= rtpHeader->header.sequenceNumber) { - if (_lastFECSeqNum > 0xff00 && rtpHeader->header.sequenceNumber < 0x0ff ) { - // wrap - _lastFECSeqNum = rtpHeader->header.sequenceNumber; - } else { - // old seqNum - } - } else { - // check for a wrap - if(rtpHeader->header.sequenceNumber > 0xff00 && _lastFECSeqNum < 0x0ff ) { - // old seqNum - } else { - _lastFECSeqNum = rtpHeader->header.sequenceNumber; - } - } - - if (incomingRtpPacket) { - // get payload type from RED header - WebRtc_UWord8 payloadType = - incomingRtpPacket[rtpHeader->header.headerLength] & 0x7f; - - // use the payloadType to decide if it's FEC or coded data - if(_payloadTypeFEC == payloadType) { - FECpacket = true; - } else { - FECpacket = false; - } - } -} - -WebRtc_Word32 ReceiverFEC::ProcessReceivedFEC(const bool forceFrameDecode) { +WebRtc_Word32 ReceiverFEC::ProcessReceivedFEC() { if (!_receivedPacketList.empty()) { - if (_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList, - _lastFECSeqNum, _frameComplete) != 0) { + if (_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList) != 0) { return -1; } assert(_receivedPacketList.empty()); } - if (forceFrameDecode) { - _frameComplete = true; - } - if (_frameComplete) { - while (!_recoveredPacketList.empty()) { - ForwardErrorCorrection::RecoveredPacket* recoveredPacket = - _recoveredPacketList.front(); - - if (ParseAndReceivePacket(recoveredPacket->pkt) != 0) { - return -1; - } - delete recoveredPacket->pkt; - delete recoveredPacket; - _recoveredPacketList.pop_front(); + ForwardErrorCorrection::RecoveredPacketList::iterator it = + _recoveredPacketList.begin(); + for (; it != _recoveredPacketList.end(); ++it) { + if ((*it)->returned) // Already sent to the VCM and the jitter buffer. + continue; + if (ParseAndReceivePacket((*it)->pkt) != 0) { + return -1; } - assert(_recoveredPacketList.empty()); + (*it)->returned = true; } return 0; } @@ -346,4 +258,5 @@ int ReceiverFEC::ParseAndReceivePacket( } return 0; } + } // namespace webrtc diff --git a/src/modules/rtp_rtcp/source/receiver_fec.h b/src/modules/rtp_rtcp/source/receiver_fec.h index 74cbb91896..63aaa728bc 100644 --- a/src/modules/rtp_rtcp/source/receiver_fec.h +++ b/src/modules/rtp_rtcp/source/receiver_fec.h @@ -11,8 +11,6 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ -#include - #include "rtp_rtcp_defines.h" // This header is included to get the nested declaration of Packet structure. #include "forward_error_correction.h" @@ -31,14 +29,9 @@ public: WebRtc_Word32 AddReceivedFECPacket(const WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* incomingRtpPacket, const WebRtc_UWord16 payloadDataLength, - bool& FECpacket, - bool oldPacket); + bool& FECpacket); - void AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - bool& FECpacket); - - WebRtc_Word32 ProcessReceivedFEC(const bool forceFrameDecode); + WebRtc_Word32 ProcessReceivedFEC(); void SetPayloadTypeFEC(const WebRtc_Word8 payloadType); @@ -46,13 +39,14 @@ private: int ParseAndReceivePacket(const ForwardErrorCorrection::Packet* packet); int _id; - RTPReceiverVideo* _owner; + RTPReceiverVideo* _owner; ForwardErrorCorrection* _fec; - std::list _receivedPacketList; - std::list _recoveredPacketList; - WebRtc_Word8 _payloadTypeFEC; - WebRtc_UWord16 _lastFECSeqNum; - bool _frameComplete; + // TODO(holmer): In the current version _receivedPacketList is never more + // than one packet, since we process FEC every time a new packet + // arrives. We should remove the list. + ForwardErrorCorrection::ReceivedPacketList _receivedPacketList; + ForwardErrorCorrection::RecoveredPacketList _recoveredPacketList; + WebRtc_Word8 _payloadTypeFEC; }; } // namespace webrtc diff --git a/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc b/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc index 3dfa27c56d..669018e0aa 100644 --- a/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc +++ b/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc @@ -124,6 +124,7 @@ class ReceiverFecTest : public ::testing::Test { fec_ = new ForwardErrorCorrection(0); receiver_fec_ = new ReceiverFEC(0, &rtp_receiver_video_); generator_ = new FrameGenerator(); + receiver_fec_->SetPayloadTypeFEC(kFecPayloadType); } virtual void TearDown() { @@ -132,6 +133,32 @@ class ReceiverFecTest : public ::testing::Test { delete generator_; } + void GenerateAndAddFrames(int num_frames, + int num_packets_per_frame, + std::list* media_rtp_packets, + std::list* media_packets) { + for (int i = 0; i < num_frames; ++i) { + GenerateFrame(num_packets_per_frame, i, media_rtp_packets, + media_packets); + } + for (std::list::iterator it = media_rtp_packets->begin(); + it != media_rtp_packets->end(); ++it) { + BuildAndAddRedMediaPacket(*it); + } + } + + void GenerateFEC(std::list* media_packets, + std::list* fec_packets, + unsigned int num_fec_packets) { + EXPECT_EQ(0, fec_->GenerateFEC( + *media_packets, + num_fec_packets * 255 / media_packets->size(), + 0, + false, + fec_packets)); + ASSERT_EQ(num_fec_packets, fec_packets->size()); + } + void GenerateFrame(int num_media_packets, int frame_offset, std::list* media_rtp_packets, @@ -163,8 +190,7 @@ class ReceiverFecTest : public ::testing::Test { red_packet->data, red_packet->length - kRtpHeaderSize, - is_fec, - false)); + is_fec)); delete red_packet; EXPECT_FALSE(is_fec); } @@ -176,8 +202,7 @@ class ReceiverFecTest : public ::testing::Test { red_packet->data, red_packet->length - kRtpHeaderSize, - is_fec, - false)); + is_fec)); delete red_packet; EXPECT_TRUE(is_fec); } @@ -197,20 +222,13 @@ void DeletePackets(std::list* packets) { TEST_F(ReceiverFecTest, TwoMediaOneFec) { const unsigned int kNumFecPackets = 1u; - const unsigned int kNumMediaPackets = 2u; std::list media_rtp_packets; std::list media_packets; GenerateFrame(2, 0, &media_rtp_packets, &media_packets); std::list fec_packets; - EXPECT_EQ(0, fec_->GenerateFEC(media_packets, - kNumFecPackets * 255 / kNumMediaPackets, - 0, - false, - &fec_packets)); - ASSERT_EQ(kNumFecPackets, fec_packets.size()); + GenerateFEC(&media_packets, &fec_packets, kNumFecPackets); // Recovery - receiver_fec_->SetPayloadTypeFEC(kFecPayloadType); std::list::iterator media_it = media_rtp_packets.begin(); BuildAndAddRedMediaPacket(*media_it); // Drop one media packet. @@ -223,27 +241,21 @@ TEST_F(ReceiverFecTest, TwoMediaOneFec) { ++it; VerifyReconstructedMediaPacket(*it, 1); } - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false)); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); DeletePackets(&media_packets); } TEST_F(ReceiverFecTest, TwoMediaTwoFec) { const unsigned int kNumFecPackets = 2u; - const unsigned int kNumMediaPackets = 2u; std::list media_rtp_packets; std::list media_packets; GenerateFrame(2, 0, &media_rtp_packets, &media_packets); std::list fec_packets; - EXPECT_EQ(0, fec_->GenerateFEC(media_packets, - kNumFecPackets * 255 / kNumMediaPackets, - 0, - false, &fec_packets)); - ASSERT_EQ(kNumFecPackets, fec_packets.size()); + GenerateFEC(&media_packets, &fec_packets, kNumFecPackets); // Recovery // Drop both media packets. - receiver_fec_->SetPayloadTypeFEC(kFecPayloadType); std::list::iterator fec_it = fec_packets.begin(); BuildAndAddRedFecPacket(*fec_it); ++fec_it; @@ -255,28 +267,21 @@ TEST_F(ReceiverFecTest, TwoMediaTwoFec) { ++it; VerifyReconstructedMediaPacket(*it, 1); } - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false)); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); DeletePackets(&media_packets); } TEST_F(ReceiverFecTest, TwoFramesOneFec) { const unsigned int kNumFecPackets = 1u; - const unsigned int kNumMediaPackets = 2u; std::list media_rtp_packets; std::list media_packets; GenerateFrame(1, 0, &media_rtp_packets, &media_packets); GenerateFrame(1, 1, &media_rtp_packets, &media_packets); std::list fec_packets; - EXPECT_EQ(0, fec_->GenerateFEC(media_packets, - kNumFecPackets * 255 / kNumMediaPackets, - 0, - false, - &fec_packets)); - ASSERT_EQ(kNumFecPackets, fec_packets.size()); + GenerateFEC(&media_packets, &fec_packets, kNumFecPackets); // Recovery - receiver_fec_->SetPayloadTypeFEC(kFecPayloadType); BuildAndAddRedMediaPacket(media_rtp_packets.front()); // Drop one media packet. BuildAndAddRedFecPacket(fec_packets.front()); @@ -287,7 +292,27 @@ TEST_F(ReceiverFecTest, TwoFramesOneFec) { ++it; VerifyReconstructedMediaPacket(*it, 1); } - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false)); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + DeletePackets(&media_packets); +} + +TEST_F(ReceiverFecTest, OneCompleteOneUnrecoverableFrame) { + const unsigned int kNumFecPackets = 1u; + std::list media_rtp_packets; + std::list media_packets; + GenerateFrame(1, 0, &media_rtp_packets, &media_packets); + GenerateFrame(2, 1, &media_rtp_packets, &media_packets); + std::list fec_packets; + GenerateFEC(&media_packets, &fec_packets, kNumFecPackets); + + // Recovery + std::list::iterator it = media_rtp_packets.begin(); + BuildAndAddRedMediaPacket(*it); // First frame + BuildAndAddRedMediaPacket(*it); // First packet of second frame. + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_, _, _)) + .Times(1); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); DeletePackets(&media_packets); } @@ -300,15 +325,9 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) { for (unsigned int i = 0; i < kNumMediaPackets; ++i) GenerateFrame(1, i, &media_rtp_packets, &media_packets); std::list fec_packets; - EXPECT_EQ(0, fec_->GenerateFEC(media_packets, - kNumFecPackets * 255 / kNumMediaPackets, - 0, - false, - &fec_packets)); - ASSERT_EQ(kNumFecPackets, fec_packets.size()); + GenerateFEC(&media_packets, &fec_packets, kNumFecPackets); // Recovery - receiver_fec_->SetPayloadTypeFEC(kFecPayloadType); std::list::iterator it = media_rtp_packets.begin(); ++it; // Drop first packet. for (; it != media_rtp_packets.end(); ++it) @@ -320,7 +339,7 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) { for (; it != media_rtp_packets.end(); ++it) VerifyReconstructedMediaPacket(*it, 1); } - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false)); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); DeletePackets(&media_packets); } @@ -342,4 +361,163 @@ TEST_F(ReceiverFecTest, TooManyFrames) { DeletePackets(&media_packets); } +TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) { + // 1 frame with 2 media packets and one FEC packet. One media packet missing. + // Delay the FEC packet. + Packet* delayed_fec = NULL; + const unsigned int kNumFecPacketsBatch1 = 1u; + const unsigned int kNumMediaPacketsBatch1 = 2u; + std::list media_rtp_packets_batch1; + std::list media_packets_batch1; + GenerateFrame(kNumMediaPacketsBatch1, 0, &media_rtp_packets_batch1, + &media_packets_batch1); + std::list fec_packets; + GenerateFEC(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1); + + BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front()); + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(1); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + delayed_fec = fec_packets.front(); + + // Fill the FEC decoder. No packets should be dropped. + const unsigned int kNumMediaPacketsBatch2 = 47u; + std::list media_rtp_packets_batch2; + std::list media_packets_batch2; + GenerateAndAddFrames(kNumMediaPacketsBatch2, 1, &media_rtp_packets_batch2, + &media_packets_batch2); + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(media_packets_batch2.size()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + // Add the delayed FEC packet. One packet should be reconstructed. + BuildAndAddRedFecPacket(delayed_fec); + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(1); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + DeletePackets(&media_packets_batch1); + DeletePackets(&media_packets_batch2); +} + +TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) { + // 1 frame with 2 media packets and one FEC packet. One media packet missing. + // Delay the FEC packet. + Packet* delayed_fec = NULL; + const unsigned int kNumFecPacketsBatch1 = 1u; + const unsigned int kNumMediaPacketsBatch1 = 2u; + std::list media_rtp_packets_batch1; + std::list media_packets_batch1; + GenerateFrame(kNumMediaPacketsBatch1, 0, &media_rtp_packets_batch1, + &media_packets_batch1); + std::list fec_packets; + GenerateFEC(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1); + + BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front()); + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(1); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + delayed_fec = fec_packets.front(); + + // Fill the FEC decoder and force the last packet to be dropped. + const unsigned int kNumMediaPacketsBatch2 = 48u; + std::list media_rtp_packets_batch2; + std::list media_packets_batch2; + GenerateAndAddFrames(kNumMediaPacketsBatch2, 1, &media_rtp_packets_batch2, + &media_packets_batch2); + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(media_packets_batch2.size()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + // Add the delayed FEC packet. No packet should be reconstructed since the + // first media packet of that frame has been dropped due to being too old. + BuildAndAddRedFecPacket(delayed_fec); + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(0); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + DeletePackets(&media_packets_batch1); + DeletePackets(&media_packets_batch2); +} + +TEST_F(ReceiverFecTest, OldFecPacketDropped) { + // 49 frames with 2 media packets and one FEC packet. All media packets + // missing. + const unsigned int kNumMediaPackets = 49 * 2; + std::list media_rtp_packets; + std::list media_packets; + for (unsigned int i = 0; i < kNumMediaPackets / 2; ++i) { + std::list frame_media_rtp_packets; + std::list frame_media_packets; + std::list fec_packets; + GenerateFrame(2, 0, &frame_media_rtp_packets, &frame_media_packets); + GenerateFEC(&frame_media_packets, &fec_packets, 1); + for (std::list::iterator it = fec_packets.begin(); + it != fec_packets.end(); ++it) { + BuildAndAddRedFecPacket(*it); + } + media_packets.insert(media_packets.end(), + frame_media_packets.begin(), + frame_media_packets.end()); + media_rtp_packets.insert(media_rtp_packets.end(), + frame_media_rtp_packets.begin(), + frame_media_rtp_packets.end()); + } + // Don't insert any media packets. + // Only FEC packets inserted. No packets should be recoverable at this time. + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(0); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + // Insert the oldest media packet. The corresponding FEC packet is too old + // and should've been dropped. Only the media packet we inserted will be + // returned. + BuildAndAddRedMediaPacket(media_rtp_packets.front()); + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(1); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + DeletePackets(&media_packets); +} + +TEST_F(ReceiverFecTest, PacketsOnlyReturnedOnce) { + const unsigned int kNumFecPackets = 1u; + std::list media_rtp_packets; + std::list media_packets; + GenerateFrame(1, 0, &media_rtp_packets, &media_packets); + GenerateFrame(2, 1, &media_rtp_packets, &media_packets); + std::list fec_packets; + GenerateFEC(&media_packets, &fec_packets, kNumFecPackets); + + // Recovery + std::list::iterator media_it = media_rtp_packets.begin(); + BuildAndAddRedMediaPacket(*media_it); // First frame. + { + std::list::iterator verify_it = media_rtp_packets.begin(); + VerifyReconstructedMediaPacket(*verify_it, 1); // First frame + } + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + ++media_it; + BuildAndAddRedMediaPacket(*media_it); // 1st packet of 2nd frame. + BuildAndAddRedFecPacket(fec_packets.front()); // Insert FEC packet. + { + InSequence s; + std::list::iterator verify_it = media_rtp_packets.begin(); + ++verify_it; // First frame has already been returned. + VerifyReconstructedMediaPacket(*verify_it, 1); // 1st packet of 2nd frame. + ++verify_it; + VerifyReconstructedMediaPacket(*verify_it, 1); // 2nd packet of 2nd frame. + } + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + ++media_it; + BuildAndAddRedMediaPacket(*media_it); // 2nd packet of 2nd frame. + EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_)) + .Times(0); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC()); + + DeletePackets(&media_packets); +} + } // namespace webrtc diff --git a/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc b/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc index 6726e2c9b7..d794701e26 100644 --- a/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc +++ b/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc @@ -131,7 +131,6 @@ TEST_F(RtpFecTest, HandleIncorrectInputs) { } TEST_F(RtpFecTest, FecRecoveryNoLoss) { - bool frame_complete = true; const int num_important_packets = 0; const bool use_unequal_protection = false; const int num_media_packets = 4; @@ -153,15 +152,14 @@ TEST_F(RtpFecTest, FecRecoveryNoLoss) { memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // No packets lost, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); } TEST_F(RtpFecTest, FecRecoveryWithLoss) { - bool frame_complete = true; const int num_important_packets = 0; const bool use_unequal_protection = false; const int num_media_packets = 4; @@ -184,8 +182,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) { media_loss_mask_[3] = 1; NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // One packet lost, one FEC packet, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); @@ -198,15 +196,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) { media_loss_mask_[3] = 1; NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // 2 packets lost, one FEC packet, cannot get complete recovery. EXPECT_FALSE(IsRecoveryComplete()); } TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) { - bool frame_complete = true; const int num_important_packets = 0; const bool use_unequal_protection = false; const int num_media_packets = 4; @@ -243,8 +240,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) { NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // With media packet#1 and FEC packets #0, #1, #3, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); @@ -259,15 +256,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) { media_loss_mask_[3] = 1; NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // Cannot get complete recovery for this loss configuration. EXPECT_FALSE(IsRecoveryComplete()); } TEST_F(RtpFecTest, FecRecoveryNoLossUep) { - bool frame_complete = true; const int num_important_packets = 2; const bool use_unequal_protection = true; const int num_media_packets = 4; @@ -289,15 +285,14 @@ TEST_F(RtpFecTest, FecRecoveryNoLossUep) { memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // No packets lost, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); } TEST_F(RtpFecTest, FecRecoveryWithLossUep) { - bool frame_complete = true; const int num_important_packets = 2; const bool use_unequal_protection = true; const int num_media_packets = 4; @@ -320,8 +315,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) { media_loss_mask_[3] = 1; NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // One packet lost, one FEC packet, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); @@ -334,15 +329,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) { media_loss_mask_[3] = 1; NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // 2 packets lost, one FEC packet, cannot get complete recovery. EXPECT_FALSE(IsRecoveryComplete()); } TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { - bool frame_complete = true; const int num_important_packets = 1; const bool use_unequal_protection = true; const int num_media_packets = 4; @@ -378,8 +372,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { media_loss_mask_[3] = 1; NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // With media packet#1 and FEC packets #0, #2, #3, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); @@ -394,8 +388,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { media_loss_mask_[3] = 1; NetworkReceivedPackets(); - EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_, - fec_seq_num_, frame_complete)); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); // Cannot get complete recovery for this loss configuration. EXPECT_FALSE(IsRecoveryComplete()); @@ -404,11 +398,11 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { // TODO(marpan): Add more test cases. void RtpFecTest::TearDown() { + fec_->ResetState(&recovered_packet_list_); + delete fec_; FreeRecoveredPacketList(); ClearList(&media_packet_list_); EXPECT_TRUE(media_packet_list_.empty()); - - fec_packet_list_.clear(); } void RtpFecTest::FreeRecoveredPacketList() { @@ -482,18 +476,13 @@ void RtpFecTest:: ReceivedPackets( // obtained from RTP header. These were set in ConstructMediaPackets(). received_packet->seqNum = webrtc::ModuleRTPUtility::BufferToUWord16(&packet->data[2]); - received_packet->lastMediaPktInFrame = (packet->data[1] & 0x80) != 0; } else { // The sequence number, marker bit, and ssrc number are defined in the // RTP header of the FEC packet, which is not constructed in this test. // So we set these values below based on the values generated in // ConstructMediaPackets(). - received_packet->seqNum = seq_num; - // The marker bit (last media packet of frame) for FEC packets is - // always zero. - received_packet->lastMediaPktInFrame = false; // The ssrc value for FEC packets is set to the one used for the // media packets in ConstructMediaPackets(). received_packet->ssrc = ssrc_; diff --git a/src/modules/rtp_rtcp/source/rtp_receiver_video.cc b/src/modules/rtp_rtcp/source/rtp_receiver_video.cc index 4e757930ee..d6a3c17956 100644 --- a/src/modules/rtp_rtcp/source/rtp_receiver_video.cc +++ b/src/modules/rtp_rtcp/source/rtp_receiver_video.cc @@ -247,62 +247,14 @@ RTPReceiverVideo::ParseVideoCodecSpecific(WebRtcRTPHeader* rtpHeader, _criticalSectionReceiverVideo->Leave(); return -1; } - bool oldPacket = false; bool FECpacket = false; - bool wrapped = false; // Not used; just for OldTimeStamp(). - - // Check for old packets. - if (ModuleRTPUtility::OldTimestamp(rtpHeader->header.timestamp, - TimeStamp(), - &wrapped)) - { - // We have an old packet. - // FEC receiver holds a list of packets with current timestamp. - // Setting "oldPacket = true" will send old packets directly - // to the jitter buffer. - oldPacket = true; - retVal = _receiveFEC->AddReceivedFECPacket(rtpHeader, - incomingRtpPacket, - payloadDataLength, - FECpacket, - oldPacket); - } - else - { - // Check for future packets. - if (rtpHeader->header.timestamp != TimeStamp()) - { - // We have a packet from next frame. - // Force a decode with the existing packets. - retVal = _receiveFEC->ProcessReceivedFEC(true); - _currentFecFrameDecoded = false; - } - if(retVal != -1) - { - if (!_currentFecFrameDecoded) - { - retVal = _receiveFEC->AddReceivedFECPacket( - rtpHeader, - incomingRtpPacket, - payloadDataLength, - FECpacket, - oldPacket); - - if (retVal != -1 && (FECpacket || - rtpHeader->header.markerBit)) - { - // Only attempt a decode after receiving the - // last media packet or an FEC packet. - retVal = _receiveFEC->ProcessReceivedFEC(false); - } - }else - { - _receiveFEC->AddReceivedFECInfo(rtpHeader, - incomingRtpPacket, - FECpacket); - } - } - } + retVal = _receiveFEC->AddReceivedFECPacket( + rtpHeader, + incomingRtpPacket, + payloadDataLength, + FECpacket); + if (retVal != -1) + retVal = _receiveFEC->ProcessReceivedFEC(); _criticalSectionReceiverVideo->Leave(); if(retVal == 0 && FECpacket) diff --git a/src/modules/rtp_rtcp/test/testFec/test_fec.cc b/src/modules/rtp_rtcp/test/testFec/test_fec.cc index a30ed63376..867c5021d3 100644 --- a/src/modules/rtp_rtcp/test/testFec/test_fec.cc +++ b/src/modules/rtp_rtcp/test/testFec/test_fec.cc @@ -31,8 +31,8 @@ using namespace webrtc; void ReceivePackets( - std::list* toDecodeList, - std::list* receivedPacketList, + ForwardErrorCorrection::ReceivedPacketList* toDecodeList, + ForwardErrorCorrection::ReceivedPacketList* receivedPacketList, WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate); int main() { @@ -48,11 +48,11 @@ int main() { WebRtc_UWord32 id = 0; ForwardErrorCorrection fec(id); - std::list mediaPacketList; - std::list fecPacketList; - std::list toDecodeList; - std::list receivedPacketList; - std::list recoveredPacketList; + ForwardErrorCorrection::PacketList mediaPacketList; + ForwardErrorCorrection::PacketList fecPacketList; + ForwardErrorCorrection::ReceivedPacketList toDecodeList; + ForwardErrorCorrection::ReceivedPacketList receivedPacketList; + ForwardErrorCorrection::RecoveredPacketList recoveredPacketList; std::list fecMaskList; ForwardErrorCorrection::Packet* mediaPacket; @@ -226,7 +226,7 @@ int main() { return -1; } memset(mediaLossMask, 0, sizeof(mediaLossMask)); - std::list::iterator + ForwardErrorCorrection::PacketList::iterator mediaPacketListItem = mediaPacketList.begin(); ForwardErrorCorrection::ReceivedPacket* receivedPacket; WebRtc_UWord32 mediaPacketIdx = 0; @@ -241,8 +241,7 @@ int main() { mediaLossMask[mediaPacketIdx] = 1; receivedPacket = new ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = - new ForwardErrorCorrection::Packet; + receivedPacket->pkt = new ForwardErrorCorrection::Packet; receivedPacketList.push_back(receivedPacket); receivedPacket->pkt->length = mediaPacket->length; @@ -251,14 +250,12 @@ int main() { receivedPacket->seqNum = ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]); receivedPacket->isFec = false; - receivedPacket->lastMediaPktInFrame = - (mediaPacket->data[1] & 0x80) != 0; } mediaPacketIdx++; ++mediaPacketListItem; } memset(fecLossMask, 0, sizeof(fecLossMask)); - std::list::iterator + ForwardErrorCorrection::PacketList::iterator fecPacketListItem = fecPacketList.begin(); ForwardErrorCorrection::Packet* fecPacket; WebRtc_UWord32 fecPacketIdx = 0; @@ -270,8 +267,7 @@ int main() { fecLossMask[fecPacketIdx] = 1; receivedPacket = new ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = - new ForwardErrorCorrection::Packet; + receivedPacket->pkt = new ForwardErrorCorrection::Packet; receivedPacketList.push_back(receivedPacket); @@ -281,7 +277,6 @@ int main() { receivedPacket->seqNum = seqNum; receivedPacket->isFec = true; - receivedPacket->lastMediaPktInFrame = false; receivedPacket->ssrc = ssrc; fecMaskList.push_back(fecPacketMasks[fecPacketIdx]); @@ -336,7 +331,6 @@ int main() { } printf("\n\n"); #endif - bool complete = true; // Marks start of new frame. bool fecPacketReceived = false; // For error-checking frame completion. while (!receivedPacketList.empty()) { WebRtc_UWord32 numPacketsToDecode = static_cast @@ -349,7 +343,7 @@ int main() { numPacketsToDecode, reorderRate, duplicateRate); if (fecPacketReceived == false) { - std::list::iterator + ForwardErrorCorrection::ReceivedPacketList::iterator toDecodeIt = toDecodeList.begin(); while (toDecodeIt != toDecodeList.end()) { receivedPacket = *toDecodeIt; @@ -359,8 +353,8 @@ int main() { ++toDecodeIt; } } - if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList, seqNum, - complete) != 0) { + if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList) + != 0) { printf("Error: DecodeFEC() failed\n"); return -1; } @@ -368,34 +362,13 @@ int main() { printf("Error: received packet list is not empty\n"); return -1; } - if (recoveredPacketList.size() == numMediaPackets && - fecPacketReceived == true) { - if (complete == true) { -#ifdef VERBOSE_OUTPUT - printf("Full frame recovery correctly marked\n\n"); -#endif - break; - } else { - printf("Error: " - "it should be possible to verify full frame recovery," - " but complete parameter was set to false\n"); - return -1; - } - } else { - if (complete) { - printf("Error: " - "it should not be possible to verify full frame recovery," - " but complete parameter was set to true\n"); - return -1; - } - } } mediaPacketListItem = mediaPacketList.begin(); mediaPacketIdx = 0; while (mediaPacketListItem != mediaPacketList.end()) { if (mediaLossMask[mediaPacketIdx] == 1) { // Should have recovered this packet. - std::list::iterator + ForwardErrorCorrection::RecoveredPacketList::iterator recoveredPacketListItem = recoveredPacketList.begin(); if (recoveredPacketListItem == recoveredPacketList.end()) { @@ -417,13 +390,13 @@ int main() { "original media packet\n"); return -1; } - delete recoveredPacket->pkt; delete recoveredPacket; recoveredPacketList.pop_front(); } mediaPacketIdx++; ++mediaPacketListItem; } + fec.ResetState(&recoveredPacketList); if (!recoveredPacketList.empty()) { printf("Error: excessive number of recovered packets.\n"); printf("\t size is:%u\n", @@ -447,11 +420,10 @@ int main() { // Delete received packets we didn't pass to DecodeFEC(), due to early // frame completion. - std::list::iterator + ForwardErrorCorrection::ReceivedPacketList::iterator receivedPacketIt = receivedPacketList.begin(); while (receivedPacketIt != receivedPacketList.end()) { receivedPacket = *receivedPacketIt; - delete receivedPacket->pkt; delete receivedPacket; ++receivedPacketIt; receivedPacketList.pop_front(); @@ -469,8 +441,7 @@ int main() { } // loop over loss rates // Have DecodeFEC free allocated memory. - bool complete = true; - fec.DecodeFEC(&receivedPacketList, &recoveredPacketList, seqNum, complete); + fec.ResetState(&recoveredPacketList); if (!recoveredPacketList.empty()) { printf("Error: recovered packet list is not empty\n"); return -1; @@ -480,13 +451,13 @@ int main() { } void ReceivePackets( - std::list* toDecodeList, - std::list* receivedPacketList, + ForwardErrorCorrection::ReceivedPacketList* toDecodeList, + ForwardErrorCorrection::ReceivedPacketList* receivedPacketList, WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate) { assert(toDecodeList->empty()); assert(numPacketsToDecode <= receivedPacketList->size()); - std::list::iterator it; + ForwardErrorCorrection::ReceivedPacketList::iterator it; for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++) { it = receivedPacketList->begin(); // Reorder packets. @@ -507,9 +478,7 @@ void ReceivePackets( while (randomVariable < duplicateRate) { ForwardErrorCorrection::ReceivedPacket* duplicatePacket = new ForwardErrorCorrection::ReceivedPacket; - memcpy(duplicatePacket, receivedPacket, - sizeof(ForwardErrorCorrection::ReceivedPacket)); - + *duplicatePacket = *receivedPacket; duplicatePacket->pkt = new ForwardErrorCorrection::Packet; memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data, receivedPacket->pkt->length);