Variable audio bitrate.
This is a first CL wiring up AudioSendStream to BitrateAllocator. This is still experimental and there is a test added for the audio only case, combined audio video variable bitrate test cases will be added as a follow up. BUG=5079 Review-Url: https://codereview.webrtc.org/2165743003 Cr-Commit-Position: refs/heads/master@{#13527}
This commit is contained in:
parent
62d695f655
commit
86cc6ffc7c
@ -2,12 +2,14 @@ include_rules = [
|
||||
"+webrtc/base",
|
||||
"+webrtc/voice_engine",
|
||||
"+webrtc/modules/audio_coding/codecs/mock",
|
||||
"+webrtc/call",
|
||||
"+webrtc/modules/bitrate_controller",
|
||||
"+webrtc/modules/congestion_controller",
|
||||
"+webrtc/modules/pacing",
|
||||
"+webrtc/modules/remote_bitrate_estimator",
|
||||
"+webrtc/modules/rtp_rtcp",
|
||||
"+webrtc/system_wrappers",
|
||||
"+webrtc/voice_engine",
|
||||
]
|
||||
|
||||
specific_include_rules = {
|
||||
|
||||
@ -59,8 +59,11 @@ namespace internal {
|
||||
AudioSendStream::AudioSendStream(
|
||||
const webrtc::AudioSendStream::Config& config,
|
||||
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
|
||||
CongestionController* congestion_controller)
|
||||
: config_(config), audio_state_(audio_state) {
|
||||
CongestionController* congestion_controller,
|
||||
BitrateAllocator* bitrate_allocator)
|
||||
: config_(config),
|
||||
audio_state_(audio_state),
|
||||
bitrate_allocator_(bitrate_allocator) {
|
||||
LOG(LS_INFO) << "AudioSendStream: " << config_.ToString();
|
||||
RTC_DCHECK_NE(config_.voe_channel_id, -1);
|
||||
RTC_DCHECK(audio_state_.get());
|
||||
@ -104,6 +107,12 @@ AudioSendStream::~AudioSendStream() {
|
||||
|
||||
void AudioSendStream::Start() {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
if (config_.min_bitrate_kbps != -1 && config_.max_bitrate_kbps != -1) {
|
||||
RTC_DCHECK_GE(config_.max_bitrate_kbps, config_.min_bitrate_kbps);
|
||||
bitrate_allocator_->AddObserver(this, config_.min_bitrate_kbps * 1000,
|
||||
config_.max_bitrate_kbps * 1000, 0, true);
|
||||
}
|
||||
|
||||
ScopedVoEInterface<VoEBase> base(voice_engine());
|
||||
int error = base->StartSend(config_.voe_channel_id);
|
||||
if (error != 0) {
|
||||
@ -113,6 +122,7 @@ void AudioSendStream::Start() {
|
||||
|
||||
void AudioSendStream::Stop() {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
bitrate_allocator_->RemoveObserver(this);
|
||||
ScopedVoEInterface<VoEBase> base(voice_engine());
|
||||
int error = base->StopSend(config_.voe_channel_id);
|
||||
if (error != 0) {
|
||||
@ -227,6 +237,24 @@ bool AudioSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
|
||||
return channel_proxy_->ReceivedRTCPPacket(packet, length);
|
||||
}
|
||||
|
||||
uint32_t AudioSendStream::OnBitrateUpdated(uint32_t bitrate_bps,
|
||||
uint8_t fraction_loss,
|
||||
int64_t rtt) {
|
||||
RTC_DCHECK_GE(bitrate_bps,
|
||||
static_cast<uint32_t>(config_.min_bitrate_kbps * 1000));
|
||||
// The bitrate allocator might allocate an higher than max configured bitrate
|
||||
// if there is room, to allow for, as example, extra FEC. Ignore that for now.
|
||||
const uint32_t max_bitrate_bps = config_.max_bitrate_kbps * 1000;
|
||||
if (bitrate_bps > max_bitrate_bps)
|
||||
bitrate_bps = max_bitrate_bps;
|
||||
|
||||
channel_proxy_->SetBitrate(bitrate_bps);
|
||||
|
||||
// The amount of audio protection is not exposed by the encoder, hence
|
||||
// always returning 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
const webrtc::AudioSendStream::Config& AudioSendStream::config() const {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
return config_;
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "webrtc/audio_state.h"
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/thread_checker.h"
|
||||
#include "webrtc/call/bitrate_allocator.h"
|
||||
|
||||
namespace webrtc {
|
||||
class CongestionController;
|
||||
@ -27,11 +28,13 @@ class ChannelProxy;
|
||||
} // namespace voe
|
||||
|
||||
namespace internal {
|
||||
class AudioSendStream final : public webrtc::AudioSendStream {
|
||||
class AudioSendStream final : public webrtc::AudioSendStream,
|
||||
public webrtc::BitrateAllocatorObserver {
|
||||
public:
|
||||
AudioSendStream(const webrtc::AudioSendStream::Config& config,
|
||||
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
|
||||
CongestionController* congestion_controller);
|
||||
CongestionController* congestion_controller,
|
||||
BitrateAllocator* bitrate_allocator);
|
||||
~AudioSendStream() override;
|
||||
|
||||
// webrtc::AudioSendStream implementation.
|
||||
@ -44,6 +47,12 @@ class AudioSendStream final : public webrtc::AudioSendStream {
|
||||
|
||||
void SignalNetworkState(NetworkState state);
|
||||
bool DeliverRtcp(const uint8_t* packet, size_t length);
|
||||
|
||||
// Implements BitrateAllocatorObserver.
|
||||
uint32_t OnBitrateUpdated(uint32_t bitrate_bps,
|
||||
uint8_t fraction_loss,
|
||||
int64_t rtt) override;
|
||||
|
||||
const webrtc::AudioSendStream::Config& config() const;
|
||||
|
||||
private:
|
||||
@ -54,6 +63,8 @@ class AudioSendStream final : public webrtc::AudioSendStream {
|
||||
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
|
||||
std::unique_ptr<voe::ChannelProxy> channel_proxy_;
|
||||
|
||||
BitrateAllocator* const bitrate_allocator_;
|
||||
|
||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AudioSendStream);
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
@ -50,6 +50,13 @@ const int kTelephoneEventPayloadType = 123;
|
||||
const int kTelephoneEventCode = 45;
|
||||
const int kTelephoneEventDuration = 6789;
|
||||
|
||||
class MockLimitObserver : public BitrateAllocator::LimitObserver {
|
||||
public:
|
||||
MOCK_METHOD2(OnAllocationLimitsChanged,
|
||||
void(uint32_t min_send_bitrate_bps,
|
||||
uint32_t max_padding_bitrate_bps));
|
||||
};
|
||||
|
||||
struct ConfigHelper {
|
||||
ConfigHelper()
|
||||
: simulated_clock_(123456),
|
||||
@ -57,7 +64,8 @@ struct ConfigHelper {
|
||||
congestion_controller_(&simulated_clock_,
|
||||
&bitrate_observer_,
|
||||
&remote_bitrate_observer_,
|
||||
&event_log_) {
|
||||
&event_log_),
|
||||
bitrate_allocator_(&limit_observer_) {
|
||||
using testing::Invoke;
|
||||
using testing::StrEq;
|
||||
|
||||
@ -116,6 +124,7 @@ struct ConfigHelper {
|
||||
CongestionController* congestion_controller() {
|
||||
return &congestion_controller_;
|
||||
}
|
||||
BitrateAllocator* bitrate_allocator() { return &bitrate_allocator_; }
|
||||
|
||||
void SetupMockForSendTelephoneEvent() {
|
||||
EXPECT_TRUE(channel_proxy_);
|
||||
@ -170,6 +179,8 @@ struct ConfigHelper {
|
||||
testing::NiceMock<MockRemoteBitrateObserver> remote_bitrate_observer_;
|
||||
CongestionController congestion_controller_;
|
||||
MockRtcEventLog event_log_;
|
||||
testing::NiceMock<MockLimitObserver> limit_observer_;
|
||||
BitrateAllocator bitrate_allocator_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -192,13 +203,15 @@ TEST(AudioSendStreamTest, ConfigToString) {
|
||||
TEST(AudioSendStreamTest, ConstructDestruct) {
|
||||
ConfigHelper helper;
|
||||
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
|
||||
helper.congestion_controller());
|
||||
helper.congestion_controller(),
|
||||
helper.bitrate_allocator());
|
||||
}
|
||||
|
||||
TEST(AudioSendStreamTest, SendTelephoneEvent) {
|
||||
ConfigHelper helper;
|
||||
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
|
||||
helper.congestion_controller());
|
||||
helper.congestion_controller(),
|
||||
helper.bitrate_allocator());
|
||||
helper.SetupMockForSendTelephoneEvent();
|
||||
EXPECT_TRUE(send_stream.SendTelephoneEvent(kTelephoneEventPayloadType,
|
||||
kTelephoneEventCode, kTelephoneEventDuration));
|
||||
@ -207,7 +220,8 @@ TEST(AudioSendStreamTest, SendTelephoneEvent) {
|
||||
TEST(AudioSendStreamTest, SetMuted) {
|
||||
ConfigHelper helper;
|
||||
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
|
||||
helper.congestion_controller());
|
||||
helper.congestion_controller(),
|
||||
helper.bitrate_allocator());
|
||||
EXPECT_CALL(*helper.channel_proxy(), SetInputMute(true));
|
||||
send_stream.SetMuted(true);
|
||||
}
|
||||
@ -215,7 +229,8 @@ TEST(AudioSendStreamTest, SetMuted) {
|
||||
TEST(AudioSendStreamTest, GetStats) {
|
||||
ConfigHelper helper;
|
||||
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
|
||||
helper.congestion_controller());
|
||||
helper.congestion_controller(),
|
||||
helper.bitrate_allocator());
|
||||
helper.SetupMockForGetStats();
|
||||
AudioSendStream::Stats stats = send_stream.GetStats();
|
||||
EXPECT_EQ(kSsrc, stats.local_ssrc);
|
||||
@ -243,7 +258,8 @@ TEST(AudioSendStreamTest, GetStats) {
|
||||
TEST(AudioSendStreamTest, GetStatsTypingNoiseDetected) {
|
||||
ConfigHelper helper;
|
||||
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
|
||||
helper.congestion_controller());
|
||||
helper.congestion_controller(),
|
||||
helper.bitrate_allocator());
|
||||
helper.SetupMockForGetStats();
|
||||
EXPECT_FALSE(send_stream.GetStats().typing_noise_detected);
|
||||
|
||||
|
||||
@ -88,6 +88,12 @@ class AudioSendStream {
|
||||
// TODO(solenberg): Implement, once we configure codecs through the new API.
|
||||
// std::unique_ptr<AudioEncoder> encoder;
|
||||
int cng_payload_type = -1; // pt, or -1 to disable Comfort Noise Generator.
|
||||
|
||||
// Bitrate limits used for variable audio bitrate streams. Set both to -1 to
|
||||
// disable audio bitrate adaptation.
|
||||
// Note: This is still an experimental feature and not ready for real usage.
|
||||
int min_bitrate_kbps = -1;
|
||||
int max_bitrate_kbps = -1;
|
||||
};
|
||||
|
||||
// Starts stream activity.
|
||||
|
||||
@ -348,7 +348,8 @@ webrtc::AudioSendStream* Call::CreateAudioSendStream(
|
||||
TRACE_EVENT0("webrtc", "Call::CreateAudioSendStream");
|
||||
RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread());
|
||||
AudioSendStream* send_stream = new AudioSendStream(
|
||||
config, config_.audio_state, congestion_controller_.get());
|
||||
config, config_.audio_state, congestion_controller_.get(),
|
||||
bitrate_allocator_.get());
|
||||
{
|
||||
WriteLockScoped write_lock(*send_crit_);
|
||||
RTC_DCHECK(audio_send_ssrcs_.find(config.rtp.ssrc) ==
|
||||
|
||||
@ -41,6 +41,7 @@ RampUpTester::RampUpTester(size_t num_video_streams,
|
||||
num_audio_streams_(num_audio_streams),
|
||||
rtx_(rtx),
|
||||
red_(red),
|
||||
sender_call_(nullptr),
|
||||
send_stream_(nullptr),
|
||||
start_bitrate_bps_(start_bitrate_bps),
|
||||
start_bitrate_verified_(false),
|
||||
@ -53,8 +54,7 @@ RampUpTester::RampUpTester(size_t num_video_streams,
|
||||
audio_ssrcs_(GenerateSsrcs(num_audio_streams_, 300)),
|
||||
poller_thread_(&BitrateStatsPollingThread,
|
||||
this,
|
||||
"BitrateStatsPollingThread"),
|
||||
sender_call_(nullptr) {
|
||||
"BitrateStatsPollingThread") {
|
||||
EXPECT_LE(num_audio_streams_, 1u);
|
||||
if (rtx_) {
|
||||
for (size_t i = 0; i < video_ssrcs_.size(); ++i)
|
||||
@ -188,6 +188,9 @@ void RampUpTester::ModifyAudioConfigs(
|
||||
send_config->rtp.ssrc = audio_ssrcs_[0];
|
||||
send_config->rtp.extensions.clear();
|
||||
|
||||
send_config->min_bitrate_kbps = 6;
|
||||
send_config->max_bitrate_kbps = 60;
|
||||
|
||||
bool transport_cc = false;
|
||||
if (extension_type_ == RtpExtension::kAbsSendTimeUri) {
|
||||
transport_cc = false;
|
||||
@ -267,6 +270,9 @@ void RampUpTester::TriggerTestDone() {
|
||||
RTC_DCHECK_GE(test_start_ms_, 0);
|
||||
|
||||
// TODO(holmer): Add audio send stats here too when those APIs are available.
|
||||
if (!send_stream_)
|
||||
return;
|
||||
|
||||
VideoSendStream::Stats send_stats = send_stream_->GetStats();
|
||||
|
||||
size_t total_packets_sent = 0;
|
||||
@ -341,6 +347,11 @@ bool RampUpDownUpTester::PollStats() {
|
||||
transmit_bitrate_bps += it.second.total_bitrate_bps;
|
||||
}
|
||||
EvolveTestState(transmit_bitrate_bps, stats.suspended);
|
||||
} else if (num_audio_streams_ > 0 && sender_call_ != nullptr) {
|
||||
// An audio send stream doesn't have bitrate stats, so the call send BW is
|
||||
// currently used instead.
|
||||
int transmit_bitrate_bps = sender_call_->GetStats().send_bandwidth_bps;
|
||||
EvolveTestState(transmit_bitrate_bps, false);
|
||||
}
|
||||
|
||||
return !event_.Wait(kPollIntervalMs);
|
||||
@ -380,7 +391,7 @@ void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) {
|
||||
switch (test_state_) {
|
||||
case kFirstRampup: {
|
||||
EXPECT_FALSE(suspended);
|
||||
if (bitrate_bps > kExpectedHighBitrateBps) {
|
||||
if (bitrate_bps >= kExpectedHighBitrateBps) {
|
||||
// The first ramp-up has reached the target bitrate. Change the
|
||||
// channel limit, and move to the next test state.
|
||||
forward_transport_config_.link_capacity_kbps =
|
||||
@ -397,7 +408,10 @@ void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) {
|
||||
break;
|
||||
}
|
||||
case kLowRate: {
|
||||
if (bitrate_bps < kExpectedLowBitrateBps && suspended) {
|
||||
// Audio streams are never suspended.
|
||||
bool check_suspend_state = num_video_streams_ > 0;
|
||||
if (bitrate_bps < kExpectedLowBitrateBps &&
|
||||
suspended == check_suspend_state) {
|
||||
// The ramp-down was successful. Change the channel limit back to a
|
||||
// high value, and move to the next test state.
|
||||
forward_transport_config_.link_capacity_kbps =
|
||||
@ -414,7 +428,7 @@ void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) {
|
||||
break;
|
||||
}
|
||||
case kSecondRampup: {
|
||||
if (bitrate_bps > kExpectedHighBitrateBps && !suspended) {
|
||||
if (bitrate_bps >= kExpectedHighBitrateBps && !suspended) {
|
||||
webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(),
|
||||
"second_rampup", now - state_start_ms_, "ms",
|
||||
false);
|
||||
@ -520,6 +534,13 @@ TEST_F(RampUpTest, DISABLED_SendSideAudioVideoUpDownUpRtx) {
|
||||
RunBaseTest(&test);
|
||||
}
|
||||
|
||||
TEST_F(RampUpTest, SendSideAudioOnlyUpDownUpRtx) {
|
||||
RampUpDownUpTester test(0, 1, kStartBitrateBps,
|
||||
RtpExtension::kTransportSequenceNumberUri, true,
|
||||
false);
|
||||
RunBaseTest(&test);
|
||||
}
|
||||
|
||||
TEST_F(RampUpTest, AbsSendTimeSingleStream) {
|
||||
RampUpTester test(1, 0, 0, RtpExtension::kAbsSendTimeUri, false, false);
|
||||
RunBaseTest(&test);
|
||||
|
||||
@ -64,6 +64,7 @@ class RampUpTester : public test::EndToEndTest {
|
||||
const size_t num_audio_streams_;
|
||||
const bool rtx_;
|
||||
const bool red_;
|
||||
Call* sender_call_;
|
||||
VideoSendStream* send_stream_;
|
||||
test::PacketTransport* send_transport_;
|
||||
|
||||
@ -99,7 +100,6 @@ class RampUpTester : public test::EndToEndTest {
|
||||
SsrcMap rtx_ssrc_map_;
|
||||
|
||||
rtc::PlatformThread poller_thread_;
|
||||
Call* sender_call_;
|
||||
};
|
||||
|
||||
class RampUpDownUpTester : public RampUpTester {
|
||||
|
||||
@ -1136,6 +1136,14 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
|
||||
stream_ = nullptr;
|
||||
}
|
||||
config_.rtp.extensions = extensions;
|
||||
if (webrtc::field_trial::FindFullName("WebRTC-AdaptAudioBitrate") ==
|
||||
"Enabled") {
|
||||
// TODO(mflodman): Keep testing this and set proper values.
|
||||
// Note: This is an early experiment currently only supported by Opus.
|
||||
config_.min_bitrate_kbps = kOpusMinBitrate;
|
||||
config_.max_bitrate_kbps = kOpusBitrateFb;
|
||||
}
|
||||
|
||||
RTC_DCHECK(!stream_);
|
||||
stream_ = call_->CreateAudioSendStream(config_);
|
||||
RTC_CHECK(stream_);
|
||||
|
||||
@ -62,8 +62,8 @@ void CallTest::RunBaseTest(BaseTest* test) {
|
||||
CreateReceiverCall(recv_config);
|
||||
}
|
||||
test->OnCallsCreated(sender_call_.get(), receiver_call_.get());
|
||||
send_transport_.reset(test->CreateSendTransport(sender_call_.get()));
|
||||
receive_transport_.reset(test->CreateReceiveTransport());
|
||||
send_transport_.reset(test->CreateSendTransport(sender_call_.get()));
|
||||
|
||||
if (test->ShouldCreateReceivers()) {
|
||||
send_transport_->SetReceiver(receiver_call_->Receiver());
|
||||
|
||||
@ -58,6 +58,7 @@ class MockVoEChannelProxy : public voe::ChannelProxy {
|
||||
const rtc::scoped_refptr<AudioDecoderFactory>&());
|
||||
MOCK_METHOD1(SetChannelOutputVolumeScaling, void(float scaling));
|
||||
MOCK_METHOD1(SetRtcEventLog, void(RtcEventLog* event_log));
|
||||
MOCK_METHOD1(SetBitrate, void(int bitrate_bps));
|
||||
};
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
@ -158,6 +158,11 @@ bool ChannelProxy::SendTelephoneEventOutband(int event, int duration_ms) {
|
||||
return channel()->SendTelephoneEventOutband(event, duration_ms) == 0;
|
||||
}
|
||||
|
||||
void ChannelProxy::SetBitrate(int bitrate_bps) {
|
||||
// May be called on different threads and needs to be handled by the channel.
|
||||
channel()->SetBitRate(bitrate_bps);
|
||||
}
|
||||
|
||||
void ChannelProxy::SetSink(std::unique_ptr<AudioSinkInterface> sink) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
channel()->SetSink(std::move(sink));
|
||||
|
||||
@ -73,6 +73,7 @@ class ChannelProxy {
|
||||
|
||||
virtual bool SetSendTelephoneEventPayloadType(int payload_type);
|
||||
virtual bool SendTelephoneEventOutband(int event, int duration_ms);
|
||||
virtual void SetBitrate(int bitrate_bps);
|
||||
virtual void SetSink(std::unique_ptr<AudioSinkInterface> sink);
|
||||
virtual void SetInputMute(bool muted);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user