
BUG=webrtc:4771 R=stefan@webrtc.org Review URL: https://codereview.webrtc.org/1219703002 Cr-Commit-Position: refs/heads/master@{#9514}
260 lines
8.9 KiB
C++
260 lines
8.9 KiB
C++
/*
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "webrtc/base/scoped_ptr.h"
|
|
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
|
|
#include "webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h"
|
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
|
#include "webrtc/system_wrappers/interface/logging.h"
|
|
|
|
// RFC 5109
|
|
namespace webrtc {
|
|
|
|
FecReceiver* FecReceiver::Create(RtpData* callback) {
|
|
return new FecReceiverImpl(callback);
|
|
}
|
|
|
|
FecReceiverImpl::FecReceiverImpl(RtpData* callback)
|
|
: crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
|
recovered_packet_callback_(callback),
|
|
fec_(new ForwardErrorCorrection()) {}
|
|
|
|
FecReceiverImpl::~FecReceiverImpl() {
|
|
while (!received_packet_list_.empty()) {
|
|
delete received_packet_list_.front();
|
|
received_packet_list_.pop_front();
|
|
}
|
|
if (fec_ != NULL) {
|
|
fec_->ResetState(&recovered_packet_list_);
|
|
delete fec_;
|
|
}
|
|
}
|
|
|
|
FecPacketCounter FecReceiverImpl::GetPacketCounter() const {
|
|
CriticalSectionScoped cs(crit_sect_.get());
|
|
return packet_counter_;
|
|
}
|
|
|
|
// 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 |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
//
|
|
//
|
|
// RFC 2198 RTP Payload for Redundant Audio Data September 1997
|
|
//
|
|
// The bits in the header are specified as follows:
|
|
//
|
|
// F: 1 bit First bit in header indicates whether another header block
|
|
// follows. If 1 further header blocks follow, if 0 this is the
|
|
// last header block.
|
|
// If 0 there is only 1 byte RED header
|
|
//
|
|
// block PT: 7 bits RTP payload type for this block.
|
|
//
|
|
// timestamp offset: 14 bits Unsigned offset of timestamp of this block
|
|
// relative to timestamp given in RTP header. The use of an unsigned
|
|
// offset implies that redundant data must be sent after the primary
|
|
// data, and is hence a time to be subtracted from the current
|
|
// timestamp to determine the timestamp of the data for which this
|
|
// block is the redundancy.
|
|
//
|
|
// block length: 10 bits Length in bytes of the corresponding data
|
|
// block excluding header.
|
|
|
|
int32_t FecReceiverImpl::AddReceivedRedPacket(
|
|
const RTPHeader& header, const uint8_t* incoming_rtp_packet,
|
|
size_t packet_length, uint8_t ulpfec_payload_type) {
|
|
CriticalSectionScoped cs(crit_sect_.get());
|
|
uint8_t REDHeaderLength = 1;
|
|
size_t payload_data_length = packet_length - header.headerLength;
|
|
|
|
if (payload_data_length == 0) {
|
|
LOG(LS_WARNING) << "Corrupt/truncated FEC packet.";
|
|
return -1;
|
|
}
|
|
|
|
// Add to list without RED header, aka a virtual RTP packet
|
|
// we remove the RED header
|
|
|
|
rtc::scoped_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet(
|
|
new ForwardErrorCorrection::ReceivedPacket);
|
|
received_packet->pkt = new ForwardErrorCorrection::Packet;
|
|
|
|
// get payload type from RED header
|
|
uint8_t payload_type =
|
|
incoming_rtp_packet[header.headerLength] & 0x7f;
|
|
|
|
received_packet->is_fec = payload_type == ulpfec_payload_type;
|
|
received_packet->seq_num = header.sequenceNumber;
|
|
|
|
uint16_t blockLength = 0;
|
|
if (incoming_rtp_packet[header.headerLength] & 0x80) {
|
|
// f bit set in RED header
|
|
REDHeaderLength = 4;
|
|
if (payload_data_length < REDHeaderLength) {
|
|
LOG(LS_WARNING) << "Corrupt/truncated FEC packet.";
|
|
return -1;
|
|
}
|
|
|
|
uint16_t timestamp_offset =
|
|
(incoming_rtp_packet[header.headerLength + 1]) << 8;
|
|
timestamp_offset +=
|
|
incoming_rtp_packet[header.headerLength + 2];
|
|
timestamp_offset = timestamp_offset >> 2;
|
|
if (timestamp_offset != 0) {
|
|
LOG(LS_WARNING) << "Corrupt payload found.";
|
|
return -1;
|
|
}
|
|
|
|
blockLength =
|
|
(0x03 & incoming_rtp_packet[header.headerLength + 2]) << 8;
|
|
blockLength += (incoming_rtp_packet[header.headerLength + 3]);
|
|
|
|
// check next RED header
|
|
if (incoming_rtp_packet[header.headerLength + 4] & 0x80) {
|
|
LOG(LS_WARNING) << "More than 2 blocks in packet not supported.";
|
|
return -1;
|
|
}
|
|
if (blockLength > payload_data_length - REDHeaderLength) {
|
|
LOG(LS_WARNING) << "Block length longer than packet.";
|
|
return -1;
|
|
}
|
|
}
|
|
++packet_counter_.num_packets;
|
|
|
|
rtc::scoped_ptr<ForwardErrorCorrection::ReceivedPacket>
|
|
second_received_packet;
|
|
if (blockLength > 0) {
|
|
// handle block length, split into 2 packets
|
|
REDHeaderLength = 5;
|
|
|
|
// copy the RTP header
|
|
memcpy(received_packet->pkt->data, incoming_rtp_packet,
|
|
header.headerLength);
|
|
|
|
// replace the RED payload type
|
|
received_packet->pkt->data[1] &= 0x80; // reset the payload
|
|
received_packet->pkt->data[1] +=
|
|
payload_type; // set the media payload type
|
|
|
|
// copy the payload data
|
|
memcpy(
|
|
received_packet->pkt->data + header.headerLength,
|
|
incoming_rtp_packet + header.headerLength + REDHeaderLength,
|
|
blockLength);
|
|
|
|
received_packet->pkt->length = blockLength;
|
|
|
|
second_received_packet.reset(new ForwardErrorCorrection::ReceivedPacket);
|
|
second_received_packet->pkt = new ForwardErrorCorrection::Packet;
|
|
|
|
second_received_packet->is_fec = true;
|
|
second_received_packet->seq_num = header.sequenceNumber;
|
|
++packet_counter_.num_fec_packets;
|
|
|
|
// copy the FEC payload data
|
|
memcpy(second_received_packet->pkt->data,
|
|
incoming_rtp_packet + header.headerLength +
|
|
REDHeaderLength + blockLength,
|
|
payload_data_length - REDHeaderLength - blockLength);
|
|
|
|
second_received_packet->pkt->length =
|
|
payload_data_length - REDHeaderLength - blockLength;
|
|
|
|
} else if (received_packet->is_fec) {
|
|
++packet_counter_.num_fec_packets;
|
|
// everything behind the RED header
|
|
memcpy(
|
|
received_packet->pkt->data,
|
|
incoming_rtp_packet + header.headerLength + REDHeaderLength,
|
|
payload_data_length - REDHeaderLength);
|
|
received_packet->pkt->length = payload_data_length - REDHeaderLength;
|
|
received_packet->ssrc =
|
|
ByteReader<uint32_t>::ReadBigEndian(&incoming_rtp_packet[8]);
|
|
|
|
} else {
|
|
// copy the RTP header
|
|
memcpy(received_packet->pkt->data, incoming_rtp_packet,
|
|
header.headerLength);
|
|
|
|
// replace the RED payload type
|
|
received_packet->pkt->data[1] &= 0x80; // reset the payload
|
|
received_packet->pkt->data[1] +=
|
|
payload_type; // set the media payload type
|
|
|
|
// copy the media payload data
|
|
memcpy(
|
|
received_packet->pkt->data + header.headerLength,
|
|
incoming_rtp_packet + header.headerLength + REDHeaderLength,
|
|
payload_data_length - REDHeaderLength);
|
|
|
|
received_packet->pkt->length =
|
|
header.headerLength + payload_data_length - REDHeaderLength;
|
|
}
|
|
|
|
if (received_packet->pkt->length == 0) {
|
|
return 0;
|
|
}
|
|
|
|
received_packet_list_.push_back(received_packet.release());
|
|
if (second_received_packet) {
|
|
received_packet_list_.push_back(second_received_packet.release());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32_t FecReceiverImpl::ProcessReceivedFec() {
|
|
crit_sect_->Enter();
|
|
if (!received_packet_list_.empty()) {
|
|
// Send received media packet to VCM.
|
|
if (!received_packet_list_.front()->is_fec) {
|
|
ForwardErrorCorrection::Packet* packet =
|
|
received_packet_list_.front()->pkt;
|
|
crit_sect_->Leave();
|
|
if (!recovered_packet_callback_->OnRecoveredPacket(packet->data,
|
|
packet->length)) {
|
|
return -1;
|
|
}
|
|
crit_sect_->Enter();
|
|
}
|
|
if (fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_) != 0) {
|
|
crit_sect_->Leave();
|
|
return -1;
|
|
}
|
|
assert(received_packet_list_.empty());
|
|
}
|
|
// Send any recovered media packets to VCM.
|
|
ForwardErrorCorrection::RecoveredPacketList::iterator it =
|
|
recovered_packet_list_.begin();
|
|
for (; it != recovered_packet_list_.end(); ++it) {
|
|
if ((*it)->returned) // Already sent to the VCM and the jitter buffer.
|
|
continue;
|
|
ForwardErrorCorrection::Packet* packet = (*it)->pkt;
|
|
++packet_counter_.num_recovered_packets;
|
|
crit_sect_->Leave();
|
|
if (!recovered_packet_callback_->OnRecoveredPacket(packet->data,
|
|
packet->length)) {
|
|
return -1;
|
|
}
|
|
crit_sect_->Enter();
|
|
(*it)->returned = true;
|
|
}
|
|
crit_sect_->Leave();
|
|
return 0;
|
|
}
|
|
|
|
} // namespace webrtc
|