BUG=1662 R=stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/1557004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4135 4adac7df-926f-26a2-2b94-8c16560cd09d
261 lines
9.6 KiB
C++
261 lines
9.6 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/producer_fec.h"
|
|
|
|
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
|
|
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
|
|
|
namespace webrtc {
|
|
|
|
enum { kREDForFECHeaderLength = 1 };
|
|
// This controls the maximum amount of excess overhead (actual - target)
|
|
// allowed in order to trigger GenerateFEC(), before |params_.max_fec_frames|
|
|
// is reached. Overhead here is defined as relative to number of media packets.
|
|
enum { kMaxExcessOverhead = 50 }; // Q8.
|
|
// This is the minimum number of media packets required (above some protection
|
|
// level) in order to trigger GenerateFEC(), before |params_.max_fec_frames| is
|
|
// reached.
|
|
enum { kMinimumMediaPackets = 4 };
|
|
// Threshold on the received FEC protection level, above which we enforce at
|
|
// least |kMinimumMediaPackets| packets for the FEC code. Below this
|
|
// threshold |kMinimumMediaPackets| is set to default value of 1.
|
|
enum { kHighProtectionThreshold = 80 }; // Corresponds to ~30 overhead, range
|
|
// is 0 to 255, where 255 corresponds to 100% overhead (relative to number of
|
|
// media packets).
|
|
|
|
struct RtpPacket {
|
|
uint16_t rtpHeaderLength;
|
|
ForwardErrorCorrection::Packet* pkt;
|
|
};
|
|
|
|
RedPacket::RedPacket(int length)
|
|
: data_(new uint8_t[length]),
|
|
length_(length),
|
|
header_length_(0) {
|
|
}
|
|
|
|
RedPacket::~RedPacket() {
|
|
delete [] data_;
|
|
}
|
|
|
|
void RedPacket::CreateHeader(const uint8_t* rtp_header, int header_length,
|
|
int red_pl_type, int pl_type) {
|
|
assert(header_length + kREDForFECHeaderLength <= length_);
|
|
memcpy(data_, rtp_header, header_length);
|
|
// Replace payload type.
|
|
data_[1] &= 0x80;
|
|
data_[1] += red_pl_type;
|
|
// Add RED header
|
|
// f-bit always 0
|
|
data_[header_length] = pl_type;
|
|
header_length_ = header_length + kREDForFECHeaderLength;
|
|
}
|
|
|
|
void RedPacket::SetSeqNum(int seq_num) {
|
|
assert(seq_num >= 0 && seq_num < (1<<16));
|
|
ModuleRTPUtility::AssignUWord16ToBuffer(&data_[2], seq_num);
|
|
}
|
|
|
|
void RedPacket::AssignPayload(const uint8_t* payload, int length) {
|
|
assert(header_length_ + length <= length_);
|
|
memcpy(data_ + header_length_, payload, length);
|
|
}
|
|
|
|
void RedPacket::ClearMarkerBit() {
|
|
data_[1] &= 0x7F;
|
|
}
|
|
|
|
uint8_t* RedPacket::data() const {
|
|
return data_;
|
|
}
|
|
|
|
int RedPacket::length() const {
|
|
return length_;
|
|
}
|
|
|
|
ProducerFec::ProducerFec(ForwardErrorCorrection* fec)
|
|
: fec_(fec),
|
|
media_packets_fec_(),
|
|
fec_packets_(),
|
|
num_frames_(0),
|
|
incomplete_frame_(false),
|
|
num_first_partition_(0),
|
|
minimum_media_packets_fec_(1),
|
|
params_(),
|
|
new_params_() {
|
|
memset(¶ms_, 0, sizeof(params_));
|
|
memset(&new_params_, 0, sizeof(new_params_));
|
|
}
|
|
|
|
ProducerFec::~ProducerFec() {
|
|
DeletePackets();
|
|
}
|
|
|
|
void ProducerFec::SetFecParameters(const FecProtectionParams* params,
|
|
int num_first_partition) {
|
|
// Number of first partition packets cannot exceed kMaxMediaPackets
|
|
assert(params->fec_rate >= 0 && params->fec_rate < 256);
|
|
if (num_first_partition >
|
|
static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets)) {
|
|
num_first_partition =
|
|
ForwardErrorCorrection::kMaxMediaPackets;
|
|
}
|
|
// Store the new params and apply them for the next set of FEC packets being
|
|
// produced.
|
|
new_params_ = *params;
|
|
num_first_partition_ = num_first_partition;
|
|
if (params->fec_rate > kHighProtectionThreshold) {
|
|
minimum_media_packets_fec_ = kMinimumMediaPackets;
|
|
} else {
|
|
minimum_media_packets_fec_ = 1;
|
|
}
|
|
}
|
|
|
|
RedPacket* ProducerFec::BuildRedPacket(const uint8_t* data_buffer,
|
|
int payload_length,
|
|
int rtp_header_length,
|
|
int red_pl_type) {
|
|
RedPacket* red_packet = new RedPacket(payload_length +
|
|
kREDForFECHeaderLength +
|
|
rtp_header_length);
|
|
int pl_type = data_buffer[1] & 0x7f;
|
|
red_packet->CreateHeader(data_buffer, rtp_header_length,
|
|
red_pl_type, pl_type);
|
|
red_packet->AssignPayload(data_buffer + rtp_header_length, payload_length);
|
|
return red_packet;
|
|
}
|
|
|
|
int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
|
|
int payload_length,
|
|
int rtp_header_length) {
|
|
assert(fec_packets_.empty());
|
|
if (media_packets_fec_.empty()) {
|
|
params_ = new_params_;
|
|
}
|
|
incomplete_frame_ = true;
|
|
const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
|
|
if (media_packets_fec_.size() < ForwardErrorCorrection::kMaxMediaPackets) {
|
|
// Generic FEC can only protect up to kMaxMediaPackets packets.
|
|
ForwardErrorCorrection::Packet* packet = new ForwardErrorCorrection::Packet;
|
|
packet->length = payload_length + rtp_header_length;
|
|
memcpy(packet->data, data_buffer, packet->length);
|
|
media_packets_fec_.push_back(packet);
|
|
}
|
|
if (marker_bit) {
|
|
++num_frames_;
|
|
incomplete_frame_ = false;
|
|
}
|
|
// Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
|
|
// (1) the excess overhead (actual overhead - requested/target overhead) is
|
|
// less than |kMaxExcessOverhead|, and
|
|
// (2) at least |minimum_media_packets_fec_| media packets is reached.
|
|
if (!incomplete_frame_ &&
|
|
(num_frames_ == params_.max_fec_frames ||
|
|
(ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
|
|
assert(num_first_partition_ <=
|
|
static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets));
|
|
int ret = fec_->GenerateFEC(media_packets_fec_,
|
|
params_.fec_rate,
|
|
num_first_partition_,
|
|
params_.use_uep_protection,
|
|
params_.fec_mask_type,
|
|
&fec_packets_);
|
|
if (fec_packets_.empty()) {
|
|
num_frames_ = 0;
|
|
DeletePackets();
|
|
}
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Returns true if the excess overhead (actual - target) for the FEC is below
|
|
// the amount |kMaxExcessOverhead|. This effects the lower protection level
|
|
// cases and low number of media packets/frame. The target overhead is given by
|
|
// |params_.fec_rate|, and is only achievable in the limit of large number of
|
|
// media packets.
|
|
bool ProducerFec::ExcessOverheadBelowMax() {
|
|
return ((Overhead() - params_.fec_rate) < kMaxExcessOverhead);
|
|
}
|
|
|
|
// Returns true if the media packet list for the FEC is at least
|
|
// |minimum_media_packets_fec_|. This condition tries to capture the effect
|
|
// that, for the same amount of protection/overhead, longer codes
|
|
// (e.g. (2k,2m) vs (k,m)) are generally more effective at recovering losses.
|
|
bool ProducerFec::MinimumMediaPacketsReached() {
|
|
float avg_num_packets_frame = static_cast<float>(media_packets_fec_.size()) /
|
|
num_frames_;
|
|
if (avg_num_packets_frame < 2.0f) {
|
|
return (static_cast<int>(media_packets_fec_.size()) >=
|
|
minimum_media_packets_fec_);
|
|
} else {
|
|
// For larger rates (more packets/frame), increase the threshold.
|
|
return (static_cast<int>(media_packets_fec_.size()) >=
|
|
minimum_media_packets_fec_ + 1);
|
|
}
|
|
}
|
|
|
|
bool ProducerFec::FecAvailable() const {
|
|
return (fec_packets_.size() > 0);
|
|
}
|
|
|
|
RedPacket* ProducerFec::GetFecPacket(int red_pl_type,
|
|
int fec_pl_type,
|
|
uint16_t seq_num,
|
|
int rtp_header_length) {
|
|
if (fec_packets_.empty())
|
|
return NULL;
|
|
// Build FEC packet. The FEC packets in |fec_packets_| doesn't
|
|
// have RTP headers, so we're reusing the header from the last
|
|
// media packet.
|
|
ForwardErrorCorrection::Packet* packet_to_send = fec_packets_.front();
|
|
ForwardErrorCorrection::Packet* last_media_packet = media_packets_fec_.back();
|
|
RedPacket* return_packet = new RedPacket(packet_to_send->length +
|
|
kREDForFECHeaderLength +
|
|
rtp_header_length);
|
|
return_packet->CreateHeader(last_media_packet->data,
|
|
rtp_header_length,
|
|
red_pl_type,
|
|
fec_pl_type);
|
|
return_packet->SetSeqNum(seq_num);
|
|
return_packet->ClearMarkerBit();
|
|
return_packet->AssignPayload(packet_to_send->data, packet_to_send->length);
|
|
fec_packets_.pop_front();
|
|
if (fec_packets_.empty()) {
|
|
// Done with all the FEC packets. Reset for next run.
|
|
DeletePackets();
|
|
num_frames_ = 0;
|
|
}
|
|
return return_packet;
|
|
}
|
|
|
|
int ProducerFec::Overhead() const {
|
|
// Overhead is defined as relative to the number of media packets, and not
|
|
// relative to total number of packets. This definition is inhereted from the
|
|
// protection factor produced by video_coding module and how the FEC
|
|
// generation is implemented.
|
|
assert(!media_packets_fec_.empty());
|
|
int num_fec_packets = fec_->GetNumberOfFecPackets(media_packets_fec_.size(),
|
|
params_.fec_rate);
|
|
// Return the overhead in Q8.
|
|
return (num_fec_packets << 8) / media_packets_fec_.size();
|
|
}
|
|
|
|
void ProducerFec::DeletePackets() {
|
|
while (!media_packets_fec_.empty()) {
|
|
delete media_packets_fec_.front();
|
|
media_packets_fec_.pop_front();
|
|
}
|
|
assert(media_packets_fec_.empty());
|
|
}
|
|
|
|
} // namespace webrtc
|