blob: ba93442f391764680ae6f5e1b00c649ea6a5e94a [file] [log] [blame]
/*
* Copyright (c) 2016 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/video_coding/protection_bitrate_calculator.h"
namespace webrtc {
using rtc::CritScope;
struct ProtectionBitrateCalculator::EncodedFrameSample {
EncodedFrameSample(size_t size_bytes,
uint32_t timestamp,
int64_t time_complete_ms)
: size_bytes(size_bytes),
timestamp(timestamp),
time_complete_ms(time_complete_ms) {}
size_t size_bytes;
uint32_t timestamp;
int64_t time_complete_ms;
};
ProtectionBitrateCalculator::ProtectionBitrateCalculator(
Clock* clock,
VCMProtectionCallback* protection_callback)
: clock_(clock),
protection_callback_(protection_callback),
loss_prot_logic_(new media_optimization::VCMLossProtectionLogic(
clock_->TimeInMilliseconds())),
max_payload_size_(1460) {}
ProtectionBitrateCalculator::~ProtectionBitrateCalculator(void) {
loss_prot_logic_->Release();
}
void ProtectionBitrateCalculator::SetEncodingData(uint32_t target_bitrate,
uint16_t width,
uint16_t height,
uint32_t frame_rate,
size_t num_temporal_layers,
size_t max_payload_size) {
CritScope lock(&crit_sect_);
// Everything codec specific should be reset here since this means the codec
// has changed.
float target_bitrate_kbps = static_cast<float>(target_bitrate) / 1000.0f;
loss_prot_logic_->UpdateBitRate(target_bitrate_kbps);
loss_prot_logic_->UpdateFrameRate(static_cast<float>(frame_rate));
loss_prot_logic_->UpdateFrameSize(width, height);
loss_prot_logic_->UpdateNumLayers(num_temporal_layers);
max_payload_size_ = max_payload_size;
}
uint32_t ProtectionBitrateCalculator::SetTargetRates(
uint32_t estimated_bitrate_bps,
int actual_framerate_fps,
uint8_t fraction_lost,
int64_t round_trip_time_ms) {
float target_bitrate_kbps =
static_cast<float>(estimated_bitrate_bps) / 1000.0f;
// Sanity check.
if (actual_framerate_fps < 1.0) {
actual_framerate_fps = 1.0;
}
FecProtectionParams delta_fec_params;
FecProtectionParams key_fec_params;
{
CritScope lock(&crit_sect_);
loss_prot_logic_->UpdateBitRate(target_bitrate_kbps);
loss_prot_logic_->UpdateRtt(round_trip_time_ms);
// Update frame rate for the loss protection logic class: frame rate should
// be the actual/sent rate.
loss_prot_logic_->UpdateFrameRate(actual_framerate_fps);
// Returns the filtered packet loss, used for the protection setting.
// The filtered loss may be the received loss (no filter), or some
// filtered value (average or max window filter).
// Use max window filter for now.
media_optimization::FilterPacketLossMode filter_mode =
media_optimization::kMaxFilter;
uint8_t packet_loss_enc = loss_prot_logic_->FilteredLoss(
clock_->TimeInMilliseconds(), filter_mode, fraction_lost);
// For now use the filtered loss for computing the robustness settings.
loss_prot_logic_->UpdateFilteredLossPr(packet_loss_enc);
if (loss_prot_logic_->SelectedType() == media_optimization::kNone) {
return estimated_bitrate_bps;
}
// Update method will compute the robustness settings for the given
// protection method and the overhead cost
// the protection method is set by the user via SetVideoProtection.
loss_prot_logic_->UpdateMethod();
// Get the bit cost of protection method, based on the amount of
// overhead data actually transmitted (including headers) the last
// second.
// Get the FEC code rate for Key frames (set to 0 when NA).
key_fec_params.fec_rate =
loss_prot_logic_->SelectedMethod()->RequiredProtectionFactorK();
// Get the FEC code rate for Delta frames (set to 0 when NA).
delta_fec_params.fec_rate =
loss_prot_logic_->SelectedMethod()->RequiredProtectionFactorD();
// The RTP module currently requires the same |max_fec_frames| for both
// key and delta frames.
delta_fec_params.max_fec_frames =
loss_prot_logic_->SelectedMethod()->MaxFramesFec();
key_fec_params.max_fec_frames =
loss_prot_logic_->SelectedMethod()->MaxFramesFec();
}
// Set the FEC packet mask type. |kFecMaskBursty| is more effective for
// consecutive losses and little/no packet re-ordering. As we currently
// do not have feedback data on the degree of correlated losses and packet
// re-ordering, we keep default setting to |kFecMaskRandom| for now.
delta_fec_params.fec_mask_type = kFecMaskRandom;
key_fec_params.fec_mask_type = kFecMaskRandom;
// Update protection callback with protection settings.
uint32_t sent_video_rate_bps = 0;
uint32_t sent_nack_rate_bps = 0;
uint32_t sent_fec_rate_bps = 0;
// Rate cost of the protection methods.
float protection_overhead_rate = 0.0f;
// TODO(Marco): Pass FEC protection values per layer.
protection_callback_->ProtectionRequest(
&delta_fec_params, &key_fec_params, &sent_video_rate_bps,
&sent_nack_rate_bps, &sent_fec_rate_bps);
uint32_t sent_total_rate_bps =
sent_video_rate_bps + sent_nack_rate_bps + sent_fec_rate_bps;
// Estimate the overhead costs of the next second as staying the same
// wrt the source bitrate.
if (sent_total_rate_bps > 0) {
protection_overhead_rate =
static_cast<float>(sent_nack_rate_bps + sent_fec_rate_bps) /
sent_total_rate_bps;
}
// Cap the overhead estimate to 50%.
if (protection_overhead_rate > 0.5)
protection_overhead_rate = 0.5;
// Source coding rate: total rate - protection overhead.
return estimated_bitrate_bps * (1.0 - protection_overhead_rate);
}
void ProtectionBitrateCalculator::SetProtectionMethod(bool enable_fec,
bool enable_nack) {
media_optimization::VCMProtectionMethodEnum method(media_optimization::kNone);
if (enable_fec && enable_nack) {
method = media_optimization::kNackFec;
} else if (enable_nack) {
method = media_optimization::kNack;
} else if (enable_fec) {
method = media_optimization::kFec;
}
CritScope lock(&crit_sect_);
loss_prot_logic_->SetMethod(method);
}
void ProtectionBitrateCalculator::UpdateWithEncodedData(
const EncodedImage& encoded_image) {
const size_t encoded_length = encoded_image._length;
CritScope lock(&crit_sect_);
if (encoded_length > 0) {
const bool delta_frame = encoded_image._frameType != kVideoFrameKey;
if (max_payload_size_ > 0 && encoded_length > 0) {
const float min_packets_per_frame =
encoded_length / static_cast<float>(max_payload_size_);
if (delta_frame) {
loss_prot_logic_->UpdatePacketsPerFrame(min_packets_per_frame,
clock_->TimeInMilliseconds());
} else {
loss_prot_logic_->UpdatePacketsPerFrameKey(
min_packets_per_frame, clock_->TimeInMilliseconds());
}
}
if (!delta_frame && encoded_length > 0) {
loss_prot_logic_->UpdateKeyFrameSize(static_cast<float>(encoded_length));
}
}
}
} // namespace webrtc