diff --git a/src/modules/rtp_rtcp/source/forward_error_correction.cc b/src/modules/rtp_rtcp/source/forward_error_correction.cc index 3e0d4e34d5..81db2a00fc 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.cc +++ b/src/modules/rtp_rtcp/source/forward_error_correction.cc @@ -8,68 +8,71 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "forward_error_correction.h" -#include "rtp_utility.h" - -#include "trace.h" #include #include +#include "forward_error_correction.h" #include "forward_error_correction_internal.h" +#include "rtp_utility.h" +#include "trace.h" namespace webrtc { // Minimum RTP header size in bytes. -const WebRtc_UWord8 kRtpHeaderSize = 12; +const uint8_t kRtpHeaderSize = 12; // FEC header size in bytes. -const WebRtc_UWord8 kFecHeaderSize = 10; +const uint8_t kFecHeaderSize = 10; // ULP header size in bytes (L bit is set). -const WebRtc_UWord8 kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet); +const uint8_t kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet); // ULP header size in bytes (L bit is cleared). -const WebRtc_UWord8 kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear); +const uint8_t kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear); // Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum. -const WebRtc_UWord8 kTransportOverhead = 28; +const uint8_t kTransportOverhead = 28; + +// Used to link media packets to their protecting FEC packets. +// +struct ProtectedPacket { + uint16_t seqNum; + ForwardErrorCorrection::Packet* pkt; +}; // // Used for internal storage of FEC packets in a list. // -struct FecPacket -{ - ListWrapper protectedPktList; /**> List containing #ProtectedPacket types.*/ - WebRtc_UWord16 seqNum; /**> Sequence number. */ - WebRtc_UWord32 ssrc; /**> SSRC of the current frame. */ - ForwardErrorCorrection::Packet* pkt; /**> Pointer to the packet storage. */ +struct FecPacket { + std::list protectedPktList; + uint16_t seqNum; + uint32_t ssrc; // SSRC of the current frame. + ForwardErrorCorrection::Packet* pkt; }; -// -// Used to link media packets to their protecting FEC packets. -// -struct ProtectedPacket -{ - WebRtc_UWord16 seqNum; /**> Sequence number. */ - ForwardErrorCorrection::Packet* pkt; /**> Pointer to the packet storage. */ -}; - -ForwardErrorCorrection::ForwardErrorCorrection(WebRtc_Word32 id) : - _id(id), - _generatedFecPackets(NULL), - _fecPacketList(), - _seqNumBase(0), - _lastMediaPacketReceived(false), - _fecPacketReceived(false) -{ +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; } -ForwardErrorCorrection::~ForwardErrorCorrection() -{ - if (_generatedFecPackets != NULL) - { - delete [] _generatedFecPackets; - } +ForwardErrorCorrection::ForwardErrorCorrection(int32_t id) + : _id(id), + _generatedFecPackets(kMaxMediaPackets), + _seqNumBase(0), + _lastMediaPacketReceived(false), + _fecPacketReceived(false) { +} + +ForwardErrorCorrection::~ForwardErrorCorrection() { } // Input packet @@ -89,752 +92,631 @@ ForwardErrorCorrection::~ForwardErrorCorrection() // | FEC Level 0 Payload | // | | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -WebRtc_Word32 -ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList, - WebRtc_UWord8 protectionFactor, - int numImportantPackets, - bool useUnequalProtection, - ListWrapper& fecPacketList) -{ - if (mediaPacketList.Empty()) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s media packet list is empty", __FUNCTION__); - return -1; +int32_t ForwardErrorCorrection::GenerateFEC( + const std::list& mediaPacketList, + uint8_t protectionFactor, + int numImportantPackets, + bool useUnequalProtection, + std::list* fecPacketList) { + if (mediaPacketList.empty()) { + WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, + "%s media packet list is empty", __FUNCTION__); + return -1; + } + if (!fecPacketList->empty()) { + WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, + "%s FEC packet list is not empty", __FUNCTION__); + return -1; + } + const uint16_t numMediaPackets = mediaPacketList.size(); + const uint8_t lBit = numMediaPackets > 16 ? 1 : 0; + const uint16_t numMaskBytes = (lBit == 1)? + kMaskSizeLBitSet : kMaskSizeLBitClear; + + if (numMediaPackets > kMaxMediaPackets) { + WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, + "%s can only protect %d media packets per frame; %d requested", + __FUNCTION__, kMaxMediaPackets, numMediaPackets); + return -1; + } + + // Error checking on the number of important packets. + // Can't have more important packets than media packets. + if (numImportantPackets > numMediaPackets) { + WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, + "Number of important packets (%d) greater than number of media " + "packets (%d)", numImportantPackets, numMediaPackets); + return -1; + } + if (numImportantPackets < 0) { + WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, + "Number of important packets (%d) less than zero", + numImportantPackets); + return -1; + } + // Do some error checking on the media packets. + std::list::const_iterator mediaListIt = mediaPacketList.begin(); + while (mediaListIt != mediaPacketList.end()) { + Packet* mediaPacket = *mediaListIt; + assert(mediaPacket); + + if (mediaPacket->length < kRtpHeaderSize) { + WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, + "%s media packet (%d bytes) is smaller than RTP header", + __FUNCTION__, mediaPacket->length); + return -1; } - if (!fecPacketList.Empty()) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s FEC packet list is not empty", __FUNCTION__); - return -1; + // Ensure our FEC packets will fit in a typical MTU. + if (mediaPacket->length + PacketOverhead() + kTransportOverhead > + IP_PACKET_SIZE) { + WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, + "%s media packet (%d bytes) with overhead is larger than MTU(%d)", + __FUNCTION__, mediaPacket->length, IP_PACKET_SIZE); + return -1; } - - const WebRtc_UWord16 numMediaPackets = mediaPacketList.GetSize(); - const WebRtc_UWord8 lBit = numMediaPackets > 16 ? 1 : 0; - const WebRtc_UWord16 numMaskBytes = - (lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear; - const WebRtc_UWord16 ulpHeaderSize = - (lBit == 1)? kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; - const WebRtc_UWord16 fecRtpOffset = - kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize; - - if (numMediaPackets > kMaxMediaPackets) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s can only protect %d media packets per frame; %d requested", - __FUNCTION__, kMaxMediaPackets, numMediaPackets); - return -1; - } - - // Error checking on the number of important packets. - // Can't have more important packets than media packets. - if (numImportantPackets > numMediaPackets) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "Number of important packets (%d) greater than number of media " - "packets (%d)", numImportantPackets, numMediaPackets); - return -1; - } - if (numImportantPackets < 0) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "Number of important packets (%d) less than zero", - numImportantPackets); - return -1; - } - - // Do some error checking on the media packets. - Packet* mediaPacket; - ListItem* mediaListItem = mediaPacketList.First(); - while (mediaListItem != NULL) - { - mediaPacket = static_cast(mediaListItem->GetItem()); - - if (mediaPacket->length < kRtpHeaderSize) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s media packet (%d bytes) is smaller than RTP header", - __FUNCTION__, mediaPacket->length); - return -1; - } - - // Ensure our FEC packets will fit in a typical MTU. - if (mediaPacket->length + PacketOverhead() + kTransportOverhead > - IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s media packet (%d bytes) with overhead is larger than MTU (%d bytes)", - __FUNCTION__, mediaPacket->length, IP_PACKET_SIZE); - return -1; - } - - mediaListItem = mediaPacketList.Next(mediaListItem); - } - - // Result in Q0 with an unsigned round. - WebRtc_UWord32 numFecPackets = (numMediaPackets * protectionFactor + - (1 << 7)) >> 8; - // Generate at least one FEC packet if we need protection. - if (protectionFactor > 0 && numFecPackets == 0) - { - numFecPackets = 1; - } - if (numFecPackets == 0) - { - return 0; - } - assert(numFecPackets <= numMediaPackets); - - // -- Initialize FEC list -- - if (_generatedFecPackets != NULL) - { - delete [] _generatedFecPackets; - _generatedFecPackets = NULL; - } - - _generatedFecPackets = new Packet[numFecPackets]; - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - memset(_generatedFecPackets[i].data, 0, IP_PACKET_SIZE); - _generatedFecPackets[i].length = 0; // Use this as a marker for untouched - // packets. - fecPacketList.PushBack(&_generatedFecPackets[i]); - } - - // -- Generate packet masks -- - WebRtc_UWord8* packetMask = new WebRtc_UWord8[numFecPackets * numMaskBytes]; - memset(packetMask, 0, numFecPackets * numMaskBytes); - internal::GeneratePacketMasks(numMediaPackets, numFecPackets, - numImportantPackets, useUnequalProtection, packetMask); - - // -- Generate FEC bit strings -- - WebRtc_UWord8 mediaPayloadLength[2]; - mediaListItem = mediaPacketList.First(); - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - mediaListItem = mediaPacketList.First(); - WebRtc_UWord32 pktMaskIdx = i * numMaskBytes; - WebRtc_UWord32 mediaPktIdx = 0; - WebRtc_UWord16 fecPacketLength = 0; - while (mediaListItem != NULL) - { - // Each FEC packet has a multiple byte mask. - if (packetMask[pktMaskIdx] & (1 << (7 - mediaPktIdx))) - { - mediaPacket = static_cast(mediaListItem->GetItem()); - - // Assign network-ordered media payload length. - ModuleRTPUtility::AssignUWord16ToBuffer(mediaPayloadLength, - mediaPacket->length - kRtpHeaderSize); - fecPacketLength = mediaPacket->length + fecRtpOffset; - // On the first protected packet, we don't need to XOR. - if (_generatedFecPackets[i].length == 0) - { - // Copy the first 2 bytes of the RTP header. - memcpy(_generatedFecPackets[i].data, mediaPacket->data, 2); - // Copy the 5th to 8th bytes of the RTP header. - memcpy(&_generatedFecPackets[i].data[4], &mediaPacket->data[4], 4); - // Copy network-ordered payload size. - memcpy(&_generatedFecPackets[i].data[8], mediaPayloadLength, 2); - - // Copy RTP payload, leaving room for the ULP header. - memcpy(&_generatedFecPackets[i].data[kFecHeaderSize + ulpHeaderSize], - &mediaPacket->data[kRtpHeaderSize], - mediaPacket->length - kRtpHeaderSize); - } - else - { - // XOR with the first 2 bytes of the RTP header. - _generatedFecPackets[i].data[0] ^= mediaPacket->data[0]; - _generatedFecPackets[i].data[1] ^= mediaPacket->data[1]; - - // XOR with the 5th to 8th bytes of the RTP header. - for (WebRtc_UWord32 j = 4; j < 8; j++) - { - _generatedFecPackets[i].data[j] ^= mediaPacket->data[j]; - } - - // XOR with the network-ordered payload size. - _generatedFecPackets[i].data[8] ^= mediaPayloadLength[0]; - _generatedFecPackets[i].data[9] ^= mediaPayloadLength[1]; - - // XOR with RTP payload, leaving room for the ULP header. - for (WebRtc_Word32 j = kFecHeaderSize + ulpHeaderSize; - j < fecPacketLength; j++) - { - _generatedFecPackets[i].data[j] ^= - mediaPacket->data[j - fecRtpOffset]; - } - } - - if (fecPacketLength > _generatedFecPackets[i].length) - { - _generatedFecPackets[i].length = fecPacketLength; - } - } - - mediaListItem = mediaPacketList.Next(mediaListItem); - mediaPktIdx++; - if (mediaPktIdx == 8) - { - // Switch to the next mask byte. - mediaPktIdx = 0; - pktMaskIdx++; - } - } - - if (_generatedFecPackets[i].length == 0) - { - //Note: This shouldn't happen: means packet mask is wrong or poorly designed - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "Packet mask has row of zeros %d %d %d ", - numMediaPackets, numImportantPackets, numFecPackets); - delete [] packetMask; - return -1; - - } - } - - // -- Generate FEC and ULP headers -- - // - // FEC Header, 10 bytes - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |E|L|P|X| CC |M| PT recovery | SN base | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | TS recovery | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | length recovery | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // - // ULP Header, 4 bytes (for L = 0) - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | Protection Length | mask | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | mask cont. (present only when L = 1) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - mediaListItem = mediaPacketList.First(); - mediaPacket = static_cast(mediaListItem->GetItem()); - assert(mediaPacket != NULL); - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - // -- FEC header -- - _generatedFecPackets[i].data[0] &= 0x7f; // Set E to zero. - if (lBit == 0) - { - _generatedFecPackets[i].data[0] &= 0xbf; // Clear the L bit. - } - else - { - _generatedFecPackets[i].data[0] |= 0x40; // Set the L bit. - } - - // Two byte sequence number from first RTP packet to SN base. - // We use the same sequence number base for every FEC packet, - // but that's not required in general. - memcpy(&_generatedFecPackets[i].data[2], &mediaPacket->data[2], 2); - - // -- ULP header -- - // Copy the payload size to the protection length field. - // (We protect the entire packet.) - ModuleRTPUtility::AssignUWord16ToBuffer(&_generatedFecPackets[i].data[10], - _generatedFecPackets[i].length - kFecHeaderSize - ulpHeaderSize); - - // Copy the packet mask. - memcpy(&_generatedFecPackets[i].data[12], &packetMask[i * numMaskBytes], - numMaskBytes); - } - delete [] packetMask; + mediaListIt++; + } + // Result in Q0 with an unsigned round. + uint32_t numFecPackets = (numMediaPackets * protectionFactor + (1 << 7)) >> 8; + // Generate at least one FEC packet if we need protection. + if (protectionFactor > 0 && numFecPackets == 0) { + numFecPackets = 1; + } + if (numFecPackets == 0) { return 0; + } + assert(numFecPackets <= numMediaPackets); + + // Prepare FEC packets by setting them to 0. + for (uint32_t i = 0; i < numFecPackets; i++) { + memset(_generatedFecPackets[i].data, 0, IP_PACKET_SIZE); + _generatedFecPackets[i].length = 0; // Use this as a marker for untouched + // packets. + fecPacketList->push_back(&_generatedFecPackets[i]); + } + + // -- Generate packet masks -- + uint8_t* packetMask = new uint8_t[numFecPackets * numMaskBytes]; + memset(packetMask, 0, numFecPackets * numMaskBytes); + internal::GeneratePacketMasks(numMediaPackets, numFecPackets, + numImportantPackets, useUnequalProtection, + packetMask); + + GenerateFecBitStrings(mediaPacketList, packetMask, numFecPackets); + + GenerateFecUlpHeaders(mediaPacketList, packetMask, numFecPackets); + + delete [] packetMask; + return 0; } -WebRtc_Word32 -ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList, - ListWrapper& recoveredPacketList, - WebRtc_UWord16 lastFECSeqNum, - bool& frameComplete) -{ - // TODO: can we check for multiple ULP headers, and return an error? +void ForwardErrorCorrection::GenerateFecBitStrings( + const std::list& mediaPacketList, + uint8_t* packetMask, + uint32_t numFecPackets) { + uint8_t mediaPayloadLength[2]; + const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0; + const uint16_t numMaskBytes = (lBit == 1) ? + kMaskSizeLBitSet : kMaskSizeLBitClear; + const uint16_t ulpHeaderSize = (lBit == 1) ? + kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; + const uint16_t fecRtpOffset = kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize; - // 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; + for (uint32_t i = 0; i < numFecPackets; i++) { + std::list::const_iterator mediaListIt = mediaPacketList.begin(); + uint32_t pktMaskIdx = i * numMaskBytes; + uint32_t mediaPktIdx = 0; + uint16_t fecPacketLength = 0; + while (mediaListIt != mediaPacketList.end()) { + // Each FEC packet has a multiple byte mask. + if (packetMask[pktMaskIdx] & (1 << (7 - mediaPktIdx))) { + Packet* mediaPacket = *mediaListIt; + + // Assign network-ordered media payload length. + ModuleRTPUtility::AssignUWord16ToBuffer( + mediaPayloadLength, + mediaPacket->length - kRtpHeaderSize); + + fecPacketLength = mediaPacket->length + fecRtpOffset; + // On the first protected packet, we don't need to XOR. + if (_generatedFecPackets[i].length == 0) { + // Copy the first 2 bytes of the RTP header. + memcpy(_generatedFecPackets[i].data, mediaPacket->data, 2); + // Copy the 5th to 8th bytes of the RTP header. + memcpy(&_generatedFecPackets[i].data[4], &mediaPacket->data[4], 4); + // Copy network-ordered payload size. + memcpy(&_generatedFecPackets[i].data[8], mediaPayloadLength, 2); + + // Copy RTP payload, leaving room for the ULP header. + memcpy(&_generatedFecPackets[i].data[kFecHeaderSize + ulpHeaderSize], + &mediaPacket->data[kRtpHeaderSize], + mediaPacket->length - kRtpHeaderSize); + } else { + // XOR with the first 2 bytes of the RTP header. + _generatedFecPackets[i].data[0] ^= mediaPacket->data[0]; + _generatedFecPackets[i].data[1] ^= mediaPacket->data[1]; + + // XOR with the 5th to 8th bytes of the RTP header. + for (uint32_t j = 4; j < 8; j++) { + _generatedFecPackets[i].data[j] ^= mediaPacket->data[j]; + } + + // XOR with the network-ordered payload size. + _generatedFecPackets[i].data[8] ^= mediaPayloadLength[0]; + _generatedFecPackets[i].data[9] ^= mediaPayloadLength[1]; + + // XOR with RTP payload, leaving room for the ULP header. + for (int32_t j = kFecHeaderSize + ulpHeaderSize; + j < fecPacketLength; j++) { + _generatedFecPackets[i].data[j] ^= + mediaPacket->data[j - fecRtpOffset]; + } + } + if (fecPacketLength > _generatedFecPackets[i].length) { + _generatedFecPackets[i].length = fecPacketLength; + } + } + mediaListIt++; + mediaPktIdx++; + if (mediaPktIdx == 8) { + // Switch to the next mask byte. + mediaPktIdx = 0; + pktMaskIdx++; + } } - - ListItem* packetListItem = NULL; - ListItem* protectedPacketListItem = NULL; - FecPacket* fecPacket = NULL; - RecoveredPacket* recPacket = NULL; - if (frameComplete) - { - // We have a new frame. - _seqNumBase = 0; - _lastMediaPacketReceived = false; - _fecPacketReceived = false; - - // Free the memory for any existing recovered packets, if the user hasn't. - if (!recoveredPacketList.Empty()) - { - packetListItem = recoveredPacketList.First(); - while (packetListItem != NULL) - { - recPacket = static_cast(packetListItem->GetItem()); - delete recPacket->pkt; - delete recPacket; - recPacket = NULL; - packetListItem = recoveredPacketList.Next(packetListItem); - recoveredPacketList.PopFront(); - } - assert(recoveredPacketList.Empty()); - } - - // Free the FEC packet list. - packetListItem = _fecPacketList.First(); - while (packetListItem != NULL) - { - fecPacket = static_cast(packetListItem->GetItem()); - protectedPacketListItem = fecPacket->protectedPktList.First(); - while (protectedPacketListItem != NULL) - { - delete static_cast(protectedPacketListItem->GetItem()); - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - fecPacket->protectedPktList.PopFront(); - } - assert(fecPacket->protectedPktList.Empty()); - delete fecPacket->pkt; - delete fecPacket; - fecPacket = NULL; - packetListItem = _fecPacketList.Next(packetListItem); - _fecPacketList.PopFront(); - } - assert(_fecPacketList.Empty()); - } - - // -- Insert packets into FEC or recovered list -- - ReceivedPacket* rxPacket = NULL; - RecoveredPacket* recPacketToInsert = NULL; - ProtectedPacket* protectedPacket = NULL; - ListItem* recPacketListItem = NULL; - ListItem* fecPacketListItem = NULL; - packetListItem = receivedPacketList.First(); - while (packetListItem != NULL) - { - rxPacket = static_cast(packetListItem->GetItem()); - - if (!rxPacket->isFec) // Media packet - { - // Insert into recovered packet list. This is essentially an insertion - // sort, with duplicate removal. We search from the back of the list as we - // expect packets to arrive in order. - 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; - recPacketListItem = recoveredPacketList.Last(); - ListItem* nextItem = NULL; - while (recPacketListItem != NULL) - { - recPacket = static_cast(recPacketListItem->GetItem()); - if (rxPacket->seqNum == recPacket->seqNum) - { - // Duplicate packet, no need to add to list. - duplicatePacket = true; - break; - } - else if ((rxPacket->seqNum < recPacket->seqNum || - rxPacket->seqNum > recPacket->seqNum + 48) && // Wrap guard. - rxPacket->seqNum > recPacket->seqNum - 48) // - { - nextItem = recPacketListItem; - recPacketListItem = recoveredPacketList.Previous(recPacketListItem); - } - else - { - // Found the correct position. - break; - } - } - - if (duplicatePacket) - { - // Delete duplicate media packet data. - delete rxPacket->pkt; - }else - { - recPacketToInsert = new RecoveredPacket; - recPacketToInsert->wasRecovered = false; - recPacketToInsert->seqNum = rxPacket->seqNum; - recPacketToInsert->pkt = rxPacket->pkt; - recPacketToInsert->pkt->length = rxPacket->pkt->length; - - if (nextItem == NULL) - { - // Insert at the back. - recoveredPacketList.PushBack(recPacketToInsert); - }else - { - // Insert in sorted position. - recoveredPacketList.InsertBefore(nextItem, new ListItem(recPacketToInsert)); - } - } - }else // FEC packet - { - _fecPacketReceived = true; - WebRtc_UWord8 packetMask; - - // Check for duplicate. - bool duplicatePacket = false; - fecPacketListItem = _fecPacketList.First(); - while (fecPacketListItem != NULL) - { - fecPacket = static_cast(fecPacketListItem->GetItem()); - if (rxPacket->seqNum == fecPacket->seqNum) - { - duplicatePacket = true; - break; - } - fecPacketListItem = _fecPacketList.Next(fecPacketListItem); - } - if (duplicatePacket) - { - // Delete duplicate FEC packet data. - delete rxPacket->pkt; - rxPacket->pkt = NULL; - - }else - { - 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(&fecPacket->pkt->data[2]); - const WebRtc_UWord16 maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ? - kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set? - - for (WebRtc_UWord16 byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++) - { - packetMask = fecPacket->pkt->data[12 + byteIdx]; - for (WebRtc_UWord16 bitIdx = 0; bitIdx < 8; bitIdx++) - { - if (packetMask & (1 << (7 - bitIdx))) - { - protectedPacket = new ProtectedPacket; - fecPacket->protectedPktList.PushBack(protectedPacket); - // This wraps naturally with the sequence number. - protectedPacket->seqNum = static_cast - (_seqNumBase + (byteIdx << 3) + bitIdx); - protectedPacket->pkt = NULL; - } - } - } - - if (fecPacket->protectedPktList.Empty()) - { - // All-zero packet mask; we can discard this FEC packet. - delete fecPacket->pkt; - delete fecPacket; - fecPacket = NULL; - } - else - { - _fecPacketList.PushBack(fecPacket); - } - } - } - - packetListItem = receivedPacketList.Next(packetListItem); - - // Delete the received packet "wrapper", but not the packet data. - delete rxPacket; - rxPacket = NULL; - receivedPacketList.PopFront(); - } - assert(receivedPacketList.Empty()); - - // -- Attempt to recover packets -- - WebRtc_UWord16 protectedPacketsFound; - WebRtc_UWord8 mediaPayloadLength[2]; - WebRtc_UWord8 protectionLength[2]; - fecPacketListItem = _fecPacketList.First(); - ListItem* fecPacketListItemToDiscard = NULL; - while (fecPacketListItem != NULL) - { - // Search for each FEC packet's protected media packets. - fecPacket = static_cast(fecPacketListItem->GetItem()); - protectedPacketListItem = fecPacket->protectedPktList.First(); - recPacketListItem = recoveredPacketList.First(); - protectedPacketsFound = 0; - fecPacketListItemToDiscard = NULL; - while (protectedPacketListItem != NULL) - { - protectedPacket = - static_cast(protectedPacketListItem->GetItem()); - - if (protectedPacket->pkt != NULL) - { - // We already have the required packet. - protectedPacketsFound++; - } - else - { - // Search for the required packet. - while (recPacketListItem != NULL) - { - recPacket = - static_cast(recPacketListItem->GetItem()); - recPacketListItem = recoveredPacketList.Next(recPacketListItem); - 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) - { - recPacketListItem = recoveredPacketList.First(); - } - } - - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - } - - if (protectedPacketsFound == fecPacket->protectedPktList.GetSize() - 1) - { - // Recovery possible. - WebRtc_UWord8 lengthRecovery[2]; - const WebRtc_UWord16 ulpHeaderSize = fecPacket->pkt->data[0] & 0x40 ? - kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set? - - RecoveredPacket* recPacketToInsert = new RecoveredPacket; - recPacketToInsert->wasRecovered = true; - recPacketToInsert->pkt = new Packet; - memset(recPacketToInsert->pkt->data, 0, IP_PACKET_SIZE); - - // 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)); - - protectedPacketListItem = fecPacket->protectedPktList.First(); - while (protectedPacketListItem != NULL) - { - protectedPacket = - static_cast(protectedPacketListItem->GetItem()); - - if (protectedPacket->pkt == NULL) - { - // This is the packet we're recovering. - recPacketToInsert->seqNum = protectedPacket->seqNum; - } - else - { - // XOR with the first 2 bytes of the RTP header. - for (WebRtc_UWord32 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 (WebRtc_UWord32 i = 4; i < 8; i++) - { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } - - // XOR with the network-ordered payload size. - 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 (WebRtc_Word32 i = kRtpHeaderSize; i < protectedPacket->pkt->length; - i++) - { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } - } - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - } - - // 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; - - // Insert into recovered list in correct position. - recPacketListItem = recoveredPacketList.Last(); - ListItem* nextItem = NULL; - while (recPacketListItem != NULL) - { - recPacket = static_cast(recPacketListItem->GetItem()); - if ((recPacketToInsert->seqNum < recPacket->seqNum || - recPacketToInsert->seqNum > recPacket->seqNum + 48) && // Wrap guard. - recPacketToInsert->seqNum > recPacket->seqNum - 48) // - { - nextItem = recPacketListItem; - recPacketListItem = recoveredPacketList.Previous(recPacketListItem); - } - else - { - // Found the correct position. - break; - } - } - - if (nextItem == NULL) - { - // Insert at the back. - recoveredPacketList.PushBack(recPacketToInsert); - } - else - { - // Insert in sorted position. - recoveredPacketList.InsertBefore(nextItem, - new ListItem(recPacketToInsert)); - } - - protectedPacketsFound++; - assert(protectedPacketsFound == fecPacket->protectedPktList.GetSize()); - fecPacketListItemToDiscard = fecPacketListItem; - } - - if (fecPacketListItemToDiscard != NULL) - { - // A packet has been recovered. We need to check the FEC list again, as this - // may allow additional packets to be recovered. - fecPacketListItem = _fecPacketList.First(); - if (fecPacketListItem == fecPacketListItemToDiscard) - { - // If we're deleting the first item, we need to get the next first. - fecPacketListItem = _fecPacketList.Next(fecPacketListItem); - } - } - else - { - // Store this in case a discard is required. - fecPacketListItemToDiscard = fecPacketListItem; - fecPacketListItem = _fecPacketList.Next(fecPacketListItem); - } - - if (protectedPacketsFound == fecPacket->protectedPktList.GetSize()) - { - // Either all protected packets arrived or have been recovered. - // We can discard this FEC packet. - protectedPacketListItem = fecPacket->protectedPktList.First(); - while (protectedPacketListItem != NULL) - { - delete static_cast(protectedPacketListItem->GetItem()); - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - fecPacket->protectedPktList.PopFront(); - } - assert(fecPacket->protectedPktList.Empty()); - delete fecPacket->pkt; - delete fecPacket; - fecPacket = NULL; - assert(fecPacketListItemToDiscard != NULL); - _fecPacketList.Erase(fecPacketListItemToDiscard); - } - } - - // Check if we have a complete frame. - frameComplete = false; - - if (_lastMediaPacketReceived) - { - 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. - WebRtc_UWord16 seqNumIdx = 0; - frameComplete = true; - recPacketListItem = recoveredPacketList.First(); - while (recPacketListItem != NULL && frameComplete == true) - { - recPacket = static_cast(recPacketListItem->GetItem()); - recPacketListItem = recoveredPacketList.Next(recPacketListItem); - if (recPacket->seqNum != - static_cast(_seqNumBase + seqNumIdx)) // Wraps naturally. - { - frameComplete = false; - } - seqNumIdx++; - } - } - - return 0; + assert(_generatedFecPackets[i].length); + //Note: This shouldn't happen: means packet mask is wrong or poorly designed + } } -WebRtc_UWord16 -ForwardErrorCorrection::PacketOverhead() -{ - return kFecHeaderSize + kUlpHeaderSizeLBitSet; +void ForwardErrorCorrection::GenerateFecUlpHeaders( + const std::list& mediaPacketList, + uint8_t* packetMask, + uint32_t numFecPackets) { + // -- Generate FEC and ULP headers -- + // + // FEC Header, 10 bytes + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |E|L|P|X| CC |M| PT recovery | SN base | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | TS recovery | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | length recovery | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // ULP Header, 4 bytes (for L = 0) + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Protection Length | mask | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | mask cont. (present only when L = 1) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + std::list::const_iterator mediaListIt = mediaPacketList.begin(); + Packet* mediaPacket = *mediaListIt; + assert(mediaPacket != NULL); + const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0; + const uint16_t numMaskBytes = (lBit == 1)? + kMaskSizeLBitSet : kMaskSizeLBitClear; + const uint16_t ulpHeaderSize = (lBit == 1)? + kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; + + for (uint32_t i = 0; i < numFecPackets; i++) { + // -- FEC header -- + _generatedFecPackets[i].data[0] &= 0x7f; // Set E to zero. + if (lBit == 0) { + _generatedFecPackets[i].data[0] &= 0xbf; // Clear the L bit. + } else { + _generatedFecPackets[i].data[0] |= 0x40; // Set the L bit. + } + // Two byte sequence number from first RTP packet to SN base. + // We use the same sequence number base for every FEC packet, + // but that's not required in general. + memcpy(&_generatedFecPackets[i].data[2], &mediaPacket->data[2], 2); + + // -- ULP header -- + // Copy the payload size to the protection length field. + // (We protect the entire packet.) + ModuleRTPUtility::AssignUWord16ToBuffer(&_generatedFecPackets[i].data[10], + _generatedFecPackets[i].length - kFecHeaderSize - ulpHeaderSize); + + // Copy the packet mask. + memcpy(&_generatedFecPackets[i].data[12], &packetMask[i * numMaskBytes], + numMaskBytes); + } +} + +void ForwardErrorCorrection::ResetState( + std::list* recoveredPacketList) { + _seqNumBase = 0; + _lastMediaPacketReceived = false; + _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; + recoveredPacketList->pop_front(); + } + assert(recoveredPacketList->empty()); + + // Free the FEC packet list. + while (!_fecPacketList.empty()) { + std::list::iterator fecPacketListIt = _fecPacketList.begin(); + FecPacket* fecPacket = *fecPacketListIt; + std::list::iterator protectedPacketListIt; + 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.pop_front(); + } + assert(_fecPacketList.empty()); +} + +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->begin(); + + while (recoveredPacketListIt != recoveredPacketList->end()) { + RecoveredPacket* recPacket = *recoveredPacketListIt; + if (rxPacket->seqNum == recPacket->seqNum) { + // Duplicate packet, no need to add to list. + duplicatePacket = true; + break; + } + recoveredPacketListIt++; + } + if (duplicatePacket) { + // Delete duplicate media packet data. + delete rxPacket->pkt; + return; + } + RecoveredPacket* recoverdPacketToInsert = new RecoveredPacket; + recoverdPacketToInsert->wasRecovered = false; + recoverdPacketToInsert->seqNum = rxPacket->seqNum; + recoverdPacketToInsert->pkt = rxPacket->pkt; + recoverdPacketToInsert->pkt->length = rxPacket->pkt->length; + + recoveredPacketList->push_back(recoverdPacketToInsert); +} + +void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) { + _fecPacketReceived = true; + + // Check for duplicate. + bool duplicatePacket = false; + std::list::iterator fecPacketListIt = _fecPacketList.begin(); + while (fecPacketListIt != _fecPacketList.end()) { + FecPacket* fecPacket = *fecPacketListIt; + if (rxPacket->seqNum == fecPacket->seqNum) { + duplicatePacket = true; + break; + } + 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( + &fecPacket->pkt->data[2]); + + const uint16_t maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ? + kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set? + + for (uint16_t byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++) { + uint8_t packetMask = fecPacket->pkt->data[12 + byteIdx]; + for (uint16_t bitIdx = 0; bitIdx < 8; bitIdx++) { + if (packetMask & (1 << (7 - bitIdx))) { + ProtectedPacket* protectedPacket = new ProtectedPacket; + fecPacket->protectedPktList.push_back(protectedPacket); + // This wraps naturally with the sequence number. + protectedPacket->seqNum = static_cast(_seqNumBase + + (byteIdx << 3) + bitIdx); + protectedPacket->pkt = NULL; + } + } + } + if (fecPacket->protectedPktList.empty()) { + // All-zero packet mask; we can discard this FEC packet. + delete fecPacket->pkt; + delete fecPacket; + } else { + _fecPacketList.push_back(fecPacket); + } +} + +void ForwardErrorCorrection::InsertPackets( + std::list* receivedPacketList, + std::list* recoveredPacketList) { + + while (!receivedPacketList->empty()) { + ReceivedPacket* rxPacket = receivedPacketList->front(); + + if (rxPacket->isFec) { + InsertFECPacket(rxPacket); + } else { + // Insert packet in end of list. + InsertMediaPacket(rxPacket, recoveredPacketList); + } + // Delete the received packet "wrapper", but not the packet data. + delete rxPacket; + receivedPacketList->pop_front(); + } + assert(receivedPacketList->empty()); + // Sort our recovered packet list. + recoveredPacketList->sort(CompareRecoveredPackets); +} + +void ForwardErrorCorrection::RecoverPacket( + 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) { + // This is the packet we're recovering. + recPacketToInsert->seqNum = protectedPacket->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]; + } + } + protectedPacketListIt++; + } + // 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; +} + +void ForwardErrorCorrection::AttemptRecover( + std::list* recoveredPacketList) { + std::list::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); + + if (protectedPacketsFound == fecPacket->protectedPktList.size() - 1) { + // Recovery possible. + RecoveredPacket* packetToInsert = new RecoveredPacket; + RecoverPacket(*fecPacket, packetToInsert); + + // Add recovered packet in back of list. + recoveredPacketList->push_back(packetToInsert); + + // Sort our recovered packet list. + recoveredPacketList->sort(CompareRecoveredPackets); + + protectedPacketsFound++; + assert(protectedPacketsFound == fecPacket->protectedPktList.size()); + + // 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 { + 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; + } + recPacketListIt++; + seqNumIdx++; + } + } + return 0; +} + +uint16_t ForwardErrorCorrection::PacketOverhead() { + return kFecHeaderSize + kUlpHeaderSizeLBitSet; } } // 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 cb48223c22..361229758f 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.h +++ b/src/modules/rtp_rtcp/source/forward_error_correction.h @@ -11,178 +11,222 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_ +#include +#include + #include "typedefs.h" #include "rtp_rtcp_defines.h" -#include "list_wrapper.h" - namespace webrtc { + +// Forward declaration. +struct FecPacket; + /** * Performs codec-independent forward error correction (FEC), based on RFC 5109. * Option exists to enable unequal protection (UEP) across packets. * This is not to be confused with protection within packets * (referred to as uneven level protection (ULP) in RFC 5109). */ -class ForwardErrorCorrection -{ -public: +class ForwardErrorCorrection { + public: + // Maximum number of media packets we can protect + static const int kMaxMediaPackets = 48; - // 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. */ + }; - /** - * The ListWrapper parameters of #GenerateFEC() should reference structs of this type. - */ - struct Packet - { - WebRtc_UWord16 length; /**> Length of packet in bytes. */ - WebRtc_UWord8 data[IP_PACKET_SIZE]; /**> Packet data. */ - }; + /** + * The received list parameter of #DecodeFEC() must reference structs of this + * type. The lastMediaPktInFrame is not required to be used for correct + * recovery, but will reduce delay by allowing #DecodeFEC() to pre-emptively + * determine frame completion. If set, we assume a FEC stream, and the + * following assumptions must hold:\n + * + * 1. The media packets in a frame have contiguous sequence numbers, i.e. the + * frame's FEC packets have sequence numbers either lower than the first + * media packet or higher than the last media packet.\n + * 2. All FEC packets have a sequence number base equal to the first media + * packet in the corresponding frame.\n + * + * The ssrc member is needed to ensure we can restore the SSRC field of + * recovered packets. In most situations this could be retrieved from other + * 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. */ + }; - /** - * The received list parameter of #DecodeFEC() must reference structs of this type. - * The lastMediaPktInFrame is not required to be used for correct recovery, but will - * reduce delay by allowing #DecodeFEC() to pre-emptively determine frame completion. - * If set, we assume a FEC stream, and the following assumptions must hold:\n - * - * 1. The media packets in a frame have contiguous sequence numbers, i.e. the frame's - * FEC packets have sequence numbers either lower than the first media packet or - * higher than the last media packet.\n - * 2. All FEC packets have a sequence number base equal to the first media packet in - * the corresponding frame.\n - * - * The ssrc member is needed to ensure we can restore the SSRC field of recovered - * packets. In most situations this could be retrieved from other 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 - { - WebRtc_UWord16 seqNum; /**> Sequence number of packet. */ - WebRtc_UWord32 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. */ - }; + /** + * 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. */ + }; - /** - * 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. */ - WebRtc_UWord16 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. */ - }; + /** + * \param[in] id Module ID + */ + ForwardErrorCorrection(int32_t id); - /** - * Constructor. - * - * \param[in] id Module ID - */ - ForwardErrorCorrection(WebRtc_Word32 id); + virtual ~ForwardErrorCorrection(); - /** - * Destructor. Before freeing an instance of the class, #DecodeFEC() must be called - * in a particular fashion to free outstanding memory. Refer to #DecodeFEC(). - */ - virtual ~ForwardErrorCorrection(); + /** + * Generates a list of FEC packets from supplied media packets. + * + * \param[in] mediaPacketList List of media packets to protect, of type + * #Packet. All packets must belong to the + * same frame and the list must not be empty. + * \param[in] protectionFactor FEC protection overhead in the [0, 255] + * domain. To obtain 100% overhead, or an + * equal number of FEC packets as media + * packets, use 255. + * \param[in] numImportantPackets The number of "important" packets in the + * frame. These packets may receive greater + * protection than the remaining packets. The + * important packets must be located at the + * start of the media packet list. For codecs + * with data partitioning, the important + * packets may correspond to first partition + * packets. + * \param[in] useUnequalProtection Parameter to enable/disable unequal + * protection (UEP) across packets. Enabling + * UEP will allocate more protection to the + * numImportantPackets from the start of the + * mediaPacketList. + * \param[out] fecPacketList List of FEC packets, of type #Packet. Must + * be empty on entry. The memory available + * through the list will be valid until the + * next call to GenerateFEC(). + * + * \return 0 on success, -1 on failure. + */ + int32_t GenerateFEC(const std::list& mediaPacketList, + uint8_t protectionFactor, + int numImportantPackets, + bool useUnequalProtection, + std::list* fecPacketList); - /** - * Generates a list of FEC packets from supplied media packets. - * - * \param[in] mediaPacketList List of media packets to protect, of type #Packet. - * All packets must belong to the same frame and the - * list must not be empty. - * \param[in] protectionFactor FEC protection overhead in the [0, 255] domain. To - * obtain 100% overhead, or an equal number of FEC - * packets as media packets, use 255. - * \param[in] numImportantPackets The number of "important" packets in the frame. - * These packets may receive greater protection than - * the remaining packets. The important packets must - * be located at the start of the media packet list. - * For codecs with data partitioning, the important - * packets may correspond to first partition packets. - * \param[in] useUnequalProtection Parameter to enable/disable unequal protection - * (UEP) across packets. Enabling UEP will allocate more - * protection to the numImportantPackets from - * the start of the mediaPacketList. - * \param[out] fecPacketList List of FEC packets, of type #Packet. Must be empty - * on entry. The memory available through the list - * will be valid until the next call to GenerateFEC(). - * - * \return 0 on success, -1 on failure. - */ - WebRtc_Word32 GenerateFEC(const ListWrapper& mediaPacketList, - WebRtc_UWord8 protectionFactor, - int numImportantPackets, - bool useUnequalProtection, - ListWrapper& 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 + * 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 + * + * The user will allocate packets submitted through the received list. The + * function will handle allocation of recovered packets and optionally + * deleting of all packet memory. The user may delete the recovered list + * 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 + * frame. At output the list will be empty, + * with packets either stored internally, + * or accessible through the recovered list. + * \param[out] recoveredPacketList List of recovered media packets, of type + * #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. + * + * \return 0 on success, -1 on failure. + */ + int32_t DecodeFEC(std::list* receivedPacketList, + std::list* recoveredPacketList, + uint16_t lastFECSeqNum, + bool& frameComplete); + /** + * Gets the size in bytes of the FEC/ULP headers, which must be accounted for + * as packet overhead. + * \return Packet overhead in bytes. + */ + static uint16_t PacketOverhead(); - /** - * 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 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 - * - * The user will allocate packets submitted through the received list. The function - * will handle allocation of recovered packets and optionally deleting of all packet - * memory. The user may delete the recovered list 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 frame. At - * output the list will be empty, with packets - * either stored internally, or accessible through - * the recovered list. - * \param[out] recoveredPacketList List of recovered media packets, of type - * #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. - * - * \return 0 on success, -1 on failure. - */ - WebRtc_Word32 DecodeFEC(ListWrapper& receivedPacketList, - ListWrapper& recoveredPacketList, - WebRtc_UWord16 lastFECSeqNum, - bool& frameComplete); - /** - * Gets the size in bytes of the FEC/ULP headers, which must be accounted for as - * packet overhead. - * \return Packet overhead in bytes. - */ - static WebRtc_UWord16 PacketOverhead(); + private: + // True if first is <= than second. + static bool CompareRecoveredPackets(RecoveredPacket* first, + RecoveredPacket* second); -private: - WebRtc_Word32 _id; - Packet* _generatedFecPackets; - ListWrapper _fecPacketList; - WebRtc_UWord16 _seqNumBase; - bool _lastMediaPacketReceived; - bool _fecPacketReceived; + 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); + + // Insert received packets into FEC or recovered list. + void InsertPackets(std::list* receivedPacketList, + std::list* recoveredPacketList); + + // Insert media packet into recovered packet list. We delete duplicates. + void InsertMediaPacket(ReceivedPacket* rxPacket, + std::list* recoveredPacketList); + + // Insert packet into FEC list. We delete duplicates. + void InsertFECPacket(ReceivedPacket* rxPacket); + + // Insert into recovered list in correct position. + void InsertRecoveredPacket( + RecoveredPacket* recPacketToInsert, + std::list* recoveredPacketList); + + // Attempt to recover missing packets. + void AttemptRecover(std::list* recoveredPacketList); + + // Recover a missing packet. + void RecoverPacket(const FecPacket& fecPacket, + RecoveredPacket* recPacketToInsert); + + // Get number of protected packet in the fecPacket. + uint32_t NumberOfProtectedPackets( + const FecPacket& fecPacket, + std::list* recoveredPacketList); + + int32_t _id; + std::vector _generatedFecPackets; + std::list _fecPacketList; + uint16_t _seqNumBase; + bool _lastMediaPacketReceived; + bool _fecPacketReceived; }; } // namespace webrtc - #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_ diff --git a/src/modules/rtp_rtcp/source/receiver_fec.cc b/src/modules/rtp_rtcp/source/receiver_fec.cc index 63c2821110..34e127f88f 100644 --- a/src/modules/rtp_rtcp/source/receiver_fec.cc +++ b/src/modules/rtp_rtcp/source/receiver_fec.cc @@ -16,42 +16,35 @@ // RFC 5109 namespace webrtc { -ReceiverFEC::ReceiverFEC(const WebRtc_Word32 id, RTPReceiverVideo* owner) : - _owner(owner), - _fec(new ForwardErrorCorrection(id)), - _payloadTypeFEC(-1), - _lastFECSeqNum(0), - _frameComplete(true) -{ +ReceiverFEC::ReceiverFEC(const WebRtc_Word32 id, RTPReceiverVideo* owner) + : _owner(owner), + _fec(new ForwardErrorCorrection(id)), + _payloadTypeFEC(-1), + _lastFECSeqNum(0), + _frameComplete(true) { } -ReceiverFEC::~ReceiverFEC() -{ - // Clean up DecodeFEC() - while (_receivedPacketList.First() != NULL) - { - ForwardErrorCorrection::ReceivedPacket* receivedPacket = - static_cast( - _receivedPacketList.First()->GetItem()); - delete receivedPacket->pkt; - delete receivedPacket; - receivedPacket = NULL; - _receivedPacketList.PopFront(); - } - assert(_receivedPacketList.Empty()); +ReceiverFEC::~ReceiverFEC() { + // Clean up DecodeFEC() + 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); - delete _fec; - } + if (_fec != NULL) { + bool frameComplete = true; + _fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList,_lastFECSeqNum, + frameComplete); + delete _fec; + } } -void -ReceiverFEC::SetPayloadTypeFEC(const WebRtc_Word8 payloadType) -{ - _payloadTypeFEC = payloadType; +void ReceiverFEC::SetPayloadTypeFEC(const WebRtc_Word8 payloadType) { + _payloadTypeFEC = payloadType; } /* @@ -84,279 +77,242 @@ RFC 2198 RTP Payload for Redundant Audio Data September 1997 block excluding header. */ -WebRtc_Word32 -ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 payloadDataLength, - bool& FECpacket, - bool oldPacket) -{ - if (_payloadTypeFEC == -1) - { - return -1; +WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket( + const WebRtcRTPHeader* rtpHeader, + const WebRtc_UWord8* incomingRtpPacket, + const WebRtc_UWord16 payloadDataLength, + bool& FECpacket, + bool oldPacket) { + if (_payloadTypeFEC == -1) { + return -1; + } + + WebRtc_UWord8 REDHeaderLength = 1; + + // Add to list without RED header, aka a virtual RTP packet + // we remove the RED header + + ForwardErrorCorrection::ReceivedPacket* receivedPacket = + new ForwardErrorCorrection::ReceivedPacket; + receivedPacket->pkt = new ForwardErrorCorrection::Packet; + + // 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) { + 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) return 0; + } else { + receivedPacket->isFec = false; + FECpacket = false; + } + receivedPacket->lastMediaPktInFrame = rtpHeader->header.markerBit; + receivedPacket->seqNum = rtpHeader->header.sequenceNumber; + + WebRtc_UWord16 blockLength = 0; + if(incomingRtpPacket[rtpHeader->header.headerLength] & 0x80) { + // f bit set in RED header + REDHeaderLength = 4; + WebRtc_UWord16 timestampOffset = + (incomingRtpPacket[rtpHeader->header.headerLength + 1]) << 8; + timestampOffset += incomingRtpPacket[rtpHeader->header.headerLength+2]; + timestampOffset = timestampOffset >> 2; + if(timestampOffset != 0) { + // sanity timestampOffset must be 0 + assert(false); + return -1; + } + blockLength = + (0x03 & incomingRtpPacket[rtpHeader->header.headerLength + 2]) << 8; + blockLength += (incomingRtpPacket[rtpHeader->header.headerLength + 3]); + + // check next RED header + if(incomingRtpPacket[rtpHeader->header.headerLength+4] & 0x80) { + // more than 2 blocks in packet not supported + assert(false); + return -1; + } + if(blockLength > payloadDataLength - REDHeaderLength) { + // block length longer than packet + assert(false); + return -1; + } + } + + ForwardErrorCorrection::ReceivedPacket* secondReceivedPacket = NULL; + if (blockLength > 0) { + // handle block length, split into 2 packets + REDHeaderLength = 5; + + // copy the RTP header + memcpy(receivedPacket->pkt->data, + incomingRtpPacket, + rtpHeader->header.headerLength); + + // replace the RED payload type + receivedPacket->pkt->data[1] &= 0x80; // reset the payload + receivedPacket->pkt->data[1] += payloadType; // set the media payload type + + // copy the payload data + memcpy(receivedPacket->pkt->data + rtpHeader->header.headerLength, + incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, + blockLength); + + receivedPacket->pkt->length = blockLength; + + secondReceivedPacket = new ForwardErrorCorrection::ReceivedPacket; + secondReceivedPacket->pkt = new ForwardErrorCorrection::Packet; + + secondReceivedPacket->isFec = true; + secondReceivedPacket->lastMediaPktInFrame = false; + secondReceivedPacket->seqNum = rtpHeader->header.sequenceNumber; + + // copy the FEC payload data + memcpy(secondReceivedPacket->pkt->data, + incomingRtpPacket + rtpHeader->header.headerLength + + REDHeaderLength + blockLength, + payloadDataLength - REDHeaderLength - blockLength); + + secondReceivedPacket->pkt->length = payloadDataLength - REDHeaderLength - + blockLength; + + } else if(receivedPacket->isFec) { + // everything behind the RED header + memcpy(receivedPacket->pkt->data, + incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, + payloadDataLength - REDHeaderLength); + receivedPacket->pkt->length = payloadDataLength - REDHeaderLength; + receivedPacket->ssrc = + ModuleRTPUtility::BufferToUWord32(&incomingRtpPacket[8]); + + } else { + // copy the RTP header + memcpy(receivedPacket->pkt->data, + incomingRtpPacket, + rtpHeader->header.headerLength); + + // replace the RED payload type + receivedPacket->pkt->data[1] &= 0x80; // reset the payload + receivedPacket->pkt->data[1] += payloadType; // set the media payload type + + // copy the media payload data + memcpy(receivedPacket->pkt->data + rtpHeader->header.headerLength, + incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, + payloadDataLength - REDHeaderLength); + + receivedPacket->pkt->length = rtpHeader->header.headerLength + + payloadDataLength - REDHeaderLength; + } + + if(receivedPacket->isFec) { + AddReceivedFECInfo(rtpHeader, NULL, FECpacket); + } + + if(receivedPacket->pkt->length == 0) { + 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) { + return -1; } - WebRtc_UWord8 REDHeaderLength = 1; + delete receivedPacket->pkt; + delete receivedPacket; + } else { + _receivedPacketList.push_back(receivedPacket); + if (secondReceivedPacket) { + _receivedPacketList.push_back(secondReceivedPacket); + } + } + return 0; +} - // Add to list without RED header, aka a virtual RTP packet - // we remove the RED header - - ForwardErrorCorrection::ReceivedPacket* receivedPacket = new ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = new ForwardErrorCorrection::Packet; +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; + WebRtc_UWord8 payloadType = + incomingRtpPacket[rtpHeader->header.headerLength] & 0x7f; // use the payloadType to decide if it's FEC or coded data - 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) - return 0; - } else - { - receivedPacket->isFec = false; - FECpacket = false; + if(_payloadTypeFEC == payloadType) { + FECpacket = true; + } else { + FECpacket = false; } - receivedPacket->lastMediaPktInFrame = rtpHeader->header.markerBit; - receivedPacket->seqNum = rtpHeader->header.sequenceNumber; - - WebRtc_UWord16 blockLength = 0; - if(incomingRtpPacket[rtpHeader->header.headerLength] & 0x80) - { - // f bit set in RED header - REDHeaderLength = 4; - WebRtc_UWord16 timestampOffset = (incomingRtpPacket[rtpHeader->header.headerLength + 1]) << 8; - timestampOffset += incomingRtpPacket[rtpHeader->header.headerLength+2]; - timestampOffset = timestampOffset >> 2; - if(timestampOffset != 0) - { - // sanity timestampOffset must be 0 - assert(false); - return -1; - } - blockLength = (0x03 & incomingRtpPacket[rtpHeader->header.headerLength + 2]) << 8; - blockLength += (incomingRtpPacket[rtpHeader->header.headerLength + 3]); - - // check next RED header - if(incomingRtpPacket[rtpHeader->header.headerLength+4] & 0x80) - { - // more than 2 blocks in packet not supported - assert(false); - return -1; - } - if(blockLength > payloadDataLength - REDHeaderLength) - { - // block length longer than packet - assert(false); - return -1; - } - } - - ForwardErrorCorrection::ReceivedPacket* secondReceivedPacket = NULL; - if(blockLength > 0) - { - // handle block length, split into 2 packets - REDHeaderLength = 5; - - // copy the RTP header - memcpy(receivedPacket->pkt->data, - incomingRtpPacket, - rtpHeader->header.headerLength); - - // replace the RED payload type - receivedPacket->pkt->data[1] &= 0x80; // reset the payload - receivedPacket->pkt->data[1] += payloadType; // set the media payload type - - // copy the payload data - memcpy(receivedPacket->pkt->data + rtpHeader->header.headerLength, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, - blockLength); - - receivedPacket->pkt->length = blockLength; - - secondReceivedPacket = new ForwardErrorCorrection::ReceivedPacket; - secondReceivedPacket->pkt = new ForwardErrorCorrection::Packet; - - secondReceivedPacket->isFec = true; - secondReceivedPacket->lastMediaPktInFrame = false; - secondReceivedPacket->seqNum = rtpHeader->header.sequenceNumber; - - // copy the FEC payload data - memcpy(secondReceivedPacket->pkt->data, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength + - blockLength, payloadDataLength - REDHeaderLength - blockLength); - - secondReceivedPacket->pkt->length = payloadDataLength - REDHeaderLength - - blockLength; - - } else if(receivedPacket->isFec) - { - // everything behind the RED header - memcpy(receivedPacket->pkt->data, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, - payloadDataLength - REDHeaderLength); - receivedPacket->pkt->length = payloadDataLength - REDHeaderLength; - receivedPacket->ssrc = ModuleRTPUtility::BufferToUWord32(&incomingRtpPacket[8]); - - }else - { - // copy the RTP header - memcpy(receivedPacket->pkt->data, - incomingRtpPacket, - rtpHeader->header.headerLength); - - // replace the RED payload type - receivedPacket->pkt->data[1] &= 0x80; // reset the payload - receivedPacket->pkt->data[1] += payloadType; // set the media payload type - - // copy the media payload data - memcpy(receivedPacket->pkt->data + rtpHeader->header.headerLength, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, - payloadDataLength - REDHeaderLength); - - receivedPacket->pkt->length = rtpHeader->header.headerLength + - payloadDataLength - REDHeaderLength; - } - - if(receivedPacket->isFec) - { - AddReceivedFECInfo(rtpHeader, NULL, FECpacket); - } - - if(receivedPacket->pkt->length == 0) - { - 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) { - return -1; - } - - delete receivedPacket->pkt; - delete receivedPacket; - } - - else - { - _receivedPacketList.PushBack(receivedPacket); - if (secondReceivedPacket) - { - _receivedPacketList.PushBack(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 ) //detect wrap around - { - // wrap - _lastFECSeqNum = rtpHeader->header.sequenceNumber; - } else - { - // old seqNum - } - }else - { - // check for a wrap - if(rtpHeader->header.sequenceNumber > 0xff00 && _lastFECSeqNum < 0x0ff ) //detect wrap around - { - // old seqNum - }else - { - _lastFECSeqNum = rtpHeader->header.sequenceNumber; - } +WebRtc_Word32 ReceiverFEC::ProcessReceivedFEC(const bool forceFrameDecode) { + if (!_receivedPacketList.empty()) { + if (_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList, + _lastFECSeqNum, _frameComplete) != 0) { + return -1; } + assert(_receivedPacketList.empty()); + } + if (forceFrameDecode) { + _frameComplete = true; + } + if (_frameComplete) { + while (!_recoveredPacketList.empty()) { + ForwardErrorCorrection::RecoveredPacket* recoveredPacket = + _recoveredPacketList.front(); - 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; - } + if (ParseAndReceivePacket(recoveredPacket->pkt) != 0) { + return -1; + } + delete recoveredPacket->pkt; + delete recoveredPacket; + _recoveredPacketList.pop_front(); } -} - -WebRtc_Word32 -ReceiverFEC::ProcessReceivedFEC(const bool forceFrameDecode) -{ - if (!_receivedPacketList.Empty()) - { - if (_fec->DecodeFEC(_receivedPacketList, - _recoveredPacketList, - _lastFECSeqNum, - _frameComplete) != 0) - { - return -1; - } - assert(_receivedPacketList.Empty()); - } - if (forceFrameDecode) - { - _frameComplete = true; - } - if (_frameComplete) - { - while (_recoveredPacketList.First() != NULL) - { - ForwardErrorCorrection::RecoveredPacket* recoveredPacket = - static_cast(_recoveredPacketList.First()->GetItem()); - - if (ParseAndReceivePacket(recoveredPacket->pkt) != 0) { - return -1; - } - - delete recoveredPacket->pkt; - delete recoveredPacket; - recoveredPacket = NULL; - _recoveredPacketList.PopFront(); - } - assert(_recoveredPacketList.Empty()); - } - - return 0; + assert(_recoveredPacketList.empty()); + } + return 0; } int ReceiverFEC::ParseAndReceivePacket( const ForwardErrorCorrection::Packet* packet) { - WebRtcRTPHeader header; memset(&header, 0, sizeof(header)); ModuleRTPUtility::RTPHeaderParser parser(packet->data, - packet->length); + packet->length); if (!parser.Parse(header)) { return -1; } if (_owner->ReceiveRecoveredPacketCallback( - &header, - &packet->data[header.header.headerLength], - packet->length - header.header.headerLength) != 0) { + &header, + &packet->data[header.header.headerLength], + packet->length - header.header.headerLength) != 0) { return -1; } return 0; diff --git a/src/modules/rtp_rtcp/source/receiver_fec.h b/src/modules/rtp_rtcp/source/receiver_fec.h index e7b34f2e6e..069787ae10 100644 --- a/src/modules/rtp_rtcp/source/receiver_fec.h +++ b/src/modules/rtp_rtcp/source/receiver_fec.h @@ -11,12 +11,13 @@ #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" #include "typedefs.h" -#include "list_wrapper.h" namespace webrtc { class RTPReceiverVideo; @@ -45,8 +46,8 @@ private: int ParseAndReceivePacket(const ForwardErrorCorrection::Packet* packet); RTPReceiverVideo* _owner; ForwardErrorCorrection* _fec; - ListWrapper _receivedPacketList; - ListWrapper _recoveredPacketList; + std::list _receivedPacketList; + std::list _recoveredPacketList; WebRtc_Word8 _payloadTypeFEC; WebRtc_UWord16 _lastFECSeqNum; bool _frameComplete; diff --git a/src/modules/rtp_rtcp/source/rtp_sender_video.cc b/src/modules/rtp_rtcp/source/rtp_sender_video.cc index 51326980b3..dc33e4b5e8 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/src/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -100,34 +100,29 @@ RTPSenderVideo::VideoCodecType() const return _videoType; } -WebRtc_Word32 -RTPSenderVideo::RegisterVideoPayload( - const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 maxBitRate, - ModuleRTPUtility::Payload*& payload) -{ - CriticalSectionScoped cs(_sendVideoCritsect); +WebRtc_Word32 RTPSenderVideo::RegisterVideoPayload( + const char payloadName[RTP_PAYLOAD_NAME_SIZE], + const WebRtc_Word8 payloadType, + const WebRtc_UWord32 maxBitRate, + ModuleRTPUtility::Payload*& payload) { + CriticalSectionScoped cs(_sendVideoCritsect); - RtpVideoCodecTypes videoType = kRtpNoVideo; - if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3)) - { - videoType = kRtpVp8Video; - } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) - { - videoType = kRtpNoVideo; - }else - { - videoType = kRtpNoVideo; - return -1; - } - payload = new ModuleRTPUtility::Payload; - strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE); - payload->typeSpecific.Video.videoCodecType = videoType; - payload->typeSpecific.Video.maxRate = maxBitRate; - payload->audio = false; - - return 0; + RtpVideoCodecTypes videoType = kRtpNoVideo; + if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3)) { + videoType = kRtpVp8Video; + } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) { + videoType = kRtpNoVideo; + } else { + videoType = kRtpNoVideo; + return -1; + } + payload = new ModuleRTPUtility::Payload; + strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1); + payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0; // Null terminate string. + payload->typeSpecific.Video.videoCodecType = videoType; + payload->typeSpecific.Video.maxRate = maxBitRate; + payload->audio = false; + return 0; } struct RtpPacket @@ -156,19 +151,17 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, ptrGenericFEC->pkt->length); // Add packet to FEC list - _rtpPacketListFec.PushBack(ptrGenericFEC); + _rtpPacketListFec.push_back(ptrGenericFEC); // FEC can only protect up to kMaxMediaPackets packets - if (static_cast(_mediaPacketListFec.GetSize()) < + if (static_cast(_mediaPacketListFec.size()) < ForwardErrorCorrection::kMaxMediaPackets) { - _mediaPacketListFec.PushBack(ptrGenericFEC->pkt); + _mediaPacketListFec.push_back(ptrGenericFEC->pkt); } // Last packet in frame if (markerBit) { - // Interface for FEC - ListWrapper fecPacketList; // Retain the RTP header of the last media packet to construct FEC // packet RTP headers. @@ -189,23 +182,22 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, ForwardErrorCorrection::kMaxMediaPackets; } + std::list fecPacketList; retVal = _fec.GenerateFEC(_mediaPacketListFec, _fecProtectionFactor, _numberFirstPartition, _fecUseUepProtection, - fecPacketList); + &fecPacketList); int fecOverheadSent = 0; int videoSent = 0; - while(!_rtpPacketListFec.Empty()) + while(!_rtpPacketListFec.empty()) { WebRtc_UWord8 newDataBuffer[IP_PACKET_SIZE]; memset(newDataBuffer, 0, sizeof(newDataBuffer)); - ListItem* item = _rtpPacketListFec.First(); - RtpPacket* packetToSend = - static_cast(item->GetItem()); + RtpPacket* packetToSend = _rtpPacketListFec.front(); // Copy RTP header memcpy(newDataBuffer, packetToSend->pkt->data, @@ -229,8 +221,8 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, packetToSend->pkt->length - packetToSend->rtpHeaderLength); - _rtpPacketListFec.PopFront(); - _mediaPacketListFec.PopFront(); + _rtpPacketListFec.pop_front(); + _mediaPacketListFec.pop_front(); // Send normal packet with RED header int packetSuccess = _rtpSender.SendToNetwork( @@ -252,19 +244,15 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, delete packetToSend; packetToSend = NULL; } - assert(_mediaPacketListFec.Empty()); - assert(_rtpPacketListFec.Empty()); + assert(_mediaPacketListFec.empty()); + assert(_rtpPacketListFec.empty()); - while(!fecPacketList.Empty()) + while(!fecPacketList.empty()) { WebRtc_UWord8 newDataBuffer[IP_PACKET_SIZE]; - ListItem* item = fecPacketList.First(); - // Build FEC packets - ForwardErrorCorrection::Packet* packetToSend = - static_cast - (item->GetItem()); + ForwardErrorCorrection::Packet* packetToSend = fecPacketList.front(); // The returned FEC packets have no RTP headers. // Copy the last media packet's modified RTP header. @@ -285,7 +273,7 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, packetToSend->data, packetToSend->length); - fecPacketList.PopFront(); + fecPacketList.pop_front(); // Invalid FEC packet assert(packetToSend->length != 0); diff --git a/src/modules/rtp_rtcp/source/rtp_sender_video.h b/src/modules/rtp_rtcp/source/rtp_sender_video.h index 107e05a7bb..2f78d8bc87 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/src/modules/rtp_rtcp/source/rtp_sender_video.h @@ -11,22 +11,24 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_H_ +#include + #include "typedefs.h" #include "common_types.h" // Transport #include "rtp_rtcp_config.h" #include "rtp_rtcp_defines.h" #include "rtp_utility.h" -#include "list_wrapper.h" #include "video_codec_information.h" #include "forward_error_correction.h" #include "Bitrate.h" - #include "rtp_sender.h" namespace webrtc { class CriticalSectionWrapper; +struct RtpPacket; + class RTPSenderVideo { public: @@ -42,10 +44,11 @@ public: WebRtc_UWord16 FECPacketOverhead() const; - WebRtc_Word32 RegisterVideoPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 maxBitRate, - ModuleRTPUtility::Payload*& payload); + WebRtc_Word32 RegisterVideoPayload( + const char payloadName[RTP_PAYLOAD_NAME_SIZE], + const WebRtc_Word8 payloadType, + const WebRtc_UWord32 maxBitRate, + ModuleRTPUtility::Payload*& payload); WebRtc_Word32 SendVideo(const RtpVideoCodecTypes videoType, const FrameType frameType, @@ -133,8 +136,8 @@ private: WebRtc_UWord8 _fecProtectionFactor; bool _fecUseUepProtection; int _numberFirstPartition; - ListWrapper _mediaPacketListFec; - ListWrapper _rtpPacketListFec; + std::list _mediaPacketListFec; + std::list _rtpPacketListFec; // Bitrate used for FEC payload, RED headers, RTP headers for FEC packets // and any padding overhead. Bitrate _fecOverheadRate; diff --git a/src/modules/rtp_rtcp/test/testFec/test_fec.cc b/src/modules/rtp_rtcp/test/testFec/test_fec.cc index 70244b3af3..7cf55100ae 100644 --- a/src/modules/rtp_rtcp/test/testFec/test_fec.cc +++ b/src/modules/rtp_rtcp/test/testFec/test_fec.cc @@ -9,576 +9,499 @@ #include #include #include +#include #include "forward_error_correction.h" #include "forward_error_correction_internal.h" -#include "list_wrapper.h" #include "rtp_utility.h" //#define VERBOSE_OUTPUT -void ReceivePackets(webrtc::ListWrapper& toDecodeList, webrtc::ListWrapper& receivedPacketList, - WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate); +using namespace webrtc; -int main() -{ - enum { kMaxNumberMediaPackets = 48 }; - enum { kMaxNumberFecPackets = 48 }; +void ReceivePackets( + std::list* toDecodeList, + std::list* receivedPacketList, + WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate); - const WebRtc_UWord32 kNumMaskBytesL0 = 2; - const WebRtc_UWord32 kNumMaskBytesL1 = 6; +int main() { + enum { kMaxNumberMediaPackets = 48 }; + enum { kMaxNumberFecPackets = 48 }; - // FOR UEP - const bool kUseUnequalProtection = true; + const WebRtc_UWord32 kNumMaskBytesL0 = 2; + const WebRtc_UWord32 kNumMaskBytesL1 = 6; - WebRtc_UWord32 id = 0; - webrtc::ForwardErrorCorrection fec(id); + // FOR UEP + const bool kUseUnequalProtection = true; - webrtc::ListWrapper mediaPacketList; - webrtc::ListWrapper fecPacketList; - webrtc::ListWrapper toDecodeList; - webrtc::ListWrapper receivedPacketList; - webrtc::ListWrapper recoveredPacketList; - webrtc::ListWrapper fecMaskList; - webrtc::ForwardErrorCorrection::Packet* mediaPacket; - const float lossRate[] = {0, 0.05f, 0.1f, 0.25f, 0.5f, 0.75f, 0.9f}; - const WebRtc_UWord32 lossRateSize = sizeof(lossRate)/sizeof(*lossRate); - const float reorderRate = 0.1f; - const float duplicateRate = 0.1f; + WebRtc_UWord32 id = 0; + ForwardErrorCorrection fec(id); - WebRtc_UWord8 mediaLossMask[kMaxNumberMediaPackets]; - WebRtc_UWord8 fecLossMask[kMaxNumberFecPackets]; - WebRtc_UWord8 fecPacketMasks[kMaxNumberFecPackets][kMaxNumberMediaPackets]; + std::list mediaPacketList; + std::list fecPacketList; + std::list toDecodeList; + std::list receivedPacketList; + std::list recoveredPacketList; + std::list fecMaskList; - // Seed the random number generator, storing the seed to file in order to reproduce - // past results. - const unsigned int randomSeed = static_cast(time(NULL)); - srand(randomSeed); - FILE* randomSeedFile = fopen("randomSeedLog.txt", "a"); - fprintf(randomSeedFile, "%u\n", randomSeed); - fclose(randomSeedFile); - randomSeedFile = NULL; + ForwardErrorCorrection::Packet* mediaPacket; + const float lossRate[] = {0, 0.05f, 0.1f, 0.25f, 0.5f, 0.75f, 0.9f}; + const WebRtc_UWord32 lossRateSize = sizeof(lossRate)/sizeof(*lossRate); + const float reorderRate = 0.1f; + const float duplicateRate = 0.1f; - WebRtc_UWord16 seqNum = static_cast(rand()); - WebRtc_UWord32 timeStamp = static_cast(rand()); - const WebRtc_UWord32 ssrc = static_cast(rand()); + WebRtc_UWord8 mediaLossMask[kMaxNumberMediaPackets]; + WebRtc_UWord8 fecLossMask[kMaxNumberFecPackets]; + WebRtc_UWord8 fecPacketMasks[kMaxNumberFecPackets][kMaxNumberMediaPackets]; - for (WebRtc_UWord32 lossRateIdx = 0; lossRateIdx < lossRateSize; lossRateIdx++) - { - WebRtc_UWord8* packetMask = - new WebRtc_UWord8[kMaxNumberMediaPackets * kNumMaskBytesL1]; + // Seed the random number generator, storing the seed to file in order to + // reproduce past results. + const unsigned int randomSeed = static_cast(time(NULL)); + srand(randomSeed); + FILE* randomSeedFile = fopen("randomSeedLog.txt", "a"); + fprintf(randomSeedFile, "%u\n", randomSeed); + fclose(randomSeedFile); + randomSeedFile = NULL; - printf("Loss rate: %.2f\n", lossRate[lossRateIdx]); - for (WebRtc_UWord32 numMediaPackets = 1; numMediaPackets <= kMaxNumberMediaPackets; - numMediaPackets++) - { + WebRtc_UWord16 seqNum = static_cast(rand()); + WebRtc_UWord32 timeStamp = static_cast(rand()); + const WebRtc_UWord32 ssrc = static_cast(rand()); - for (WebRtc_UWord32 numFecPackets = 1; numFecPackets <= numMediaPackets && - numFecPackets <= kMaxNumberFecPackets; numFecPackets++) + for (WebRtc_UWord32 lossRateIdx = 0; lossRateIdx < lossRateSize; + lossRateIdx++) { + WebRtc_UWord8* packetMask = + new WebRtc_UWord8[kMaxNumberMediaPackets * kNumMaskBytesL1]; + + printf("Loss rate: %.2f\n", lossRate[lossRateIdx]); + for (WebRtc_UWord32 numMediaPackets = 1; + numMediaPackets <= kMaxNumberMediaPackets; + numMediaPackets++) { + + for (WebRtc_UWord32 numFecPackets = 1; + numFecPackets <= numMediaPackets && + numFecPackets <= kMaxNumberFecPackets; + numFecPackets++) { + + // loop over all possible numImpPackets + for (WebRtc_UWord32 numImpPackets = 0; + numImpPackets <= numMediaPackets && + numImpPackets <= kMaxNumberMediaPackets; + numImpPackets++) { + + WebRtc_UWord8 protectionFactor = static_cast + (numFecPackets * 255 / numMediaPackets); + + const WebRtc_UWord32 maskBytesPerFecPacket = + (numMediaPackets > 16) ? kNumMaskBytesL1 : kNumMaskBytesL0; + + memset(packetMask, 0, numMediaPackets * maskBytesPerFecPacket); + + // Transfer packet masks from bit-mask to byte-mask. + internal::GeneratePacketMasks(numMediaPackets, + numFecPackets, + numImpPackets, + kUseUnequalProtection, + packetMask); + +#ifdef VERBOSE_OUTPUT + printf("%u media packets, %u FEC packets, %u numImpPackets, " + "loss rate = %.2f \n", + numMediaPackets, numFecPackets, numImpPackets, lossRate[lossRateIdx]); + printf("Packet mask matrix \n"); +#endif + + for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { + for (WebRtc_UWord32 j = 0; j < numMediaPackets; j++) { + const WebRtc_UWord8 byteMask = + packetMask[i * maskBytesPerFecPacket + j / 8]; + const WebRtc_UWord32 bitPosition = (7 - j % 8); + fecPacketMasks[i][j] = + (byteMask & (1 << bitPosition)) >> bitPosition; +#ifdef VERBOSE_OUTPUT + printf("%u ", fecPacketMasks[i][j]); +#endif + } +#ifdef VERBOSE_OUTPUT + printf("\n"); +#endif + } +#ifdef VERBOSE_OUTPUT + printf("\n"); +#endif + // Check for all zero rows or columns: indicates incorrect mask + WebRtc_UWord32 rowLimit = numMediaPackets; + for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { + WebRtc_UWord32 rowSum = 0; + for (WebRtc_UWord32 j = 0; j < rowLimit; j++) { + rowSum += fecPacketMasks[i][j]; + } + if (rowSum == 0) { + printf("ERROR: row is all zero %d \n",i); + return -1; + } + } + for (WebRtc_UWord32 j = 0; j < rowLimit; j++) { + WebRtc_UWord32 columnSum = 0; + for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { + columnSum += fecPacketMasks[i][j]; + } + if (columnSum == 0) { + printf("ERROR: column is all zero %d \n",j); + return -1; + } + } + // Construct media packets. + for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { + mediaPacket = new ForwardErrorCorrection::Packet; + mediaPacketList.push_back(mediaPacket); + mediaPacket->length = + static_cast((static_cast(rand()) / + RAND_MAX) * (IP_PACKET_SIZE - 12 - 28 - + ForwardErrorCorrection::PacketOverhead())); + if (mediaPacket->length < 12) { + mediaPacket->length = 12; + } + // Generate random values for the first 2 bytes + mediaPacket->data[0] = static_cast(rand() % 256); + mediaPacket->data[1] = static_cast(rand() % 256); + + // The first two bits are assumed to be 10 by the + // FEC encoder. In fact the FEC decoder will set the + // two first bits to 10 regardless of what they + // actually were. Set the first two bits to 10 + // so that a memcmp can be performed for the + // whole restored packet. + mediaPacket->data[0] |= 0x80; + mediaPacket->data[0] &= 0xbf; + + // FEC is applied to a whole frame. + // A frame is signaled by multiple packets without + // the marker bit set followed by the last packet of + // the frame for which the marker bit is set. + // Only push one (fake) frame to the FEC. + mediaPacket->data[1] &= 0x7f; + + ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], + seqNum); + ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], + timeStamp); + ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], + ssrc); + // Generate random values for payload + for (WebRtc_Word32 j = 12; j < mediaPacket->length; j++) { + mediaPacket->data[j] = + static_cast (rand() % 256); + } + seqNum++; + } + mediaPacket->data[1] |= 0x80; + + if (fec.GenerateFEC(mediaPacketList, protectionFactor, + numImpPackets, kUseUnequalProtection, + &fecPacketList) != 0) { + printf("Error: GenerateFEC() failed\n"); + return -1; + } + + if (fecPacketList.size() != numFecPackets) { + printf("Error: we requested %u FEC packets, " + "but GenerateFEC() produced %ld\n", + numFecPackets, fecPacketList.size()); + return -1; + } + memset(mediaLossMask, 0, sizeof(mediaLossMask)); + std::list::iterator + mediaPacketListItem = mediaPacketList.begin(); + ForwardErrorCorrection::ReceivedPacket* receivedPacket; + WebRtc_UWord32 mediaPacketIdx = 0; + + while (mediaPacketListItem != mediaPacketList.end()) { + mediaPacket = *mediaPacketListItem; + const float lossRandomVariable = (static_cast(rand()) / + (RAND_MAX)); + + if (lossRandomVariable >= lossRate[lossRateIdx]) { + mediaLossMask[mediaPacketIdx] = 1; + receivedPacket = + new ForwardErrorCorrection::ReceivedPacket; + receivedPacket->pkt = + new ForwardErrorCorrection::Packet; + receivedPacketList.push_back(receivedPacket); - // loop over all possible numImpPackets - for (WebRtc_UWord32 numImpPackets = 0; numImpPackets <= numMediaPackets && - numImpPackets <= kMaxNumberMediaPackets; numImpPackets++) - { + receivedPacket->pkt->length = mediaPacket->length; + memcpy(receivedPacket->pkt->data, mediaPacket->data, + mediaPacket->length); + 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 + fecPacketListItem = fecPacketList.begin(); + ForwardErrorCorrection::Packet* fecPacket; + WebRtc_UWord32 fecPacketIdx = 0; + while (fecPacketListItem != fecPacketList.end()) { + fecPacket = *fecPacketListItem; + const float lossRandomVariable = + (static_cast(rand()) / (RAND_MAX)); + if (lossRandomVariable >= lossRate[lossRateIdx]) { + fecLossMask[fecPacketIdx] = 1; + receivedPacket = + new ForwardErrorCorrection::ReceivedPacket; + receivedPacket->pkt = + new ForwardErrorCorrection::Packet; - WebRtc_UWord8 protectionFactor = static_cast - (numFecPackets * 255 / numMediaPackets); + receivedPacketList.push_back(receivedPacket); - const WebRtc_UWord32 maskBytesPerFecPacket = - (numMediaPackets > 16) ? kNumMaskBytesL1 : - kNumMaskBytesL0; + receivedPacket->pkt->length = fecPacket->length; + memcpy(receivedPacket->pkt->data, fecPacket->data, + fecPacket->length); - memset(packetMask, 0, - numMediaPackets * maskBytesPerFecPacket); + receivedPacket->seqNum = seqNum; + receivedPacket->isFec = true; + receivedPacket->lastMediaPktInFrame = false; + receivedPacket->ssrc = ssrc; - // Transfer packet masks from bit-mask to byte-mask. - webrtc::internal::GeneratePacketMasks(numMediaPackets, - numFecPackets, - numImpPackets, - kUseUnequalProtection, - packetMask); + fecMaskList.push_back(fecPacketMasks[fecPacketIdx]); + } + fecPacketIdx++; + seqNum++; + fecPacketListItem++; + } #ifdef VERBOSE_OUTPUT - printf("%u media packets, %u FEC packets, %u numImpPackets, " - "loss rate = %.2f \n", - numMediaPackets, numFecPackets, numImpPackets, lossRate[lossRateIdx]); - printf("Packet mask matrix \n"); + printf("Media loss mask:\n"); + for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { + printf("%u ", mediaLossMask[i]); + } + printf("\n\n"); + + printf("FEC loss mask:\n"); + for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { + printf("%u ", fecLossMask[i]); + } + printf("\n\n"); #endif - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - for (WebRtc_UWord32 j = 0; j < numMediaPackets; j++) - { - const WebRtc_UWord8 byteMask = - packetMask[i * maskBytesPerFecPacket + j / 8]; - const WebRtc_UWord32 bitPosition = (7 - j % 8); - fecPacketMasks[i][j] = - (byteMask & (1 << bitPosition)) >> bitPosition; + std::list::iterator fecMaskIt = fecMaskList.begin(); + WebRtc_UWord8* fecMask; + while (fecMaskIt != fecMaskList.end()) { + fecMask = *fecMaskIt; + WebRtc_UWord32 hammingDist = 0; + WebRtc_UWord32 recoveryPosition = 0; + for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { + if (mediaLossMask[i] == 0 && fecMask[i] == 1) { + recoveryPosition = i; + hammingDist++; + } + } + std::list::iterator itemToDelete = fecMaskIt; + fecMaskIt++; + + if (hammingDist == 1) { + // Recovery possible. Restart search. + mediaLossMask[recoveryPosition] = 1; + fecMaskIt = fecMaskList.begin(); + } else if (hammingDist == 0) { + // FEC packet cannot provide further recovery. + fecMaskList.erase(itemToDelete); + } + } #ifdef VERBOSE_OUTPUT - printf("%u ", fecPacketMasks[i][j]); + printf("Recovery mask:\n"); + for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { + printf("%u ", mediaLossMask[i]); + } + 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 + ((static_cast(rand()) / RAND_MAX) * + receivedPacketList.size() + 0.5); + if (numPacketsToDecode < 1) { + numPacketsToDecode = 1; + } + ReceivePackets(&toDecodeList, &receivedPacketList, + numPacketsToDecode, reorderRate, duplicateRate); + + if (fecPacketReceived == false) { + std::list::iterator + toDecodeIt = toDecodeList.begin(); + while (toDecodeIt != toDecodeList.end()) { + receivedPacket = *toDecodeIt; + if (receivedPacket->isFec) { + fecPacketReceived = true; + } + toDecodeIt++; + } + } + if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList, seqNum, + complete) != 0) { + printf("Error: DecodeFEC() failed\n"); + return -1; + } + if (!toDecodeList.empty()) { + printf("Error: received packet list is not empty\n"); + return -1; + } + if (recoveredPacketList.size() == numMediaPackets && + fecPacketReceived == true) { + if (complete == true) { #ifdef VERBOSE_OUTPUT - printf("\n"); + printf("Full frame recovery correctly marked\n\n"); #endif - } -#ifdef VERBOSE_OUTPUT - printf("\n"); -#endif - // Check for all zero rows or columns: indicates incorrect mask - WebRtc_UWord32 rowLimit = numMediaPackets; - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - WebRtc_UWord32 rowSum = 0; - for (WebRtc_UWord32 j = 0; j < rowLimit; j++) - { - rowSum += fecPacketMasks[i][j]; - } - if (rowSum == 0) - { - printf("ERROR: row is all zero %d \n",i); - return -1; - } - } - for (WebRtc_UWord32 j = 0; j < rowLimit; j++) - { - WebRtc_UWord32 columnSum = 0; - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - columnSum += fecPacketMasks[i][j]; - } - if (columnSum == 0) - { - printf("ERROR: column is all zero %d \n",j); - return -1; - } - } - - // Construct media packets. - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - mediaPacket = new webrtc::ForwardErrorCorrection::Packet; - mediaPacketList.PushBack(mediaPacket); - mediaPacket->length = - static_cast((static_cast(rand()) / - RAND_MAX) * (IP_PACKET_SIZE - 12 - 28 - - webrtc::ForwardErrorCorrection::PacketOverhead())); - if (mediaPacket->length < 12) - { - mediaPacket->length = 12; - } - - // Generate random values for the first 2 bytes - mediaPacket->data[0] = - static_cast(rand() % 256); - - mediaPacket->data[1] = - static_cast(rand() % 256); - - // The first two bits are assumed to be 10 by the - // FEC encoder. In fact the FEC decoder will set the - // two first bits to 10 regardless of what they - // actually were. Set the first two bits to 10 - // so that a memcmp can be performed for the - // whole restored packet. - mediaPacket->data[0] |= 0x80; - mediaPacket->data[0] &= 0xbf; - - // FEC is applied to a whole frame. - // A frame is signaled by multiple packets without - // the marker bit set followed by the last packet of - // the frame for which the marker bit is set. - // Only push one (fake) frame to the FEC. - mediaPacket->data[1] &= 0x7f; - - webrtc::ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], - seqNum); - webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], - timeStamp); - webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], - ssrc); - // Generate random values for payload - for (WebRtc_Word32 j = 12; j < mediaPacket->length; j++) - { - mediaPacket->data[j] = - static_cast (rand() % 256); - } - seqNum++; - } - mediaPacket->data[1] |= 0x80; - - if (fec.GenerateFEC(mediaPacketList, protectionFactor, numImpPackets, - kUseUnequalProtection, fecPacketList) != 0) - { - printf("Error: GenerateFEC() failed\n"); - return -1; - } - - if (fecPacketList.GetSize() != numFecPackets) - { - printf("Error: we requested %u FEC packets, " - "but GenerateFEC() produced %u\n", - numFecPackets, fecPacketList.GetSize()); - return -1; - } - memset(mediaLossMask, 0, sizeof(mediaLossMask)); - webrtc::ListItem* mediaPacketListItem = mediaPacketList.First(); - webrtc::ForwardErrorCorrection::ReceivedPacket* receivedPacket; - WebRtc_UWord32 mediaPacketIdx = 0; - - while (mediaPacketListItem != NULL) - { - mediaPacket = static_cast - (mediaPacketListItem->GetItem()); - const float lossRandomVariable = (static_cast(rand()) / - (RAND_MAX)); - - if (lossRandomVariable >= lossRate[lossRateIdx]) - { - mediaLossMask[mediaPacketIdx] = 1; - receivedPacket = - new webrtc::ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = - new webrtc::ForwardErrorCorrection::Packet; - receivedPacketList.PushBack(receivedPacket); - - receivedPacket->pkt->length = mediaPacket->length; - memcpy(receivedPacket->pkt->data, mediaPacket->data, - mediaPacket->length); - receivedPacket->seqNum = - webrtc::ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]); - receivedPacket->isFec = false; - receivedPacket->lastMediaPktInFrame = - (mediaPacket->data[1] & 0x80) != 0; - } - mediaPacketIdx++; - mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem); - } - memset(fecLossMask, 0, sizeof(fecLossMask)); - webrtc::ListItem* fecPacketListItem = fecPacketList.First(); - webrtc::ForwardErrorCorrection::Packet* fecPacket; - WebRtc_UWord32 fecPacketIdx = 0; - while (fecPacketListItem != NULL) - { - fecPacket = static_cast - (fecPacketListItem->GetItem()); - const float lossRandomVariable = - (static_cast(rand()) / (RAND_MAX)); - if (lossRandomVariable >= lossRate[lossRateIdx]) - { - fecLossMask[fecPacketIdx] = 1; - receivedPacket = - new webrtc::ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = - new webrtc::ForwardErrorCorrection::Packet; - receivedPacketList.PushBack(receivedPacket); - - receivedPacket->pkt->length = fecPacket->length; - memcpy(receivedPacket->pkt->data, fecPacket->data, - fecPacket->length); - - receivedPacket->seqNum = seqNum; - receivedPacket->isFec = true; - receivedPacket->lastMediaPktInFrame = false; - receivedPacket->ssrc = ssrc; - - fecMaskList.PushBack(fecPacketMasks[fecPacketIdx]); - } - fecPacketIdx++; - seqNum++; - fecPacketListItem = fecPacketList.Next(fecPacketListItem); - } - -#ifdef VERBOSE_OUTPUT - printf("Media loss mask:\n"); - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - printf("%u ", mediaLossMask[i]); - } - printf("\n\n"); - - printf("FEC loss mask:\n"); - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - printf("%u ", fecLossMask[i]); - } - printf("\n\n"); -#endif - - webrtc::ListItem* listItem = fecMaskList.First(); - WebRtc_UWord8* fecMask; - while (listItem != NULL) - { - fecMask = static_cast(listItem->GetItem()); - WebRtc_UWord32 hammingDist = 0; - WebRtc_UWord32 recoveryPosition = 0; - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - if (mediaLossMask[i] == 0 && fecMask[i] == 1) - { - recoveryPosition = i; - hammingDist++; - } - } - webrtc::ListItem* itemToDelete = listItem; - listItem = fecMaskList.Next(listItem); - - if (hammingDist == 1) - { - // Recovery possible. Restart search. - mediaLossMask[recoveryPosition] = 1; - listItem = fecMaskList.First(); - } - else if (hammingDist == 0) - { - // FEC packet cannot provide further recovery. - fecMaskList.Erase(itemToDelete); - } - } -#ifdef VERBOSE_OUTPUT - printf("Recovery mask:\n"); - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - printf("%u ", mediaLossMask[i]); - } - 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 - ((static_cast(rand()) / RAND_MAX) * - receivedPacketList.GetSize() + 0.5); - if (numPacketsToDecode < 1) - { - numPacketsToDecode = 1; - } - - ReceivePackets(toDecodeList, receivedPacketList, numPacketsToDecode, - reorderRate, duplicateRate); - - if (fecPacketReceived == false) - { - listItem = toDecodeList.First(); - while (listItem != NULL) - { - receivedPacket = - static_cast(listItem->GetItem()); - if (receivedPacket->isFec) - { - fecPacketReceived = true; - } - listItem = toDecodeList.Next(listItem); - } - } - - if (fec.DecodeFEC(toDecodeList, recoveredPacketList, seqNum, - complete) != 0) - { - printf("Error: DecodeFEC() failed\n"); - return -1; - } - - if (!toDecodeList.Empty()) - { - printf("Error: received packet list is not empty\n"); - return -1; - } - - if (recoveredPacketList.GetSize() == 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 == true) - { - printf("Error: " - "it should not be possible to verify full frame recovery," - " but complete parameter was set to true\n"); - return -1; - } - } - } - - mediaPacketListItem = mediaPacketList.First(); - mediaPacketIdx = 0; - while (mediaPacketListItem != NULL) - { - if (mediaLossMask[mediaPacketIdx] == 1) - { - // Should have recovered this packet. - webrtc::ListItem* recoveredPacketListItem = - recoveredPacketList.First(); - mediaPacket = - static_cast - (mediaPacketListItem->GetItem()); - - if (recoveredPacketListItem == NULL) - { - printf("Error: insufficient number of recovered packets.\n"); - return -1; - } - - webrtc::ForwardErrorCorrection::RecoveredPacket* recoveredPacket = - static_cast - (recoveredPacketListItem->GetItem()); - - if (recoveredPacket->pkt->length != mediaPacket->length) - { - printf("Error: recovered packet length not identical to " - "original media packet\n"); - return -1; - } - if (memcmp(recoveredPacket->pkt->data, mediaPacket->data, - mediaPacket->length) != 0) - { - printf("Error: recovered packet payload not identical to " - "original media packet\n"); - return -1; - } - delete recoveredPacket->pkt; - delete recoveredPacket; - recoveredPacket = NULL; - recoveredPacketList.PopFront(); - } - mediaPacketIdx++; - mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem); - } - - if (!recoveredPacketList.Empty()) - { - printf("Error: excessive number of recovered packets.\n"); - return -1; - } - // -- Teardown -- - mediaPacketListItem = mediaPacketList.First(); - while (mediaPacketListItem != NULL) - { - delete static_cast - (mediaPacketListItem->GetItem()); - mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem); - mediaPacketList.PopFront(); - } - assert(mediaPacketList.Empty()); - - fecPacketListItem = fecPacketList.First(); - while (fecPacketListItem != NULL) - { - fecPacketListItem = fecPacketList.Next(fecPacketListItem); - fecPacketList.PopFront(); - } - - // Delete received packets we didn't pass to DecodeFEC(), due to early - // frame completion. - listItem = receivedPacketList.First(); - while (listItem != NULL) - { - receivedPacket = - static_cast - (listItem->GetItem()); - delete receivedPacket->pkt; - delete receivedPacket; - receivedPacket = NULL; - listItem = receivedPacketList.Next(listItem); - receivedPacketList.PopFront(); - } - assert(receivedPacketList.Empty()); - - while (fecMaskList.First() != NULL) - { - fecMaskList.PopFront(); - } - timeStamp += 90000 / 30; - } //loop over numImpPackets - } //loop over FecPackets - } //loop over numMediaPackets - delete [] packetMask; - } // loop over loss rates - - // Have DecodeFEC free allocated memory. - bool complete = true; - fec.DecodeFEC(receivedPacketList, recoveredPacketList, seqNum, complete); - if (!recoveredPacketList.Empty()) - { - printf("Error: recovered packet list is not empty\n"); - return -1; - } - - printf("\nAll tests passed successfully\n"); - - return 0; -} - - - -void ReceivePackets(webrtc::ListWrapper& toDecodeList, webrtc::ListWrapper& receivedPacketList, - WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate) -{ - assert(toDecodeList.Empty()); - assert(numPacketsToDecode <= receivedPacketList.GetSize()); - - webrtc::ListItem* listItem = receivedPacketList.First(); - for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++) - { - // Reorder packets. - float randomVariable = static_cast(rand()) / RAND_MAX; - while (randomVariable < reorderRate) - { - webrtc::ListItem* nextItem = receivedPacketList.Next(listItem); - if (nextItem == NULL) - { 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; + } } - else - { - listItem = nextItem; + } + mediaPacketListItem = mediaPacketList.begin(); + mediaPacketIdx = 0; + while (mediaPacketListItem != mediaPacketList.end()) { + if (mediaLossMask[mediaPacketIdx] == 1) { + // Should have recovered this packet. + std::list::iterator + recoveredPacketListItem = recoveredPacketList.begin(); + + if (recoveredPacketListItem == recoveredPacketList.end()) { + printf("Error: insufficient number of recovered packets.\n"); + return -1; + } + mediaPacket = *mediaPacketListItem; + ForwardErrorCorrection::RecoveredPacket* recoveredPacket = + *recoveredPacketListItem; + + if (recoveredPacket->pkt->length != mediaPacket->length) { + printf("Error: recovered packet length not identical to " + "original media packet\n"); + return -1; + } + if (memcmp(recoveredPacket->pkt->data, mediaPacket->data, + mediaPacket->length) != 0) { + printf("Error: recovered packet payload not identical to " + "original media packet\n"); + return -1; + } + delete recoveredPacket->pkt; + delete recoveredPacket; + recoveredPacketList.pop_front(); } - randomVariable = static_cast(rand()) / RAND_MAX; - } + mediaPacketIdx++; + mediaPacketListItem++; + } + if (!recoveredPacketList.empty()) { + printf("Error: excessive number of recovered packets.\n"); + printf("\t size is:%ld\n", recoveredPacketList.size()); + return -1; + } + // -- Teardown -- + mediaPacketListItem = mediaPacketList.begin(); + while (mediaPacketListItem != mediaPacketList.end()) { + delete *mediaPacketListItem; + mediaPacketListItem++; + mediaPacketList.pop_front(); + } + assert(mediaPacketList.empty()); - assert(listItem != NULL); - webrtc::ForwardErrorCorrection::ReceivedPacket* receivedPacket = - static_cast(listItem->GetItem()); - toDecodeList.PushBack(receivedPacket); + fecPacketListItem = fecPacketList.begin(); + while (fecPacketListItem != fecPacketList.end()) { + fecPacketListItem++; + fecPacketList.pop_front(); + } - // Duplicate packets. - randomVariable = static_cast(rand()) / RAND_MAX; - while (randomVariable < duplicateRate) - { - webrtc::ForwardErrorCorrection::ReceivedPacket* duplicatePacket = - new webrtc::ForwardErrorCorrection::ReceivedPacket; - memcpy(duplicatePacket, receivedPacket, - sizeof(webrtc::ForwardErrorCorrection::ReceivedPacket)); + // Delete received packets we didn't pass to DecodeFEC(), due to early + // frame completion. + std::list::iterator + receivedPacketIt = receivedPacketList.begin(); + while (receivedPacketIt != receivedPacketList.end()) { + receivedPacket = *receivedPacketIt; + delete receivedPacket->pkt; + delete receivedPacket; + receivedPacketIt++; + receivedPacketList.pop_front(); + } + assert(receivedPacketList.empty()); - duplicatePacket->pkt = new webrtc::ForwardErrorCorrection::Packet; - memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data, - receivedPacket->pkt->length); - duplicatePacket->pkt->length = receivedPacket->pkt->length; + while (!fecMaskList.empty()) { + fecMaskList.pop_front(); + } + timeStamp += 90000 / 30; + } //loop over numImpPackets + } //loop over FecPackets + } //loop over numMediaPackets + delete [] packetMask; + } // loop over loss rates - toDecodeList.PushBack(duplicatePacket); - randomVariable = static_cast(rand()) / RAND_MAX; - } - - receivedPacketList.Erase(listItem); - listItem = receivedPacketList.First(); - } + // Have DecodeFEC free allocated memory. + bool complete = true; + fec.DecodeFEC(&receivedPacketList, &recoveredPacketList, seqNum, complete); + if (!recoveredPacketList.empty()) { + printf("Error: recovered packet list is not empty\n"); + return -1; + } + printf("\nAll tests passed successfully\n"); + return 0; +} + +void ReceivePackets( + std::list* toDecodeList, + std::list* receivedPacketList, + WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate) { + assert(toDecodeList->empty()); + assert(numPacketsToDecode <= receivedPacketList->size()); + + std::list::iterator it; + for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++) { + it = receivedPacketList->begin(); + // Reorder packets. + float randomVariable = static_cast(rand()) / RAND_MAX; + while (randomVariable < reorderRate) { + it++; + if (it == receivedPacketList->end()) { + it++; + break; + } + randomVariable = static_cast(rand()) / RAND_MAX; + } + ForwardErrorCorrection::ReceivedPacket* receivedPacket = *it; + toDecodeList->push_back(receivedPacket); + + // Duplicate packets. + randomVariable = static_cast(rand()) / RAND_MAX; + while (randomVariable < duplicateRate) { + ForwardErrorCorrection::ReceivedPacket* duplicatePacket = + new ForwardErrorCorrection::ReceivedPacket; + memcpy(duplicatePacket, receivedPacket, + sizeof(ForwardErrorCorrection::ReceivedPacket)); + + duplicatePacket->pkt = new ForwardErrorCorrection::Packet; + memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data, + receivedPacket->pkt->length); + duplicatePacket->pkt->length = receivedPacket->pkt->length; + + toDecodeList->push_back(duplicatePacket); + randomVariable = static_cast(rand()) / RAND_MAX; + } + receivedPacketList->erase(it); + } }