/* * 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/byte_io.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(size_t length) : data_(new uint8_t[length]), length_(length), header_length_(0) { } RedPacket::~RedPacket() { delete [] data_; } void RedPacket::CreateHeader(const uint8_t* rtp_header, size_t 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] = static_cast(pl_type); header_length_ = header_length + kREDForFECHeaderLength; } void RedPacket::SetSeqNum(int seq_num) { assert(seq_num >= 0 && seq_num < (1<<16)); ByteWriter::WriteBigEndian(&data_[2], seq_num); } void RedPacket::AssignPayload(const uint8_t* payload, size_t 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_; } size_t RedPacket::length() const { return length_; } ProducerFec::ProducerFec(ForwardErrorCorrection* fec) : fec_(fec), media_packets_fec_(), fec_packets_(), num_frames_(0), 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(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, size_t payload_length, size_t 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, size_t payload_length, size_t rtp_header_length) { assert(fec_packets_.empty()); if (media_packets_fec_.empty()) { params_ = new_params_; } bool complete_frame = false; 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_; complete_frame = true; } // 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 (complete_frame && (num_frames_ == params_.max_fec_frames || (ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) { assert(num_first_partition_ <= static_cast(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(media_packets_fec_.size()) / num_frames_; if (avg_num_packets_frame < 2.0f) { return (static_cast(media_packets_fec_.size()) >= minimum_media_packets_fec_); } else { // For larger rates (more packets/frame), increase the threshold. return (static_cast(media_packets_fec_.size()) >= minimum_media_packets_fec_ + 1); } } bool ProducerFec::FecAvailable() const { return !fec_packets_.empty(); } size_t ProducerFec::NumAvailableFecPackets() const { return fec_packets_.size(); } std::vector ProducerFec::GetFecPackets(int red_pl_type, int fec_pl_type, uint16_t first_seq_num, size_t rtp_header_length) { std::vector fec_packets; fec_packets.reserve(fec_packets_.size()); uint16_t sequence_number = first_seq_num; while (!fec_packets_.empty()) { // 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* red_packet = new RedPacket( packet_to_send->length + kREDForFECHeaderLength + rtp_header_length); red_packet->CreateHeader(last_media_packet->data, rtp_header_length, red_pl_type, fec_pl_type); red_packet->SetSeqNum(sequence_number++); red_packet->ClearMarkerBit(); red_packet->AssignPayload(packet_to_send->data, packet_to_send->length); fec_packets.push_back(red_packet); fec_packets_.pop_front(); } DeletePackets(); num_frames_ = 0; return fec_packets; } 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