Auto pause video streams based on encoder target bitrate.

This CL changes the auto-pause logic to suspend a stream based on the
encoder target bitrate instead of the allocated bitrate for a stream,
to account for possible protection, e.g. FEC and NACK.

This CL also adds periodic logging of the current BWE and possibility
to run with suspension in video loopback test.

BUG=webrtc:5868
R=stefan@webrtc.org

Review URL: https://codereview.webrtc.org/2117493002 .

Cr-Commit-Position: refs/heads/master@{#13360}
This commit is contained in:
mflodman 2016-07-01 13:03:59 +02:00
parent 6907d04f90
commit 48a4beb7a4
10 changed files with 386 additions and 183 deletions

View File

@ -15,7 +15,10 @@
#include <utility>
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
#include "webrtc/system_wrappers/include/clock.h"
#include "webrtc/system_wrappers/include/metrics.h"
namespace webrtc {
@ -28,13 +31,35 @@ const int kDefaultBitrateBps = 300000;
const double kToggleFactor = 0.1;
const uint32_t kMinToggleBitrateBps = 20000;
const int64_t kBweLogIntervalMs = 5000;
namespace {
double MediaRatio(uint32_t allocated_bitrate, uint32_t protection_bitrate) {
RTC_DCHECK_GT(allocated_bitrate, 0u);
if (protection_bitrate == 0)
return 1.0;
uint32_t media_bitrate = allocated_bitrate - protection_bitrate;
return media_bitrate / static_cast<double>(allocated_bitrate);
}
} // namespace
BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
: limit_observer_(limit_observer),
bitrate_observer_configs_(),
last_bitrate_bps_(kDefaultBitrateBps),
last_non_zero_bitrate_bps_(kDefaultBitrateBps),
last_fraction_loss_(0),
last_rtt_(0) {}
last_rtt_(0),
num_pause_events_(0),
clock_(Clock::GetRealTimeClock()),
last_bwe_log_time_(0) {}
BitrateAllocator::~BitrateAllocator() {
RTC_LOGGED_HISTOGRAM_COUNTS_100("WebRTC.Call.NumberOfPauseEvents",
num_pause_events_);
}
void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps,
uint8_t fraction_loss,
@ -46,11 +71,45 @@ void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps,
last_fraction_loss_ = fraction_loss;
last_rtt_ = rtt;
ObserverAllocation allocation = AllocateBitrates(target_bitrate_bps);
for (const auto& kv : allocation) {
kv.first->OnBitrateUpdated(kv.second, last_fraction_loss_, last_rtt_);
// Periodically log the incoming BWE.
int64_t now = clock_->TimeInMilliseconds();
if (now > last_bwe_log_time_ + kBweLogIntervalMs) {
LOG(LS_INFO) << "Current BWE " << target_bitrate_bps;
last_bwe_log_time_ = now;
}
ObserverAllocation allocation = AllocateBitrates(target_bitrate_bps);
for (auto& config : bitrate_observer_configs_) {
uint32_t allocated_bitrate = allocation[config.observer];
uint32_t protection_bitrate = config.observer->OnBitrateUpdated(
allocated_bitrate, last_fraction_loss_, last_rtt_);
if (allocated_bitrate == 0 && config.allocated_bitrate_bps > 0) {
if (target_bitrate_bps > 0)
++num_pause_events_;
// The protection bitrate is an estimate based on the ratio between media
// and protection used before this observer was muted.
uint32_t predicted_protection_bps =
(1.0 - config.media_ratio) * config.min_bitrate_bps;
LOG(LS_INFO) << "Pausing observer " << config.observer
<< " with configured min bitrate " << config.min_bitrate_bps
<< " and current estimate of " << target_bitrate_bps
<< " and protection bitrate " << predicted_protection_bps;
} else if (allocated_bitrate > 0 && config.allocated_bitrate_bps == 0) {
if (target_bitrate_bps > 0)
++num_pause_events_;
LOG(LS_INFO) << "Resuming observer " << config.observer
<< ", configured min bitrate " << config.min_bitrate_bps
<< ", current allocation " << allocated_bitrate
<< " and protection bitrate " << protection_bitrate;
}
// Only update the media ratio if the observer got an allocation.
if (allocated_bitrate > 0)
config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate);
config.allocated_bitrate_bps = allocated_bitrate;
}
last_allocation_ = allocation;
}
void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
@ -77,8 +136,14 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
if (last_bitrate_bps_ > 0) {
// Calculate a new allocation and update all observers.
allocation = AllocateBitrates(last_bitrate_bps_);
for (const auto& kv : allocation)
kv.first->OnBitrateUpdated(kv.second, last_fraction_loss_, last_rtt_);
for (auto& config : bitrate_observer_configs_) {
uint32_t allocated_bitrate = allocation[config.observer];
uint32_t protection_bitrate = config.observer->OnBitrateUpdated(
allocated_bitrate, last_fraction_loss_, last_rtt_);
config.allocated_bitrate_bps = allocated_bitrate;
if (allocated_bitrate > 0)
config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate);
}
} else {
// Currently, an encoder is not allowed to produce frames.
// But we still have to return the initial config bitrate + let the
@ -87,8 +152,6 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
observer->OnBitrateUpdated(0, last_fraction_loss_, last_rtt_);
}
UpdateAllocationLimits();
last_allocation_ = allocation;
}
void BitrateAllocator::UpdateAllocationLimits() {
@ -122,17 +185,22 @@ void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) {
int BitrateAllocator::GetStartBitrate(BitrateAllocatorObserver* observer) {
rtc::CritScope lock(&crit_sect_);
const auto& it = last_allocation_.find(observer);
if (it != last_allocation_.end())
return it->second;
// This is a new observer that has not yet been started. Assume that if it is
// added, all observers would split the available bitrate evenly.
return last_non_zero_bitrate_bps_ /
static_cast<int>((bitrate_observer_configs_.size() + 1));
const auto& it = FindObserverConfig(observer);
if (it == bitrate_observer_configs_.end()) {
// This observer hasn't been added yet, just give it its fair share.
return last_non_zero_bitrate_bps_ /
static_cast<int>((bitrate_observer_configs_.size() + 1));
} else if (it->allocated_bitrate_bps == -1) {
// This observer hasn't received an allocation yet, so do the same.
return last_non_zero_bitrate_bps_ /
static_cast<int>(bitrate_observer_configs_.size());
} else {
// This observer already has an allocation.
return it->allocated_bitrate_bps;
}
}
BitrateAllocator::ObserverConfigList::iterator
BitrateAllocator::ObserverConfigs::iterator
BitrateAllocator::FindObserverConfig(
const BitrateAllocatorObserver* observer) {
for (auto it = bitrate_observer_configs_.begin();
@ -202,9 +270,10 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
LastAllocatedBitrate(observer_config) == 0)
continue;
if (remaining_bitrate >= observer_config.min_bitrate_bps) {
allocation[observer_config.observer] = observer_config.min_bitrate_bps;
remaining_bitrate -= observer_config.min_bitrate_bps;
uint32_t required_bitrate = MinBitrateWithHysteresis(observer_config);
if (remaining_bitrate >= required_bitrate) {
allocation[observer_config.observer] = required_bitrate;
remaining_bitrate -= required_bitrate;
}
}
}
@ -263,14 +332,11 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::MaxRateAllocation(
uint32_t BitrateAllocator::LastAllocatedBitrate(
const ObserverConfig& observer_config) {
const auto& it = last_allocation_.find(observer_config.observer);
if (it != last_allocation_.end())
return it->second;
// Return the configured minimum bitrate for newly added observers, to avoid
// requiring an extra high bitrate for the observer to get an allocated
// bitrate.
return observer_config.min_bitrate_bps;
return observer_config.allocated_bitrate_bps == -1 ?
observer_config.min_bitrate_bps : observer_config.allocated_bitrate_bps;
}
uint32_t BitrateAllocator::MinBitrateWithHysteresis(
@ -280,6 +346,15 @@ uint32_t BitrateAllocator::MinBitrateWithHysteresis(
min_bitrate += std::max(static_cast<uint32_t>(kToggleFactor * min_bitrate),
kMinToggleBitrateBps);
}
// Account for protection bitrate used by this observer in the previous
// allocation.
// Note: the ratio will only be updated when the stream is active, meaning a
// paused stream won't get any ratio updates. This might lead to waiting a bit
// longer than necessary if the network condition improves, but this is to
// avoid too much toggling.
if (observer_config.media_ratio > 0.0 && observer_config.media_ratio < 1.0)
min_bitrate += min_bitrate * (1.0 - observer_config.media_ratio);
return min_bitrate;
}

View File

@ -13,25 +13,28 @@
#include <stdint.h>
#include <list>
#include <map>
#include <utility>
#include <vector>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/thread_annotations.h"
namespace webrtc {
class Clock;
// Used by all send streams with adaptive bitrate, to get the currently
// allocated bitrate for the send stream. The current network properties are
// given at the same time, to let the send stream decide about possible loss
// protection.
class BitrateAllocatorObserver {
public:
virtual void OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) = 0;
// Returns the amount of protection used by the BitrateAllocatorObserver
// implementation, as bitrate in bps.
virtual uint32_t OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) = 0;
protected:
virtual ~BitrateAllocatorObserver() {}
};
@ -54,6 +57,7 @@ class BitrateAllocator {
};
explicit BitrateAllocator(LimitObserver* limit_observer);
~BitrateAllocator();
// Allocate target_bitrate across the registered BitrateAllocatorObservers.
void OnNetworkChanged(uint32_t target_bitrate_bps,
@ -98,20 +102,25 @@ class BitrateAllocator {
min_bitrate_bps(min_bitrate_bps),
max_bitrate_bps(max_bitrate_bps),
pad_up_bitrate_bps(pad_up_bitrate_bps),
enforce_min_bitrate(enforce_min_bitrate) {}
BitrateAllocatorObserver* const observer;
enforce_min_bitrate(enforce_min_bitrate),
allocated_bitrate_bps(-1),
media_ratio(1.0) {}
BitrateAllocatorObserver* observer;
uint32_t min_bitrate_bps;
uint32_t max_bitrate_bps;
uint32_t pad_up_bitrate_bps;
bool enforce_min_bitrate;
int64_t allocated_bitrate_bps;
double media_ratio; // Part of the total bitrate used for media [0.0, 1.0].
};
// Calculates the minimum requested send bitrate and max padding bitrate and
// calls LimitObserver::OnAllocationLimitsChanged.
void UpdateAllocationLimits();
typedef std::list<ObserverConfig> ObserverConfigList;
ObserverConfigList::iterator FindObserverConfig(
typedef std::vector<ObserverConfig> ObserverConfigs;
ObserverConfigs::iterator FindObserverConfig(
const BitrateAllocatorObserver* observer)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
@ -153,12 +162,15 @@ class BitrateAllocator {
rtc::CriticalSection crit_sect_;
// Stored in a list to keep track of the insertion order.
ObserverConfigList bitrate_observer_configs_ GUARDED_BY(crit_sect_);
ObserverConfigs bitrate_observer_configs_ GUARDED_BY(crit_sect_);
uint32_t last_bitrate_bps_ GUARDED_BY(crit_sect_);
uint32_t last_non_zero_bitrate_bps_ GUARDED_BY(crit_sect_);
uint8_t last_fraction_loss_ GUARDED_BY(crit_sect_);
int64_t last_rtt_ GUARDED_BY(crit_sect_);
ObserverAllocation last_allocation_ GUARDED_BY(crit_sect_);
// Number of mute events based on too low BWE, not network up/down.
int num_pause_events_ GUARDED_BY(crit_sect_);
Clock* const clock_;
int64_t last_bwe_log_time_;
};
} // namespace webrtc
#endif // WEBRTC_CALL_BITRATE_ALLOCATOR_H_

View File

@ -31,18 +31,27 @@ class MockLimitObserver : public BitrateAllocator::LimitObserver {
class TestBitrateObserver : public BitrateAllocatorObserver {
public:
TestBitrateObserver()
: last_bitrate_bps_(0), last_fraction_loss_(0), last_rtt_ms_(0) {}
: last_bitrate_bps_(0),
last_fraction_loss_(0),
last_rtt_ms_(0),
protection_ratio_(0.0) {}
void OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) override {
void SetBitrateProtectionRatio(double protection_ratio) {
protection_ratio_ = protection_ratio;
}
uint32_t OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) override {
last_bitrate_bps_ = bitrate_bps;
last_fraction_loss_ = fraction_loss;
last_rtt_ms_ = rtt;
return bitrate_bps * protection_ratio_;
}
uint32_t last_bitrate_bps_;
uint8_t last_fraction_loss_;
int64_t last_rtt_ms_;
double protection_ratio_;
};
class BitrateAllocatorTest : public ::testing::Test {
@ -95,44 +104,44 @@ TEST_F(BitrateAllocatorTest, TwoBitrateObserversOneRtcpObserver) {
TestBitrateObserver bitrate_observer_1;
TestBitrateObserver bitrate_observer_2;
EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(100000, 0));
allocator_->AddObserver(&bitrate_observer_1, 100000, 300000, 0, true);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1));
EXPECT_CALL(limit_observer_,
OnAllocationLimitsChanged(100000 + 200000, 0));
allocator_->AddObserver(&bitrate_observer_2, 200000, 300000, 0, true);
EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2));
allocator_->AddObserver(&bitrate_observer_1, 100000, 300000, 0, true);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1));
EXPECT_CALL(limit_observer_,
OnAllocationLimitsChanged(100000 + 200000, 0));
allocator_->AddObserver(&bitrate_observer_2, 200000, 300000, 0, true);
EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2));
// Test too low start bitrate, hence lower than sum of min. Min bitrates
// will
// be allocated to all observers.
allocator_->OnNetworkChanged(200000, 0, 50);
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0, bitrate_observer_1.last_fraction_loss_);
EXPECT_EQ(50, bitrate_observer_1.last_rtt_ms_);
EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_);
EXPECT_EQ(0, bitrate_observer_2.last_fraction_loss_);
EXPECT_EQ(50, bitrate_observer_2.last_rtt_ms_);
// Test too low start bitrate, hence lower than sum of min. Min bitrates
// will
// be allocated to all observers.
allocator_->OnNetworkChanged(200000, 0, 50);
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0, bitrate_observer_1.last_fraction_loss_);
EXPECT_EQ(50, bitrate_observer_1.last_rtt_ms_);
EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_);
EXPECT_EQ(0, bitrate_observer_2.last_fraction_loss_);
EXPECT_EQ(50, bitrate_observer_2.last_rtt_ms_);
// Test a bitrate which should be distributed equally.
allocator_->OnNetworkChanged(500000, 0, 50);
const uint32_t kBitrateToShare = 500000 - 200000 - 100000;
EXPECT_EQ(100000u + kBitrateToShare / 2,
bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(200000u + kBitrateToShare / 2,
bitrate_observer_2.last_bitrate_bps_);
// Test a bitrate which should be distributed equally.
allocator_->OnNetworkChanged(500000, 0, 50);
const uint32_t kBitrateToShare = 500000 - 200000 - 100000;
EXPECT_EQ(100000u + kBitrateToShare / 2,
bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(200000u + kBitrateToShare / 2,
bitrate_observer_2.last_bitrate_bps_);
// Limited by 2x max bitrates since we leave room for FEC and
// retransmissions.
allocator_->OnNetworkChanged(1500000, 0, 50);
EXPECT_EQ(600000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(600000u, bitrate_observer_2.last_bitrate_bps_);
// Limited by 2x max bitrates since we leave room for FEC and
// retransmissions.
allocator_->OnNetworkChanged(1500000, 0, 50);
EXPECT_EQ(600000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(600000u, bitrate_observer_2.last_bitrate_bps_);
// Verify that if the bandwidth estimate is set to zero, the allocated
// rate is
// zero.
allocator_->OnNetworkChanged(0, 0, 50);
EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
// Verify that if the bandwidth estimate is set to zero, the allocated
// rate is
// zero.
allocator_->OnNetworkChanged(0, 0, 50);
EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
}
TEST_F(BitrateAllocatorTest, RemoveObserverTriggersLimitObserver) {
@ -167,19 +176,19 @@ TEST_F(BitrateAllocatorTestNoEnforceMin, OneBitrateObserver) {
// Expect OnAllocationLimitsChanged with |min_send_bitrate_bps| = 0 since
// AddObserver is called with |enforce_min_bitrate| = false.
EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1));
allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1));
// High BWE.
allocator_->OnNetworkChanged(150000, 0, 0);
EXPECT_EQ(150000u, bitrate_observer_1.last_bitrate_bps_);
// High BWE.
allocator_->OnNetworkChanged(150000, 0, 0);
EXPECT_EQ(150000u, bitrate_observer_1.last_bitrate_bps_);
// Low BWE.
allocator_->OnNetworkChanged(10000, 0, 0);
EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_);
// Low BWE.
allocator_->OnNetworkChanged(10000, 0, 0);
EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
allocator_->RemoveObserver(&bitrate_observer_1);
EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
allocator_->RemoveObserver(&bitrate_observer_1);
}
TEST_F(BitrateAllocatorTestNoEnforceMin, ThreeBitrateObservers) {
@ -243,6 +252,100 @@ TEST_F(BitrateAllocatorTestNoEnforceMin, ThreeBitrateObservers) {
allocator_->RemoveObserver(&bitrate_observer_3);
}
TEST_F(BitrateAllocatorTestNoEnforceMin, OneBitrateObserverWithPacketLoss) {
TestBitrateObserver bitrate_observer;
// Expect OnAllocationLimitsChanged with |min_send_bitrate_bps| = 0 since
// AddObserver is called with |enforce_min_bitrate| = false.
EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
allocator_->AddObserver(
&bitrate_observer, 100000, 400000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer));
// High BWE.
allocator_->OnNetworkChanged(150000, 0, 0);
EXPECT_EQ(150000u, bitrate_observer.last_bitrate_bps_);
// Add loss and use a part of the bitrate for protection.
double protection_ratio = 0.4;
uint8_t fraction_loss = protection_ratio * 256;
bitrate_observer.SetBitrateProtectionRatio(protection_ratio);
allocator_->OnNetworkChanged(200000, 0, fraction_loss);
EXPECT_EQ(200000u, bitrate_observer.last_bitrate_bps_);
// Above the min threshold, but not enough given the protection used.
allocator_->OnNetworkChanged(139000, 0, fraction_loss);
EXPECT_EQ(0u, bitrate_observer.last_bitrate_bps_);
// Verify the hysteresis is added for the protection.
allocator_->OnNetworkChanged(150000, 0, fraction_loss);
EXPECT_EQ(0u, bitrate_observer.last_bitrate_bps_);
// Just enough to enable video again.
allocator_->OnNetworkChanged(168000, 0, fraction_loss);
EXPECT_EQ(168000u, bitrate_observer.last_bitrate_bps_);
// Remove all protection and make sure video is not paused as earlier.
bitrate_observer.SetBitrateProtectionRatio(0.0);
allocator_->OnNetworkChanged(140000, 0, 0);
EXPECT_EQ(140000u, bitrate_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(139000, 0, 0);
EXPECT_EQ(139000u, bitrate_observer.last_bitrate_bps_);
EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
allocator_->RemoveObserver(&bitrate_observer);
}
TEST_F(BitrateAllocatorTestNoEnforceMin, TwoBitrateObserverWithPacketLoss) {
TestBitrateObserver bitrate_observer_1;
TestBitrateObserver bitrate_observer_2;
allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1));
allocator_->AddObserver(&bitrate_observer_2, 200000, 400000, 0, false);
EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2));
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_);
// Enough bitrate for both.
bitrate_observer_2.SetBitrateProtectionRatio(0.5);
allocator_->OnNetworkChanged(300000, 0, 0);
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_);
// Above min for observer 2, but too little given the protection used.
allocator_->OnNetworkChanged(330000, 0, 0);
EXPECT_EQ(330000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
allocator_->OnNetworkChanged(100000, 0, 0);
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
allocator_->OnNetworkChanged(99999, 0, 0);
EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
allocator_->OnNetworkChanged(119000, 0, 0);
EXPECT_EQ(0u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
allocator_->OnNetworkChanged(120000, 0, 0);
EXPECT_EQ(120000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
// Verify the protection is accounted for before resuming observer 2.
allocator_->OnNetworkChanged(429000, 0, 0);
EXPECT_EQ(400000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_bps_);
allocator_->OnNetworkChanged(430000, 0, 0);
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_);
EXPECT_EQ(330000u, bitrate_observer_2.last_bitrate_bps_);
allocator_->RemoveObserver(&bitrate_observer_1);
allocator_->RemoveObserver(&bitrate_observer_2);
}
TEST_F(BitrateAllocatorTest, ThreeBitrateObserversLowBweEnforceMin) {
TestBitrateObserver bitrate_observer_1;
TestBitrateObserver bitrate_observer_2;
@ -255,21 +358,21 @@ TEST_F(BitrateAllocatorTest, ThreeBitrateObserversLowBweEnforceMin) {
EXPECT_EQ(200000, allocator_->GetStartBitrate(&bitrate_observer_2));
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_);
allocator_->AddObserver(&bitrate_observer_3, 300000, 400000, 0, true);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_3));
EXPECT_EQ(100000, static_cast<int>(bitrate_observer_1.last_bitrate_bps_));
EXPECT_EQ(200000, static_cast<int>(bitrate_observer_2.last_bitrate_bps_));
allocator_->AddObserver(&bitrate_observer_3, 300000, 400000, 0, true);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_3));
EXPECT_EQ(100000, static_cast<int>(bitrate_observer_1.last_bitrate_bps_));
EXPECT_EQ(200000, static_cast<int>(bitrate_observer_2.last_bitrate_bps_));
// Low BWE. Verify that all observers still get their respective min
// bitrate.
allocator_->OnNetworkChanged(1000, 0, 0);
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); // Min cap.
EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_); // Min cap.
EXPECT_EQ(300000u, bitrate_observer_3.last_bitrate_bps_); // Min cap.
// Low BWE. Verify that all observers still get their respective min
// bitrate.
allocator_->OnNetworkChanged(1000, 0, 0);
EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_bps_); // Min cap.
EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_bps_); // Min cap.
EXPECT_EQ(300000u, bitrate_observer_3.last_bitrate_bps_); // Min cap.
allocator_->RemoveObserver(&bitrate_observer_1);
allocator_->RemoveObserver(&bitrate_observer_2);
allocator_->RemoveObserver(&bitrate_observer_3);
allocator_->RemoveObserver(&bitrate_observer_1);
allocator_->RemoveObserver(&bitrate_observer_2);
allocator_->RemoveObserver(&bitrate_observer_3);
}
TEST_F(BitrateAllocatorTest, AddObserverWhileNetworkDown) {
@ -303,96 +406,96 @@ TEST_F(BitrateAllocatorTest, AddObserverWhileNetworkDown) {
TEST_F(BitrateAllocatorTest, MixedEnforecedConfigs) {
TestBitrateObserver enforced_observer;
allocator_->AddObserver(&enforced_observer, 6000, 30000, 0, true);
EXPECT_EQ(60000, allocator_->GetStartBitrate(&enforced_observer));
allocator_->AddObserver(&enforced_observer, 6000, 30000, 0, true);
EXPECT_EQ(60000, allocator_->GetStartBitrate(&enforced_observer));
TestBitrateObserver not_enforced_observer;
allocator_->AddObserver(&not_enforced_observer, 30000, 2500000, 0, false);
EXPECT_EQ(270000, allocator_->GetStartBitrate(&not_enforced_observer));
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
TestBitrateObserver not_enforced_observer;
allocator_->AddObserver(&not_enforced_observer, 30000, 2500000, 0, false);
EXPECT_EQ(270000, allocator_->GetStartBitrate(&not_enforced_observer));
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(36000, 0, 50);
EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(30000u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(36000, 0, 50);
EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(30000u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(35000, 0, 50);
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(35000, 0, 50);
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(5000, 0, 50);
EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(5000, 0, 50);
EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(36000, 0, 50);
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(36000, 0, 50);
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(55000, 0, 50);
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(55000, 0, 50);
EXPECT_EQ(30000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(0u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(56000, 0, 50);
EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(50000u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(56000, 0, 50);
EXPECT_EQ(6000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(50000u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(56000, 0, 50);
EXPECT_EQ(16000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(40000u, not_enforced_observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(56000, 0, 50);
EXPECT_EQ(16000u, enforced_observer.last_bitrate_bps_);
EXPECT_EQ(40000u, not_enforced_observer.last_bitrate_bps_);
allocator_->RemoveObserver(&enforced_observer);
allocator_->RemoveObserver(&not_enforced_observer);
allocator_->RemoveObserver(&enforced_observer);
allocator_->RemoveObserver(&not_enforced_observer);
}
TEST_F(BitrateAllocatorTest, AvoidToggleAbsolute) {
TestBitrateObserver observer;
allocator_->AddObserver(&observer, 30000, 300000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&observer));
allocator_->AddObserver(&observer, 30000, 300000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&observer));
allocator_->OnNetworkChanged(30000, 0, 50);
EXPECT_EQ(30000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(30000, 0, 50);
EXPECT_EQ(30000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(20000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(20000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(30000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(30000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(49000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(49000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(50000, 0, 50);
EXPECT_EQ(50000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(50000, 0, 50);
EXPECT_EQ(50000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(30000, 0, 50);
EXPECT_EQ(30000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(30000, 0, 50);
EXPECT_EQ(30000u, observer.last_bitrate_bps_);
allocator_->RemoveObserver(&observer);
allocator_->RemoveObserver(&observer);
}
TEST_F(BitrateAllocatorTest, AvoidTogglePercent) {
TestBitrateObserver observer;
allocator_->AddObserver(&observer, 300000, 600000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&observer));
allocator_->AddObserver(&observer, 300000, 600000, 0, false);
EXPECT_EQ(300000, allocator_->GetStartBitrate(&observer));
allocator_->OnNetworkChanged(300000, 0, 50);
EXPECT_EQ(300000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(300000, 0, 50);
EXPECT_EQ(300000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(200000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(200000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(300000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(300000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(329000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(329000, 0, 50);
EXPECT_EQ(0u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(330000, 0, 50);
EXPECT_EQ(330000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(330000, 0, 50);
EXPECT_EQ(330000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(300000, 0, 50);
EXPECT_EQ(300000u, observer.last_bitrate_bps_);
allocator_->OnNetworkChanged(300000, 0, 50);
EXPECT_EQ(300000u, observer.last_bitrate_bps_);
allocator_->RemoveObserver(&observer);
allocator_->RemoveObserver(&observer);
}
} // namespace webrtc

View File

@ -25,7 +25,7 @@ class FullStackTest : public VideoQualityTest {
void ForemanCifWithoutPacketLoss(const std::string& video_codec) {
// TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif.
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 700000, 700000, 700000, video_codec, 1},
{352, 288, 30, 700000, 700000, 700000, false, video_codec, 1},
{"foreman_cif"},
{},
{"foreman_cif_net_delay_0_0_plr_0_" + video_codec, 0.0, 0.0,
@ -35,7 +35,7 @@ class FullStackTest : public VideoQualityTest {
void ForemanCifPlr5(const std::string& video_codec) {
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 30000, 500000, 2000000, video_codec, 1},
{352, 288, 30, 30000, 500000, 2000000, false, video_codec, 1},
{"foreman_cif"},
{},
{"foreman_cif_delay_50_0_plr_5_" + video_codec, 0.0, 0.0,
@ -68,7 +68,7 @@ TEST_F(FullStackTest, ForemanCifPlr5Vp9) {
TEST_F(FullStackTest, ParisQcifWithoutPacketLoss) {
VideoQualityTest::Params paris_qcif = {
{176, 144, 30, 300000, 300000, 300000, "VP8", 1},
{176, 144, 30, 300000, 300000, 300000, false, "VP8", 1},
{"paris_qcif"},
{},
{"net_delay_0_0_plr_0", 36.0, 0.96, kFullStackTestDurationSecs}};
@ -78,7 +78,7 @@ TEST_F(FullStackTest, ParisQcifWithoutPacketLoss) {
TEST_F(FullStackTest, ForemanCifWithoutPacketLoss) {
// TODO(pbos): Decide on psnr/ssim thresholds for foreman_cif.
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 700000, 700000, 700000, "VP8", 1},
{352, 288, 30, 700000, 700000, 700000, false, "VP8", 1},
{"foreman_cif"},
{},
{"foreman_cif_net_delay_0_0_plr_0", 0.0, 0.0, kFullStackTestDurationSecs}
@ -88,7 +88,7 @@ TEST_F(FullStackTest, ForemanCifWithoutPacketLoss) {
TEST_F(FullStackTest, ForemanCifPlr5) {
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 30000, 500000, 2000000, "VP8", 1},
{352, 288, 30, 30000, 500000, 2000000, false, "VP8", 1},
{"foreman_cif"},
{},
{"foreman_cif_delay_50_0_plr_5", 0.0, 0.0, kFullStackTestDurationSecs}};
@ -99,7 +99,7 @@ TEST_F(FullStackTest, ForemanCifPlr5) {
TEST_F(FullStackTest, ForemanCif500kbps) {
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 30000, 500000, 2000000, "VP8", 1},
{352, 288, 30, 30000, 500000, 2000000, false, "VP8", 1},
{"foreman_cif"},
{},
{"foreman_cif_500kbps", 0.0, 0.0, kFullStackTestDurationSecs}};
@ -111,7 +111,7 @@ TEST_F(FullStackTest, ForemanCif500kbps) {
TEST_F(FullStackTest, ForemanCif500kbpsLimitedQueue) {
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 30000, 500000, 2000000, "VP8", 1},
{352, 288, 30, 30000, 500000, 2000000, false, "VP8", 1},
{"foreman_cif"},
{},
{"foreman_cif_500kbps_32pkts_queue", 0.0, 0.0, kFullStackTestDurationSecs}
@ -124,7 +124,7 @@ TEST_F(FullStackTest, ForemanCif500kbpsLimitedQueue) {
TEST_F(FullStackTest, ForemanCif500kbps100ms) {
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 30000, 500000, 2000000, "VP8", 1},
{352, 288, 30, 30000, 500000, 2000000, false, "VP8", 1},
{"foreman_cif"},
{},
{"foreman_cif_500kbps_100ms", 0.0, 0.0, kFullStackTestDurationSecs}};
@ -136,7 +136,7 @@ TEST_F(FullStackTest, ForemanCif500kbps100ms) {
TEST_F(FullStackTest, ForemanCif500kbps100msLimitedQueue) {
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 30000, 500000, 2000000, "VP8", 1},
{352, 288, 30, 30000, 500000, 2000000, false, "VP8", 1},
{"foreman_cif"},
{},
{"foreman_cif_500kbps_100ms_32pkts_queue", 0.0, 0.0,
@ -149,7 +149,7 @@ TEST_F(FullStackTest, ForemanCif500kbps100msLimitedQueue) {
TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) {
VideoQualityTest::Params foreman_cif = {
{352, 288, 30, 30000, 2000000, 2000000, "VP8", 1},
{352, 288, 30, 30000, 2000000, 2000000, false, "VP8", 1},
{"foreman_cif"},
{},
{"foreman_cif_1000kbps_100ms_32pkts_queue", 0.0, 0.0,
@ -162,7 +162,7 @@ TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) {
TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL) {
VideoQualityTest::Params screenshare = {
{1850, 1110, 5, 50000, 200000, 2000000, "VP8", 2, 1, 400000},
{1850, 1110, 5, 50000, 200000, 2000000, false, "VP8", 2, 1, 400000},
{},
{true, 10},
{"screenshare_slides", 0.0, 0.0, kFullStackTestDurationSecs}};
@ -171,7 +171,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL) {
TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_Scroll) {
VideoQualityTest::Params config = {
{1850, 1110 / 2, 5, 50000, 200000, 2000000, "VP8", 2, 1, 400000},
{1850, 1110 / 2, 5, 50000, 200000, 2000000, false, "VP8", 2, 1, 400000},
{},
{true, 10, 2},
{"screenshare_slides_scrolling", 0.0, 0.0, kFullStackTestDurationSecs}};
@ -180,7 +180,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_Scroll) {
TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_LossyNet) {
VideoQualityTest::Params screenshare = {
{1850, 1110, 5, 50000, 200000, 2000000, "VP8", 2, 1, 400000},
{1850, 1110, 5, 50000, 200000, 2000000, false, "VP8", 2, 1, 400000},
{}, // Video-specific.
{true, 10}, // Screenshare-specific.
{"screenshare_slides_lossy_net", 0.0, 0.0, kFullStackTestDurationSecs}};
@ -192,7 +192,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_LossyNet) {
TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_VeryLossyNet) {
VideoQualityTest::Params screenshare = {
{1850, 1110, 5, 50000, 200000, 2000000, "VP8", 2, 1, 400000},
{1850, 1110, 5, 50000, 200000, 2000000, false, "VP8", 2, 1, 400000},
{}, // Video-specific.
{true, 10}, // Screenshare-specific.
{"screenshare_slides_very_lossy", 0.0, 0.0, kFullStackTestDurationSecs}};
@ -205,7 +205,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_VeryLossyNet) {
#if !defined(RTC_DISABLE_VP9)
TEST_F(FullStackTest, ScreenshareSlidesVP9_2SL) {
VideoQualityTest::Params screenshare = {
{1850, 1110, 5, 50000, 200000, 2000000, "VP9", 1, 0, 400000},
{1850, 1110, 5, 50000, 200000, 2000000, false, "VP9", 1, 0, 400000},
{},
{true, 10},
{"screenshare_slides_vp9_2sl", 0.0, 0.0, kFullStackTestDurationSecs},

View File

@ -226,7 +226,7 @@ void Loopback() {
VideoQualityTest::Params params{
{flags::Width(), flags::Height(), flags::Fps(),
flags::MinBitrateKbps() * 1000, flags::TargetBitrateKbps() * 1000,
flags::MaxBitrateKbps() * 1000, flags::Codec(),
flags::MaxBitrateKbps() * 1000, false, flags::Codec(),
flags::NumTemporalLayers(), flags::SelectedTL(),
flags::MinTransmitBitrateKbps() * 1000, call_bitrate_config,
flags::FLAGS_send_side_bwe},

View File

@ -56,6 +56,10 @@ int MaxBitrateKbps() {
return static_cast<int>(FLAGS_max_bitrate);
}
DEFINE_bool(suspend_below_min_bitrate,
false,
"Suspends video below the configured min bitrate.");
DEFINE_int32(num_temporal_layers,
1,
"Number of temporal layers. Set to 1-4 to override.");
@ -223,7 +227,9 @@ void Loopback() {
VideoQualityTest::Params params{
{flags::Width(), flags::Height(), flags::Fps(),
flags::MinBitrateKbps() * 1000, flags::TargetBitrateKbps() * 1000,
flags::MaxBitrateKbps() * 1000, flags::Codec(),
flags::MaxBitrateKbps() * 1000,
flags::FLAGS_suspend_below_min_bitrate,
flags::Codec(),
flags::NumTemporalLayers(), flags::SelectedTL(),
0, // No min transmit bitrate.
call_bitrate_config,

View File

@ -1125,6 +1125,9 @@ void VideoQualityTest::RunWithVideoRenderer(const Params& params) {
video_send_config_.local_renderer = local_preview.get();
video_receive_configs_[stream_id].renderer = loopback_video.get();
video_send_config_.suspend_below_min_bitrate =
params_.common.suspend_below_min_bitrate;
if (params.common.fec) {
video_send_config_.rtp.fec.red_payload_type = kRedPayloadType;
video_send_config_.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;

View File

@ -34,6 +34,7 @@ class VideoQualityTest : public test::CallTest {
int min_bitrate_bps;
int target_bitrate_bps;
int max_bitrate_bps;
bool suspend_below_min_bitrate;
std::string codec;
int num_temporal_layers;
int selected_tl;

View File

@ -866,15 +866,18 @@ void VideoSendStream::SignalNetworkState(NetworkState state) {
}
}
void VideoSendStream::OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) {
uint32_t VideoSendStream::OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) {
payload_router_.SetTargetSendBitrate(bitrate_bps);
// Get the encoder target rate. It is the estimated network rate -
// protection overhead.
uint32_t encoder_target_rate = protection_bitrate_calculator_.SetTargetRates(
bitrate_bps, stats_proxy_.GetSendFrameRate(), fraction_loss, rtt);
vie_encoder_.OnBitrateUpdated(encoder_target_rate, fraction_loss, rtt);
uint32_t encoder_target_rate_bps =
protection_bitrate_calculator_.SetTargetRates(
bitrate_bps, stats_proxy_.GetSendFrameRate(), fraction_loss, rtt);
vie_encoder_.OnBitrateUpdated(encoder_target_rate_bps, fraction_loss, rtt);
return bitrate_bps - encoder_target_rate_bps;
}
int VideoSendStream::ProtectionRequest(const FecProtectionParams* delta_params,

View File

@ -87,9 +87,9 @@ class VideoSendStream : public webrtc::VideoSendStream,
int GetPaddingNeededBps() const;
// Implements BitrateAllocatorObserver.
void OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) override;
uint32_t OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) override;
protected:
// Implements webrtc::VCMProtectionCallback.