558 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			558 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "defines.h"
 | 
						|
#include "rtp.h"
 | 
						|
#include "rtp_fec.h"
 | 
						|
 | 
						|
namespace RTP{
 | 
						|
  /// Based on the `block PT` value, we can either find the
 | 
						|
  /// contents of the codec payload (e.g. H264, VP8) or a ULPFEC header
 | 
						|
  /// (RFC 5109). The structure of the ULPFEC data is as follows.
 | 
						|
  ///
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///      |                RTP Header (12 octets or more)                 |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///      |                    FEC Header (10 octets)                     |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///      |                      FEC Level 0 Header                       |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///      |                     FEC Level 0 Payload                       |
 | 
						|
  ///      |                                                               |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///      |                      FEC Level 1 Header                       |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///      |                     FEC Level 1 Payload                       |
 | 
						|
  ///      |                                                               |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///      |                            Cont.                              |
 | 
						|
  ///      |                                                               |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///
 | 
						|
  /// FEC HEADER:
 | 
						|
  ///
 | 
						|
  ///       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        |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///
 | 
						|
  ///
 | 
						|
  /// FEC LEVEL HEADER
 | 
						|
  ///
 | 
						|
  ///       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)             |
 | 
						|
  ///      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///
 | 
						|
  PacketFEC::PacketFEC(){}
 | 
						|
 | 
						|
  PacketFEC::~PacketFEC(){
 | 
						|
    receivedSeqNums.clear();
 | 
						|
    coveredSeqNums.clear();
 | 
						|
  }
 | 
						|
 | 
						|
  bool PacketFEC::initWithREDPacket(const char *data, size_t nbytes){
 | 
						|
 | 
						|
    if (!data){
 | 
						|
      FAIL_MSG("Given fecData pointer is NULL.");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (nbytes < 23){
 | 
						|
      FAIL_MSG("Given fecData is too small. Should be at least: 12 (RTP) + 1 (RED) + 10 (FEC) 23 "
 | 
						|
               "bytes.");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (coveredSeqNums.size() != 0){
 | 
						|
      FAIL_MSG("It seems we're already initialized; coveredSeqNums already set.");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (receivedSeqNums.size() != 0){
 | 
						|
      FAIL_MSG("It seems we're already initialized; receivedSeqNums is not empty.");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // Decode RED header.
 | 
						|
    RTP::Packet rtpPkt(data, nbytes);
 | 
						|
    uint8_t *redHeader = (uint8_t *)(data + rtpPkt.getHsize());
 | 
						|
    uint8_t moreBlocks = redHeader[0] & 0x80;
 | 
						|
    if (moreBlocks == 1){
 | 
						|
      FAIL_MSG("RED header indicates there are multiple blocks. Haven't seen this before (@todo "
 | 
						|
               "implement, exiting now).");
 | 
						|
      // \todo do not EXIT!
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // Copy the data, starting at the FEC header (skip RTP + RED header)
 | 
						|
    size_t numHeaderBytes = rtpPkt.getHsize() + 1;
 | 
						|
    if (numHeaderBytes > nbytes){
 | 
						|
      FAIL_MSG("Invalid FEC packet; too small to contain FEC data.");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    fecPacketData.assign(NULL, 0);
 | 
						|
    fecPacketData.append(data + numHeaderBytes, nbytes - numHeaderBytes);
 | 
						|
 | 
						|
    // Extract the sequence numbers this packet protects.
 | 
						|
    if (!extractCoveringSequenceNumbers()){
 | 
						|
      FAIL_MSG("Failed to extract the protected sequence numbers for this FEC.");
 | 
						|
      // @todo we probably want to reset our set.
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  uint8_t PacketFEC::getExtensionFlag(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG("Cannot get extension-flag from the FEC header; fecPacketData member is not set. "
 | 
						|
               "Not initialized?");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return ((fecPacketData[0] & 0x80) >> 7);
 | 
						|
  }
 | 
						|
 | 
						|
  uint8_t PacketFEC::getLongMaskFlag(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG("Cannot get the long-mask-flag from the FEC header. fecPacketData member is not "
 | 
						|
               "set. Not initialized?");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return ((fecPacketData[0] & 0x40) >> 6);
 | 
						|
  }
 | 
						|
 | 
						|
  // Returns 0 (error), 2 or 6, wich are the valid sizes of the mask.
 | 
						|
  uint8_t PacketFEC::getNumBytesUsedForMask(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG("Cannot get the number of bytes used by the mask. fecPacketData member is not set. "
 | 
						|
               "Not initialized?");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (getLongMaskFlag() == 0){return 2;}
 | 
						|
 | 
						|
    return 6;
 | 
						|
  }
 | 
						|
 | 
						|
  uint16_t PacketFEC::getSequenceBaseNumber(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG(
 | 
						|
          "Cannot get the sequence base number. fecPacketData member is not set. Not initialized?");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return (uint16_t)(fecPacketData[2] << 8) | fecPacketData[3];
 | 
						|
  }
 | 
						|
 | 
						|
  char *PacketFEC::getFECHeader(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG("Cannot get fec header. fecPacketData member is not set. Not initialized?");
 | 
						|
    }
 | 
						|
 | 
						|
    return fecPacketData;
 | 
						|
  }
 | 
						|
 | 
						|
  char *PacketFEC::getLevel0Header(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG("Cannot get the level 0 header. fecPacketData member is not set. Not initialized?");
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return (char *)(fecPacketData + 10);
 | 
						|
  }
 | 
						|
 | 
						|
  char *PacketFEC::getLevel0Payload(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG("Cannot get the level 0 payload. fecPacketData member is not set. Not initialized?");
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    // 10 bytes for FEC header
 | 
						|
    // 2 bytes for `Protection Length`
 | 
						|
    // 2 or 6 bytes for `mask`.
 | 
						|
    return (char *)(fecPacketData + 10 + 2 + getNumBytesUsedForMask());
 | 
						|
  }
 | 
						|
 | 
						|
  uint16_t PacketFEC::getLevel0ProtectionLength(){
 | 
						|
 | 
						|
    if (fecPacketData.size() == 0){
 | 
						|
      FAIL_MSG("Cannot get the level 0 protection length. fecPacketData member is not set. Not "
 | 
						|
               "initialized?");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    char *level0Header = getLevel0Header();
 | 
						|
    if (!level0Header){
 | 
						|
      FAIL_MSG("Failed to get the level 0 header, cannot get protection length.");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    uint16_t protectionLength = (level0Header[0] << 8) | level0Header[1];
 | 
						|
    return protectionLength;
 | 
						|
  }
 | 
						|
 | 
						|
  uint16_t PacketFEC::getLengthRecovery(){
 | 
						|
 | 
						|
    char *fecHeader = getFECHeader();
 | 
						|
    if (!fecHeader){
 | 
						|
      FAIL_MSG("Cannot get the FEC header which we need to get the `length recovery` field. Not "
 | 
						|
               "initialized?");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    uint16_t lengthRecovery = (fecHeader[8] << 8) | fecHeader[9];
 | 
						|
    return lengthRecovery;
 | 
						|
  }
 | 
						|
 | 
						|
  // Based on InsertFecPacket of forward_error_correction.cc from
 | 
						|
  // Chromium. (used as reference). The `mask` from the
 | 
						|
  // FEC-level-header can be 2 or 6 bytes long. Whenever a bit is
 | 
						|
  // set to 1 it means that we have to calculate the sequence
 | 
						|
  // number for that bit. To calculate the sequence number we
 | 
						|
  // start with the `SN base` value (base sequence number) and
 | 
						|
  // use the bit offset to increment the SN-base value. E.g.
 | 
						|
  // when it's bit 4 and SN-base is 230, it meas that this FEC
 | 
						|
  // packet protects the media packet with sequence number
 | 
						|
  // 230. We have to start counting the bit numbers from the
 | 
						|
  // most-significant-bit (e.g. 1 << 7).
 | 
						|
  bool PacketFEC::extractCoveringSequenceNumbers(){
 | 
						|
 | 
						|
    if (coveredSeqNums.size() != 0){
 | 
						|
      FAIL_MSG("Cannot extract protected sequence numbers; looks like we already did that.");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    size_t maskNumBytes = getNumBytesUsedForMask();
 | 
						|
    if (maskNumBytes != 2 && maskNumBytes != 6){
 | 
						|
      FAIL_MSG("Invalid mask size (%u) cannot extract sequence numbers.", maskNumBytes);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    char *maskPtr = getLevel0Header();
 | 
						|
    if (!maskPtr){
 | 
						|
      FAIL_MSG("Failed to get the level-0 header ptr. Cannot extract protected sequence numbers.");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    uint16_t seqNumBase = getSequenceBaseNumber();
 | 
						|
    if (seqNumBase == 0){WARN_MSG("Base sequence number is 0; it's possible but unlikely.");}
 | 
						|
 | 
						|
    // Skip the `Protection Length`
 | 
						|
    maskPtr = maskPtr + 2;
 | 
						|
 | 
						|
    for (uint16_t byteDX = 0; byteDX < maskNumBytes; ++byteDX){
 | 
						|
      uint8_t maskByte = maskPtr[byteDX];
 | 
						|
      for (uint16_t bitDX = 0; bitDX < 8; ++bitDX){
 | 
						|
        if (maskByte & (1 << 7 - bitDX)){
 | 
						|
          uint16_t seqNum = seqNumBase + (byteDX << 3) + bitDX;
 | 
						|
          coveredSeqNums.insert(seqNum);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // \todo rename coversSequenceNumber
 | 
						|
  bool PacketFEC::coversSequenceNumber(uint16_t sn){
 | 
						|
    return (coveredSeqNums.count(sn) == 0) ? false : true;
 | 
						|
  }
 | 
						|
 | 
						|
  void PacketFEC::addReceivedSequenceNumber(uint16_t sn){
 | 
						|
    if (false == coversSequenceNumber(sn)){
 | 
						|
      FAIL_MSG("Trying to add a received sequence number this instance is not handling (%u).", sn);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    receivedSeqNums.insert(sn);
 | 
						|
  }
 | 
						|
 | 
						|
  /// This function can be called to recover a missing packet. A
 | 
						|
  /// FEC packet is received with a list of media packets it
 | 
						|
  /// might be able to recover; this PacketFEC is received after
 | 
						|
  /// we should have received the media packets it's protecting.
 | 
						|
  ///
 | 
						|
  /// Here we first fill al list with the received sequence
 | 
						|
  /// numbers that we're protecting; when we're missing one
 | 
						|
  /// packet this function will try to recover it.
 | 
						|
  ///
 | 
						|
  /// The `receivedMediaPackets` is the history of media packets
 | 
						|
  /// that you received and keep in a memory. These are used
 | 
						|
  /// when XORing when we reconstruct a packet.
 | 
						|
  void PacketFEC::tryToRecoverMissingPacket(std::map<uint16_t, Packet> &receivedMediaPackets,
 | 
						|
                                            Packet &reconstructedPacket){
 | 
						|
 | 
						|
    // Mark all the media packets that we protect and which have
 | 
						|
    // been received as "received" in our internal list.
 | 
						|
    std::set<uint16_t>::iterator protIt = coveredSeqNums.begin();
 | 
						|
    while (protIt != coveredSeqNums.end()){
 | 
						|
      if (receivedMediaPackets.count(*protIt) == 1){addReceivedSequenceNumber(*protIt);}
 | 
						|
      protIt++;
 | 
						|
    }
 | 
						|
 | 
						|
    // We have received all media packets that we could recover;
 | 
						|
    // so there is no need for this FEC packet.
 | 
						|
    // @todo Jaron shall we reuse allocs/PacketFECs?
 | 
						|
    if (receivedSeqNums.size() == coveredSeqNums.size()){return;}
 | 
						|
 | 
						|
    if (coveredSeqNums.size() != (receivedSeqNums.size() + 1)){
 | 
						|
      // missing more then 1 packet. we can only recover when
 | 
						|
      // one packet is lost.
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Find missing sequence number.
 | 
						|
    uint16_t missingSeqNum = 0;
 | 
						|
    protIt = coveredSeqNums.begin();
 | 
						|
    while (protIt != coveredSeqNums.end()){
 | 
						|
      if (receivedSeqNums.count(*protIt) == 0){
 | 
						|
        missingSeqNum = *protIt;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      ++protIt;
 | 
						|
    }
 | 
						|
    if (!coversSequenceNumber(missingSeqNum)){
 | 
						|
      WARN_MSG("We cannot recover %u.", missingSeqNum);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Copy FEC into new RTP-header
 | 
						|
    char *fecHeader = getFECHeader();
 | 
						|
    if (!fecHeader){
 | 
						|
      FAIL_MSG("Failed to get the fec header. Cannot recover.");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    recoverData.assign(NULL, 0);
 | 
						|
    recoverData.append(fecHeader, 12);
 | 
						|
 | 
						|
    // Copy FEC into new RTP-payload
 | 
						|
    char *level0Payload = getLevel0Payload();
 | 
						|
    if (!level0Payload){
 | 
						|
      FAIL_MSG("Failed to get the level-0 payload data (XOR'd media data from FEC packet).");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    uint16_t level0ProtLen = getLevel0ProtectionLength();
 | 
						|
    if (level0ProtLen == 0){
 | 
						|
      FAIL_MSG("Failed to get the level-0 protection length.");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    recoverData.append(level0Payload, level0ProtLen);
 | 
						|
 | 
						|
    uint8_t recoverLength[2] ={fecHeader[8], fecHeader[9]};
 | 
						|
 | 
						|
    // XOR headers
 | 
						|
    protIt = coveredSeqNums.begin();
 | 
						|
    while (protIt != coveredSeqNums.end()){
 | 
						|
 | 
						|
      uint16_t seqNum = *protIt;
 | 
						|
      if (seqNum == missingSeqNum){
 | 
						|
        ++protIt;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      Packet &mediaPacket = receivedMediaPackets[seqNum];
 | 
						|
      char *mediaData = mediaPacket.ptr();
 | 
						|
      uint16_t mediaSize = mediaPacket.getPayloadSize();
 | 
						|
      uint8_t *mediaSizePtr = (uint8_t *)&mediaSize;
 | 
						|
 | 
						|
      WARN_MSG(" => XOR header with %u, size: %u.", seqNum, mediaSize);
 | 
						|
 | 
						|
      // V, P, X, CC, M, PT
 | 
						|
      recoverData[0] ^= mediaData[0];
 | 
						|
      recoverData[1] ^= mediaData[1];
 | 
						|
 | 
						|
      // Timestamp
 | 
						|
      recoverData[4] ^= mediaData[4];
 | 
						|
      recoverData[5] ^= mediaData[5];
 | 
						|
      recoverData[6] ^= mediaData[6];
 | 
						|
      recoverData[7] ^= mediaData[7];
 | 
						|
 | 
						|
      // Length of recovered media packet
 | 
						|
      recoverLength[0] ^= mediaSizePtr[1];
 | 
						|
      recoverLength[1] ^= mediaSizePtr[0];
 | 
						|
 | 
						|
      ++protIt;
 | 
						|
    }
 | 
						|
 | 
						|
    uint16_t recoverPayloadSize = ntohs(*(uint16_t *)recoverLength);
 | 
						|
 | 
						|
    // XOR payloads
 | 
						|
    protIt = coveredSeqNums.begin();
 | 
						|
    while (protIt != coveredSeqNums.end()){
 | 
						|
      uint16_t seqNum = *protIt;
 | 
						|
      if (seqNum == missingSeqNum){
 | 
						|
        ++protIt;
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      Packet &mediaPacket = receivedMediaPackets[seqNum];
 | 
						|
      char *mediaData = mediaPacket.ptr() + mediaPacket.getHsize();
 | 
						|
      for (size_t i = 0; i < recoverPayloadSize; ++i){recoverData[12 + i] ^= mediaData[i];}
 | 
						|
      ++protIt;
 | 
						|
    }
 | 
						|
 | 
						|
    // And setup the reconstructed packet.
 | 
						|
    reconstructedPacket = Packet(recoverData, recoverPayloadSize);
 | 
						|
    reconstructedPacket.setSequence(missingSeqNum);
 | 
						|
    // @todo check what other header fields we need to fix.
 | 
						|
  }
 | 
						|
 | 
						|
  void FECSorter::addPacket(const Packet &pack){
 | 
						|
    if (tmpVideoLossPrevention & SDP_LOSS_PREVENTION_ULPFEC){
 | 
						|
      packetHistory[pack.getSequence()] = pack;
 | 
						|
      while (packetHistory.begin()->first < pack.getSequence() - 500){
 | 
						|
        packetHistory.erase(packetHistory.begin());
 | 
						|
      }
 | 
						|
    }
 | 
						|
    Sorter::addPacket(pack);
 | 
						|
  }
 | 
						|
 | 
						|
  /// This function will handle RED packets that may be used to
 | 
						|
  /// encapsulate ULPFEC or simply the codec payload (e.g. H264,
 | 
						|
  /// VP8). This function is created to handle FEC with
 | 
						|
  /// WebRTC. When we want to use FEC with WebRTC we have to add
 | 
						|
  /// both the `a=rtpmap:<ulp-fmt> ulpfec/90000` and
 | 
						|
  /// `a=rtpmap<red-fmt> red/90000` lines to the SDP. FEC is
 | 
						|
  /// always used together with RED (RFC 1298).  It turns out
 | 
						|
  /// that with WebRTC the RED only adds one byte after the RTP
 | 
						|
  /// header (only the `F` and `block PT`, see below)`. The
 | 
						|
  /// `block PT` is the payload type of the data that
 | 
						|
  /// follows. This would be `<ulp-fmt>` for FEC data. Though
 | 
						|
  /// these RED packets may contain FEC or just the media:
 | 
						|
  /// H264/VP8.
 | 
						|
  ///
 | 
						|
  /// RED HEADER:
 | 
						|
  ///
 | 
						|
  ///    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
 | 
						|
  ///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///   |F|   block PT  |  timestamp offset         |   block length    |
 | 
						|
  ///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
						|
  ///
 | 
						|
  void FECSorter::addREDPacket(char *dat, unsigned int len, uint8_t codecPayloadType,
 | 
						|
                               uint8_t REDPayloadType, uint8_t ULPFECPayloadType){
 | 
						|
 | 
						|
    RTP::Packet pkt(dat, len);
 | 
						|
    if (pkt.getPayloadType() != REDPayloadType){
 | 
						|
      FAIL_MSG("Requested to add a RED packet, but it has an invalid payload type.");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Check if the `F` flag is set. Chromium will always set
 | 
						|
    // this to 0 (at time of writing, check: https://goo.gl/y1eJ6k
 | 
						|
    uint8_t *REDHeader = (uint8_t *)(dat + pkt.getHsize());
 | 
						|
    uint8_t moreBlocksAvailable = REDHeader[0] & 0x80;
 | 
						|
    if (moreBlocksAvailable == 1){
 | 
						|
      FAIL_MSG("Not yet received a RED packet that had it's F bit set; @todo implement.");
 | 
						|
      exit(EXIT_FAILURE);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Extract the `block PT` field which can be the media-pt,
 | 
						|
    // fec-pt. When it's just media that follows, we move all
 | 
						|
    // data one byte up and reconstruct a normal media packet.
 | 
						|
    uint8_t blockPayloadType = REDHeader[0] & 0x7F;
 | 
						|
    if (blockPayloadType == codecPayloadType){
 | 
						|
      memmove(dat + pkt.getHsize(), dat + pkt.getHsize() + 1, len - pkt.getHsize() - 1);
 | 
						|
      dat[1] &= 0x80;
 | 
						|
      dat[1] |= codecPayloadType;
 | 
						|
      RTP::Packet mediaPacket((const char *)dat, len - 1);
 | 
						|
      addPacket(mediaPacket);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // When the payloadType equals our ULP/FEC payload type, we
 | 
						|
    // received a REC packet (RFC 5109) that contains FEC data
 | 
						|
    // and a list of sequence number that it covers and can
 | 
						|
    // reconstruct.
 | 
						|
    //
 | 
						|
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
    //
 | 
						|
    // \todo Jaron, I'm now just generating a `PacketFEC` on the heap
 | 
						|
    //       and we're not managing destruction anywhere atm; I guess
 | 
						|
    //       re-use or destruction needs to be part of the algo that
 | 
						|
    //       is going to deal with FEC.
 | 
						|
    //
 | 
						|
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
    if (blockPayloadType == ULPFECPayloadType){
 | 
						|
      WARN_MSG(" => got fec packet: %u", pkt.getSequence());
 | 
						|
      PacketFEC *fec = new PacketFEC();
 | 
						|
      if (!fec->initWithREDPacket(dat, len)){
 | 
						|
        delete fec;
 | 
						|
        fec = NULL;
 | 
						|
        FAIL_MSG("Failed to initialize a `PacketFEC`");
 | 
						|
      }
 | 
						|
      fecPackets.push_back(fec);
 | 
						|
 | 
						|
      Packet recreatedPacket;
 | 
						|
      fec->tryToRecoverMissingPacket(packetHistory, recreatedPacket);
 | 
						|
      if (recreatedPacket.ptr() != NULL){
 | 
						|
        char *pl = recreatedPacket.getPayload();
 | 
						|
        WARN_MSG(" => reconstructed %u, %02X %02X %02X %02X | %02X %02X %02X %02X",
 | 
						|
                 recreatedPacket.getSequence(), pl[0], pl[1], pl[2], pl[3], pl[4], pl[5], pl[6], pl[7]);
 | 
						|
        addPacket(recreatedPacket);
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    FAIL_MSG("Unhandled RED block payload type %u. Check the answer SDP.", blockPayloadType);
 | 
						|
  }
 | 
						|
 | 
						|
  /// Each FEC packet is capable of recovering a limited amount
 | 
						|
  /// of media packets. Some experimentation showed that most
 | 
						|
  /// often one FEC is used to protect somewhere between 2-10
 | 
						|
  /// media packets.  Each FEC packet has a list of sequence
 | 
						|
  /// number that it can recover when all other media packets
 | 
						|
  /// have been received except the one that we want to
 | 
						|
  /// recover. This function returns the FEC packet might be able
 | 
						|
  /// to recover the given sequence number.
 | 
						|
  PacketFEC *FECSorter::getFECPacketWhichCoversSequenceNumber(uint16_t sn){
 | 
						|
    size_t nfecs = fecPackets.size();
 | 
						|
    for (size_t i = 0; i < nfecs; ++i){
 | 
						|
      PacketFEC *fec = fecPackets[i];
 | 
						|
      if (fec->coversSequenceNumber(sn)){return fec;}
 | 
						|
    }
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  void FECPacket::sendRTCP_RR(RTP::FECSorter &sorter, uint32_t mySSRC, uint32_t theirSSRC, void *userData,
 | 
						|
                              void callBack(void *userData, const char *payload, uint32_t nbytes)){
 | 
						|
    char *rtcpData = (char *)malloc(32);
 | 
						|
    if (!rtcpData){
 | 
						|
      FAIL_MSG("Could not allocate 32 bytes. Something is seriously messed up.");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (!(sorter.lostCurrent + sorter.packCurrent)){sorter.packCurrent++;}
 | 
						|
    rtcpData[0] = 0x81;                  // version 2, no padding, one receiver report
 | 
						|
    rtcpData[1] = 201;                   // receiver report
 | 
						|
    Bit::htobs(rtcpData + 2, 7);         // 7 4-byte words follow the header
 | 
						|
    Bit::htobl(rtcpData + 4, mySSRC);    // set receiver identifier
 | 
						|
    Bit::htobl(rtcpData + 8, theirSSRC); // set source identifier
 | 
						|
    rtcpData[12] = (sorter.lostCurrent * 255) / (sorter.lostCurrent + sorter.packCurrent); // fraction lost since prev RR
 | 
						|
    Bit::htob24(rtcpData + 13, sorter.lostTotal); // cumulative packets lost since start
 | 
						|
    Bit::htobl(rtcpData + 16, sorter.rtpSeq | (sorter.packTotal & 0xFFFF0000ul)); // highest sequence received
 | 
						|
    Bit::htobl(rtcpData + 20, 0); /// \TODO jitter (diff in timestamp vs packet arrival)
 | 
						|
    Bit::htobl(rtcpData + 24, 0); /// \TODO last SR (middle 32 bits of last SR or zero)
 | 
						|
    Bit::htobl(rtcpData + 28, 0); /// \TODO delay since last SR in 2b seconds + 2b fraction
 | 
						|
    callBack(userData, rtcpData, 32);
 | 
						|
    sorter.lostCurrent = 0;
 | 
						|
    sorter.packCurrent = 0;
 | 
						|
    free(rtcpData);
 | 
						|
  }
 | 
						|
 | 
						|
}// namespace RTP
 |