blob: a571a0130b0369b5674c46ff9d161bb967f089e7 [file] [log] [blame]
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
pbos@webrtc.orga048d7c2013-05-29 14:27:38 +000011#include "webrtc/modules/rtp_rtcp/source/producer_fec.h"
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000012
pbos@webrtc.orga048d7c2013-05-29 14:27:38 +000013#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
14#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000015
16namespace webrtc {
17
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000018enum { kREDForFECHeaderLength = 1 };
marpan@webrtc.org747cd872012-05-22 16:50:00 +000019// This controls the maximum amount of excess overhead (actual - target)
20// allowed in order to trigger GenerateFEC(), before |params_.max_fec_frames|
21// is reached. Overhead here is defined as relative to number of media packets.
22enum { kMaxExcessOverhead = 50 }; // Q8.
23// This is the minimum number of media packets required (above some protection
24// level) in order to trigger GenerateFEC(), before |params_.max_fec_frames| is
25// reached.
26enum { kMinimumMediaPackets = 4 };
27// Threshold on the received FEC protection level, above which we enforce at
28// least |kMinimumMediaPackets| packets for the FEC code. Below this
29// threshold |kMinimumMediaPackets| is set to default value of 1.
30enum { kHighProtectionThreshold = 80 }; // Corresponds to ~30 overhead, range
31// is 0 to 255, where 255 corresponds to 100% overhead (relative to number of
32// media packets).
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000033
34struct RtpPacket {
pbos@webrtc.org2f446732013-04-08 11:08:41 +000035 uint16_t rtpHeaderLength;
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000036 ForwardErrorCorrection::Packet* pkt;
37};
38
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000039RedPacket::RedPacket(size_t length)
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000040 : data_(new uint8_t[length]),
41 length_(length),
42 header_length_(0) {
43}
44
45RedPacket::~RedPacket() {
46 delete [] data_;
47}
48
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000049void RedPacket::CreateHeader(const uint8_t* rtp_header, size_t header_length,
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000050 int red_pl_type, int pl_type) {
51 assert(header_length + kREDForFECHeaderLength <= length_);
52 memcpy(data_, rtp_header, header_length);
53 // Replace payload type.
54 data_[1] &= 0x80;
55 data_[1] += red_pl_type;
56 // Add RED header
57 // f-bit always 0
58 data_[header_length] = pl_type;
59 header_length_ = header_length + kREDForFECHeaderLength;
60}
61
62void RedPacket::SetSeqNum(int seq_num) {
63 assert(seq_num >= 0 && seq_num < (1<<16));
pbos@webrtc.org62bafae2014-07-08 12:10:51 +000064 RtpUtility::AssignUWord16ToBuffer(&data_[2], seq_num);
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000065}
66
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000067void RedPacket::AssignPayload(const uint8_t* payload, size_t length) {
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000068 assert(header_length_ + length <= length_);
69 memcpy(data_ + header_length_, payload, length);
70}
71
72void RedPacket::ClearMarkerBit() {
73 data_[1] &= 0x7F;
74}
75
76uint8_t* RedPacket::data() const {
77 return data_;
78}
79
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000080size_t RedPacket::length() const {
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000081 return length_;
82}
83
84ProducerFec::ProducerFec(ForwardErrorCorrection* fec)
85 : fec_(fec),
86 media_packets_fec_(),
87 fec_packets_(),
88 num_frames_(0),
89 incomplete_frame_(false),
90 num_first_partition_(0),
marpan@webrtc.org747cd872012-05-22 16:50:00 +000091 minimum_media_packets_fec_(1),
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000092 params_(),
93 new_params_() {
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000094 memset(&params_, 0, sizeof(params_));
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +000095 memset(&new_params_, 0, sizeof(new_params_));
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +000096}
97
98ProducerFec::~ProducerFec() {
99 DeletePackets();
100}
101
102void ProducerFec::SetFecParameters(const FecProtectionParams* params,
103 int num_first_partition) {
104 // Number of first partition packets cannot exceed kMaxMediaPackets
105 assert(params->fec_rate >= 0 && params->fec_rate < 256);
106 if (num_first_partition >
107 static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets)) {
108 num_first_partition =
109 ForwardErrorCorrection::kMaxMediaPackets;
110 }
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000111 // Store the new params and apply them for the next set of FEC packets being
112 // produced.
113 new_params_ = *params;
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000114 num_first_partition_ = num_first_partition;
marpan@webrtc.org747cd872012-05-22 16:50:00 +0000115 if (params->fec_rate > kHighProtectionThreshold) {
116 minimum_media_packets_fec_ = kMinimumMediaPackets;
117 } else {
118 minimum_media_packets_fec_ = 1;
119 }
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000120}
121
122RedPacket* ProducerFec::BuildRedPacket(const uint8_t* data_buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000123 size_t payload_length,
124 size_t rtp_header_length,
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000125 int red_pl_type) {
126 RedPacket* red_packet = new RedPacket(payload_length +
127 kREDForFECHeaderLength +
128 rtp_header_length);
129 int pl_type = data_buffer[1] & 0x7f;
130 red_packet->CreateHeader(data_buffer, rtp_header_length,
131 red_pl_type, pl_type);
132 red_packet->AssignPayload(data_buffer + rtp_header_length, payload_length);
133 return red_packet;
134}
135
136int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000137 size_t payload_length,
138 size_t rtp_header_length) {
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000139 assert(fec_packets_.empty());
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000140 if (media_packets_fec_.empty()) {
141 params_ = new_params_;
142 }
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000143 incomplete_frame_ = true;
144 const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000145 if (media_packets_fec_.size() < ForwardErrorCorrection::kMaxMediaPackets) {
stefan@webrtc.orgaf5ffd52012-03-23 16:01:15 +0000146 // Generic FEC can only protect up to kMaxMediaPackets packets.
147 ForwardErrorCorrection::Packet* packet = new ForwardErrorCorrection::Packet;
148 packet->length = payload_length + rtp_header_length;
149 memcpy(packet->data, data_buffer, packet->length);
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000150 media_packets_fec_.push_back(packet);
151 }
152 if (marker_bit) {
153 ++num_frames_;
154 incomplete_frame_ = false;
155 }
marpan@webrtc.org747cd872012-05-22 16:50:00 +0000156 // Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
157 // (1) the excess overhead (actual overhead - requested/target overhead) is
158 // less than |kMaxExcessOverhead|, and
159 // (2) at least |minimum_media_packets_fec_| media packets is reached.
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000160 if (!incomplete_frame_ &&
161 (num_frames_ == params_.max_fec_frames ||
marpan@webrtc.org747cd872012-05-22 16:50:00 +0000162 (ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000163 assert(num_first_partition_ <=
164 static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets));
165 int ret = fec_->GenerateFEC(media_packets_fec_,
166 params_.fec_rate,
167 num_first_partition_,
168 params_.use_uep_protection,
marpan@webrtc.org71707aa2012-07-13 16:27:51 +0000169 params_.fec_mask_type,
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000170 &fec_packets_);
171 if (fec_packets_.empty()) {
172 num_frames_ = 0;
173 DeletePackets();
174 }
175 return ret;
176 }
177 return 0;
178}
179
marpan@webrtc.org747cd872012-05-22 16:50:00 +0000180// Returns true if the excess overhead (actual - target) for the FEC is below
181// the amount |kMaxExcessOverhead|. This effects the lower protection level
182// cases and low number of media packets/frame. The target overhead is given by
183// |params_.fec_rate|, and is only achievable in the limit of large number of
184// media packets.
185bool ProducerFec::ExcessOverheadBelowMax() {
186 return ((Overhead() - params_.fec_rate) < kMaxExcessOverhead);
187}
188
189// Returns true if the media packet list for the FEC is at least
190// |minimum_media_packets_fec_|. This condition tries to capture the effect
191// that, for the same amount of protection/overhead, longer codes
192// (e.g. (2k,2m) vs (k,m)) are generally more effective at recovering losses.
193bool ProducerFec::MinimumMediaPacketsReached() {
marpan@webrtc.org48891202012-08-03 17:33:13 +0000194 float avg_num_packets_frame = static_cast<float>(media_packets_fec_.size()) /
195 num_frames_;
marpan@webrtc.org747cd872012-05-22 16:50:00 +0000196 if (avg_num_packets_frame < 2.0f) {
197 return (static_cast<int>(media_packets_fec_.size()) >=
198 minimum_media_packets_fec_);
199 } else {
200 // For larger rates (more packets/frame), increase the threshold.
201 return (static_cast<int>(media_packets_fec_.size()) >=
202 minimum_media_packets_fec_ + 1);
203 }
204}
205
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000206bool ProducerFec::FecAvailable() const {
207 return (fec_packets_.size() > 0);
208}
209
marpan@webrtc.org8639fd92012-07-30 18:17:02 +0000210RedPacket* ProducerFec::GetFecPacket(int red_pl_type,
211 int fec_pl_type,
212 uint16_t seq_num,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000213 size_t rtp_header_length) {
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000214 if (fec_packets_.empty())
215 return NULL;
216 // Build FEC packet. The FEC packets in |fec_packets_| doesn't
217 // have RTP headers, so we're reusing the header from the last
218 // media packet.
219 ForwardErrorCorrection::Packet* packet_to_send = fec_packets_.front();
220 ForwardErrorCorrection::Packet* last_media_packet = media_packets_fec_.back();
221 RedPacket* return_packet = new RedPacket(packet_to_send->length +
222 kREDForFECHeaderLength +
marpan@webrtc.org8639fd92012-07-30 18:17:02 +0000223 rtp_header_length);
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000224 return_packet->CreateHeader(last_media_packet->data,
marpan@webrtc.org8639fd92012-07-30 18:17:02 +0000225 rtp_header_length,
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000226 red_pl_type,
227 fec_pl_type);
228 return_packet->SetSeqNum(seq_num);
229 return_packet->ClearMarkerBit();
230 return_packet->AssignPayload(packet_to_send->data, packet_to_send->length);
231 fec_packets_.pop_front();
232 if (fec_packets_.empty()) {
233 // Done with all the FEC packets. Reset for next run.
234 DeletePackets();
235 num_frames_ = 0;
236 }
237 return return_packet;
238}
239
240int ProducerFec::Overhead() const {
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000241 // Overhead is defined as relative to the number of media packets, and not
242 // relative to total number of packets. This definition is inhereted from the
243 // protection factor produced by video_coding module and how the FEC
244 // generation is implemented.
245 assert(!media_packets_fec_.empty());
marpan@webrtc.org747cd872012-05-22 16:50:00 +0000246 int num_fec_packets = fec_->GetNumberOfFecPackets(media_packets_fec_.size(),
247 params_.fec_rate);
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000248 // Return the overhead in Q8.
stefan@webrtc.orgc35f5ce2012-04-11 07:42:25 +0000249 return (num_fec_packets << 8) / media_packets_fec_.size();
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +0000250}
251
252void ProducerFec::DeletePackets() {
253 while (!media_packets_fec_.empty()) {
254 delete media_packets_fec_.front();
255 media_packets_fec_.pop_front();
256 }
257 assert(media_packets_fec_.empty());
258}
259
260} // namespace webrtc