ulpfec_generator.cc (10248B)
1 /* 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 11 #include "modules/rtp_rtcp/source/ulpfec_generator.h" 12 13 #include <cstdint> 14 #include <cstring> 15 #include <memory> 16 #include <utility> 17 #include <vector> 18 19 #include "api/environment/environment.h" 20 #include "api/units/data_rate.h" 21 #include "api/units/time_delta.h" 22 #include "modules/include/module_fec_types.h" 23 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" 24 #include "modules/rtp_rtcp/source/forward_error_correction.h" 25 #include "modules/rtp_rtcp/source/forward_error_correction_internal.h" 26 #include "rtc_base/checks.h" 27 #include "rtc_base/race_checker.h" 28 #include "rtc_base/synchronization/mutex.h" 29 30 namespace webrtc { 31 32 namespace { 33 34 constexpr size_t kRedForFecHeaderLength = 1; 35 36 // This controls the maximum amount of excess overhead (actual - target) 37 // allowed in order to trigger EncodeFec(), before `params_.max_fec_frames` 38 // is reached. Overhead here is defined as relative to number of media packets. 39 constexpr int kMaxExcessOverhead = 50; // Q8. 40 41 // This is the minimum number of media packets required (above some protection 42 // level) in order to trigger EncodeFec(), before `params_.max_fec_frames` is 43 // reached. 44 constexpr size_t kMinMediaPackets = 4; 45 46 // Threshold on the received FEC protection level, above which we enforce at 47 // least `kMinMediaPackets` packets for the FEC code. Below this 48 // threshold `kMinMediaPackets` is set to default value of 1. 49 // 50 // The range is between 0 and 255, where 255 corresponds to 100% overhead 51 // (relative to the number of protected media packets). 52 constexpr uint8_t kHighProtectionThreshold = 80; 53 54 // This threshold is used to adapt the `kMinMediaPackets` threshold, based 55 // on the average number of packets per frame seen so far. When there are few 56 // packets per frame (as given by this threshold), at least 57 // `kMinMediaPackets` + 1 packets are sent to the FEC code. 58 constexpr float kMinMediaPacketsAdaptationThreshold = 2.0f; 59 60 // At construction time, we don't know the SSRC that is used for the generated 61 // FEC packets, but we still need to give it to the ForwardErrorCorrection ctor 62 // to be used in the decoding. 63 // TODO(brandtr): Get rid of this awkwardness by splitting 64 // ForwardErrorCorrection in two objects -- one encoder and one decoder. 65 constexpr uint32_t kUnknownSsrc = 0; 66 67 } // namespace 68 69 UlpfecGenerator::Params::Params() = default; 70 UlpfecGenerator::Params::Params(FecProtectionParams delta_params, 71 FecProtectionParams keyframe_params) 72 : delta_params(delta_params), keyframe_params(keyframe_params) {} 73 74 UlpfecGenerator::UlpfecGenerator(const Environment& env, 75 int red_payload_type, 76 int ulpfec_payload_type) 77 : env_(env), 78 red_payload_type_(red_payload_type), 79 ulpfec_payload_type_(ulpfec_payload_type), 80 fec_(ForwardErrorCorrection::CreateUlpfec(kUnknownSsrc)), 81 num_protected_frames_(0), 82 min_num_media_packets_(1), 83 media_contains_keyframe_(false), 84 fec_bitrate_(/*max_window_size=*/TimeDelta::Seconds(1)) {} 85 86 // Used by FlexFecSender, payload types are unused. 87 UlpfecGenerator::UlpfecGenerator(const Environment& env, 88 std::unique_ptr<ForwardErrorCorrection> fec) 89 : env_(env), 90 red_payload_type_(0), 91 ulpfec_payload_type_(0), 92 fec_(std::move(fec)), 93 num_protected_frames_(0), 94 min_num_media_packets_(1), 95 media_contains_keyframe_(false), 96 fec_bitrate_(/*max_window_size=*/TimeDelta::Seconds(1)) {} 97 98 UlpfecGenerator::~UlpfecGenerator() = default; 99 100 void UlpfecGenerator::SetProtectionParameters( 101 const FecProtectionParams& delta_params, 102 const FecProtectionParams& key_params) { 103 RTC_DCHECK_GE(delta_params.fec_rate, 0); 104 RTC_DCHECK_LE(delta_params.fec_rate, 255); 105 RTC_DCHECK_GE(key_params.fec_rate, 0); 106 RTC_DCHECK_LE(key_params.fec_rate, 255); 107 // Store the new params and apply them for the next set of FEC packets being 108 // produced. 109 MutexLock lock(&mutex_); 110 pending_params_.emplace(delta_params, key_params); 111 } 112 113 void UlpfecGenerator::AddPacketAndGenerateFec(const RtpPacketToSend& packet) { 114 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 115 RTC_DCHECK(generated_fec_packets_.empty()); 116 117 { 118 MutexLock lock(&mutex_); 119 if (pending_params_) { 120 current_params_ = *pending_params_; 121 pending_params_.reset(); 122 123 if (CurrentParams().fec_rate > kHighProtectionThreshold) { 124 min_num_media_packets_ = kMinMediaPackets; 125 } else { 126 min_num_media_packets_ = 1; 127 } 128 } 129 } 130 131 if (packet.is_key_frame()) { 132 media_contains_keyframe_ = true; 133 } 134 const bool complete_frame = packet.Marker(); 135 if (media_packets_.size() < kUlpfecMaxMediaPackets) { 136 // Our packet masks can only protect up to `kUlpfecMaxMediaPackets` packets. 137 auto fec_packet = std::make_unique<ForwardErrorCorrection::Packet>(); 138 fec_packet->data = packet.Buffer(); 139 media_packets_.push_back(std::move(fec_packet)); 140 141 // Keep a copy of the last RTP packet, so we can copy the RTP header 142 // from it when creating newly generated ULPFEC+RED packets. 143 RTC_DCHECK_GE(packet.headers_size(), kRtpHeaderSize); 144 last_media_packet_ = packet; 145 } 146 147 if (complete_frame) { 148 ++num_protected_frames_; 149 } 150 151 auto params = CurrentParams(); 152 153 // Produce FEC over at most `params_.max_fec_frames` frames, or as soon as: 154 // (1) the excess overhead (actual overhead - requested/target overhead) is 155 // less than `kMaxExcessOverhead`, and 156 // (2) at least `min_num_media_packets_` media packets is reached. 157 if (complete_frame && 158 (num_protected_frames_ >= params.max_fec_frames || 159 (ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) { 160 // We are not using Unequal Protection feature of the parity erasure code. 161 constexpr int kNumImportantPackets = 0; 162 constexpr bool kUseUnequalProtection = false; 163 fec_->EncodeFec(media_packets_, params.fec_rate, kNumImportantPackets, 164 kUseUnequalProtection, params.fec_mask_type, 165 &generated_fec_packets_); 166 if (generated_fec_packets_.empty()) { 167 ResetState(); 168 } 169 } 170 } 171 172 bool UlpfecGenerator::ExcessOverheadBelowMax() const { 173 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 174 175 return ((Overhead() - CurrentParams().fec_rate) < kMaxExcessOverhead); 176 } 177 178 bool UlpfecGenerator::MinimumMediaPacketsReached() const { 179 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 180 float average_num_packets_per_frame = 181 static_cast<float>(media_packets_.size()) / num_protected_frames_; 182 int num_media_packets = static_cast<int>(media_packets_.size()); 183 if (average_num_packets_per_frame < kMinMediaPacketsAdaptationThreshold) { 184 return num_media_packets >= min_num_media_packets_; 185 } else { 186 // For larger rates (more packets/frame), increase the threshold. 187 // TODO(brandtr): Investigate what impact this adaptation has. 188 return num_media_packets >= min_num_media_packets_ + 1; 189 } 190 } 191 192 const FecProtectionParams& UlpfecGenerator::CurrentParams() const { 193 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 194 return media_contains_keyframe_ ? current_params_.keyframe_params 195 : current_params_.delta_params; 196 } 197 198 size_t UlpfecGenerator::MaxPacketOverhead() const { 199 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 200 return fec_->MaxPacketOverhead(); 201 } 202 203 std::vector<std::unique_ptr<RtpPacketToSend>> UlpfecGenerator::GetFecPackets() { 204 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 205 if (generated_fec_packets_.empty()) { 206 return std::vector<std::unique_ptr<RtpPacketToSend>>(); 207 } 208 209 // Wrap FEC packet (including FEC headers) in a RED packet. Since the 210 // FEC packets in `generated_fec_packets_` don't have RTP headers, we 211 // reuse the header from the last media packet. 212 RTC_CHECK(last_media_packet_.has_value()); 213 last_media_packet_->SetPayloadSize(0); 214 215 std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets; 216 fec_packets.reserve(generated_fec_packets_.size()); 217 218 size_t total_fec_size_bytes = 0; 219 for (const auto* fec_packet : generated_fec_packets_) { 220 std::unique_ptr<RtpPacketToSend> red_packet = 221 std::make_unique<RtpPacketToSend>(*last_media_packet_); 222 red_packet->SetPayloadType(red_payload_type_); 223 red_packet->SetMarker(false); 224 uint8_t* payload_buffer = red_packet->SetPayloadSize( 225 kRedForFecHeaderLength + fec_packet->data.size()); 226 // Primary RED header with F bit unset. 227 // See https://tools.ietf.org/html/rfc2198#section-3 228 payload_buffer[0] = ulpfec_payload_type_; // RED header. 229 memcpy(&payload_buffer[1], fec_packet->data.data(), 230 fec_packet->data.size()); 231 total_fec_size_bytes += red_packet->size(); 232 red_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); 233 red_packet->set_allow_retransmission(false); 234 red_packet->set_is_red(true); 235 red_packet->set_fec_protect_packet(false); 236 fec_packets.push_back(std::move(red_packet)); 237 } 238 239 ResetState(); 240 241 MutexLock lock(&mutex_); 242 fec_bitrate_.Update(total_fec_size_bytes, env_.clock().CurrentTime()); 243 244 return fec_packets; 245 } 246 247 DataRate UlpfecGenerator::CurrentFecRate() const { 248 MutexLock lock(&mutex_); 249 return fec_bitrate_.Rate(env_.clock().CurrentTime()) 250 .value_or(DataRate::Zero()); 251 } 252 253 int UlpfecGenerator::Overhead() const { 254 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 255 RTC_DCHECK(!media_packets_.empty()); 256 int num_fec_packets = ForwardErrorCorrection::NumFecPackets( 257 media_packets_.size(), CurrentParams().fec_rate); 258 259 // Return the overhead in Q8. 260 return (num_fec_packets << 8) / media_packets_.size(); 261 } 262 263 void UlpfecGenerator::ResetState() { 264 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); 265 media_packets_.clear(); 266 last_media_packet_.reset(); 267 generated_fec_packets_.clear(); 268 num_protected_frames_ = 0; 269 media_contains_keyframe_ = false; 270 } 271 272 } // namespace webrtc