From 008731868a09e2fe01da53733a612dc24761f791 Mon Sep 17 00:00:00 2001 From: "pbos@webrtc.org" Date: Tue, 25 Nov 2014 14:03:34 +0000 Subject: [PATCH] Implement settable min/start/max bitrates in Call. These parameters are set by the x-google-*-bitrate SDP parameters. This is implemented on a Call level instead of per-stream like the currently underlying VideoEngine implementation to allow this refactoring to not reconfigure the VideoCodec at all but rather adjust bandwidth-estimator parameters. Also implements SetMaxSendBandwidth in WebRtcVideoEngine2 as it's a SDP parameter and allowing it to be dynamically readjusted in Call. R=mflodman@webrtc.org, stefan@webrtc.org BUG=1788 Review URL: https://webrtc-codereview.appspot.com/26199004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7746 4adac7df-926f-26a2-2b94-8c16560cd09d --- talk/media/webrtc/webrtcvideoengine2.cc | 60 ++++++---- talk/media/webrtc/webrtcvideoengine2.h | 1 + .../webrtc/webrtcvideoengine2_unittest.cc | 106 +++++++++--------- .../webrtc/webrtcvideoengine2_unittest.h | 4 +- webrtc/call.h | 23 +++- webrtc/video/call.cc | 47 ++++++-- webrtc/video/call_perf_tests.cc | 2 +- webrtc/video/end_to_end_tests.cc | 2 +- webrtc/video/loopback.cc | 12 +- webrtc/video/rampup_tests.cc | 2 +- webrtc/video/video_send_stream.cc | 44 ++++++-- webrtc/video/video_send_stream.h | 8 +- webrtc/video/video_send_stream_tests.cc | 98 ++++++++++++++++ 13 files changed, 302 insertions(+), 107 deletions(-) diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc index 608b8073e5..06dee05f65 100644 --- a/talk/media/webrtc/webrtcvideoengine2.cc +++ b/talk/media/webrtc/webrtcvideoengine2.cc @@ -200,18 +200,8 @@ std::vector WebRtcVideoEncoderFactory2::CreateVideoStreams( stream.max_framerate = codec.framerate != 0 ? codec.framerate : kDefaultVideoMaxFramerate; - int min_bitrate = kMinVideoBitrate; - codec.GetParam(kCodecParamMinBitrate, &min_bitrate); - // Clamp the min video bitrate, this is set from JavaScript directly and needs - // to be sanitized. - if (min_bitrate < kMinVideoBitrate) { - min_bitrate = kMinVideoBitrate; - } - - int max_bitrate = kMaxVideoBitrate; - codec.GetParam(kCodecParamMaxBitrate, &max_bitrate); - stream.min_bitrate_bps = min_bitrate * 1000; - stream.target_bitrate_bps = stream.max_bitrate_bps = max_bitrate * 1000; + stream.min_bitrate_bps = kMinVideoBitrate * 1000; + stream.target_bitrate_bps = stream.max_bitrate_bps = kMaxVideoBitrate * 1000; int max_qp = kDefaultQpMax; codec.GetParam(kCodecParamMaxQuantization, &max_qp); @@ -703,11 +693,6 @@ WebRtcVideoChannel2::WebRtcVideoChannel2( config.voice_engine = voice_engine->voe()->engine(); } - // Set start bitrate for the call. A default is provided by SetDefaultOptions. - int start_bitrate_kbps; - options_.video_start_bitrate.Get(&start_bitrate_kbps); - config.stream_start_bitrate_bps = start_bitrate_kbps * 1000; - call_.reset(call_factory->CreateCall(config)); rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc; @@ -721,8 +706,6 @@ void WebRtcVideoChannel2::SetDefaultOptions() { options_.suspend_below_min_bitrate.Set(false); options_.use_payload_padding.Set(false); options_.video_noise_reduction.Set(true); - options_.video_start_bitrate.Set( - webrtc::Call::Config::kDefaultStartBitrateBps / 1000); options_.screencast_min_bitrate.Set(0); } @@ -841,6 +824,29 @@ bool WebRtcVideoChannel2::SetSendCodecs(const std::vector& codecs) { it->second->SetCodec(supported_codecs.front()); } + VideoCodec codec = supported_codecs.front().codec; + int bitrate_kbps; + if (codec.GetParam(kCodecParamMinBitrate, &bitrate_kbps) && + bitrate_kbps > 0) { + bitrate_config_.min_bitrate_bps = bitrate_kbps * 1000; + } else { + bitrate_config_.min_bitrate_bps = 0; + } + if (codec.GetParam(kCodecParamStartBitrate, &bitrate_kbps) && + bitrate_kbps > 0) { + bitrate_config_.start_bitrate_bps = bitrate_kbps * 1000; + } else { + // Do not reconfigure start bitrate unless it's specified and positive. + bitrate_config_.start_bitrate_bps = -1; + } + if (codec.GetParam(kCodecParamMaxBitrate, &bitrate_kbps) && + bitrate_kbps > 0) { + bitrate_config_.max_bitrate_bps = bitrate_kbps * 1000; + } else { + bitrate_config_.max_bitrate_bps = -1; + } + call_->SetBitrateConfig(bitrate_config_); + return true; } @@ -1276,9 +1282,19 @@ bool WebRtcVideoChannel2::SetSendRtpHeaderExtensions( return true; } -bool WebRtcVideoChannel2::SetMaxSendBandwidth(int bps) { - // TODO(pbos): Implement. - LOG(LS_VERBOSE) << "SetMaxSendBandwidth: " << bps; +bool WebRtcVideoChannel2::SetMaxSendBandwidth(int max_bitrate_bps) { + LOG(LS_INFO) << "SetMaxSendBandwidth: " << max_bitrate_bps << "bps."; + if (max_bitrate_bps <= 0) { + // Unsetting max bitrate. + max_bitrate_bps = -1; + } + bitrate_config_.start_bitrate_bps = -1; + bitrate_config_.max_bitrate_bps = max_bitrate_bps; + if (max_bitrate_bps > 0 && + bitrate_config_.min_bitrate_bps > max_bitrate_bps) { + bitrate_config_.min_bitrate_bps = max_bitrate_bps; + } + call_->SetBitrateConfig(bitrate_config_); return true; } diff --git a/talk/media/webrtc/webrtcvideoengine2.h b/talk/media/webrtc/webrtcvideoengine2.h index 9a5fe65850..8ce6f3603e 100644 --- a/talk/media/webrtc/webrtcvideoengine2.h +++ b/talk/media/webrtc/webrtcvideoengine2.h @@ -491,6 +491,7 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, WebRtcVideoEncoderFactory2* const encoder_factory_; std::vector recv_codecs_; std::vector recv_rtp_extensions_; + webrtc::Call::Config::BitrateConfig bitrate_config_; VideoOptions options_; }; diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc index 8e0a785b4c..6af5540390 100644 --- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc +++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc @@ -299,6 +299,11 @@ webrtc::Call::Stats FakeCall::GetStats() const { return stats; } +void FakeCall::SetBitrateConfig( + const webrtc::Call::Config::BitrateConfig& bitrate_config) { + config_.stream_bitrates = bitrate_config; +} + void FakeCall::SignalNetworkState(webrtc::Call::NetworkState state) { network_state_ = state; } @@ -350,8 +355,6 @@ class WebRtcVideoEngine2Test : public ::testing::Test { cricket::WebRtcVideoDecoderFactory* decoder_factory, const std::vector& codecs); - void TestStartBitrate(bool override_start_bitrate, int start_bitrate_bps); - WebRtcVideoEngine2 engine_; VideoCodec default_codec_; VideoCodec default_red_codec_; @@ -480,39 +483,6 @@ TEST_F(WebRtcVideoEngine2Test, SupportsAbsoluteSenderTimeHeaderExtension) { FAIL() << "Absolute Sender Time extension not in header-extension list."; } -void WebRtcVideoEngine2Test::TestStartBitrate(bool override_start_bitrate, - int start_bitrate_bps) { - FakeCallFactory call_factory; - engine_.SetCallFactory(&call_factory); - - engine_.Init(rtc::Thread::Current()); - - cricket::VideoOptions options; - if (override_start_bitrate) { - options.video_start_bitrate.Set(start_bitrate_bps / 1000); - } - - rtc::scoped_ptr channel( - engine_.CreateChannel(options, NULL)); - - EXPECT_EQ(override_start_bitrate - ? start_bitrate_bps - : webrtc::Call::Config::kDefaultStartBitrateBps, - call_factory.GetCall()->GetConfig().stream_start_bitrate_bps); -} - -TEST_F(WebRtcVideoEngine2Test, UsesCorrectDefaultStartBitrate) { - TestStartBitrate(false, -1); -} - -TEST_F(WebRtcVideoEngine2Test, CreateChannelCanUseIncreasedStartBitrate) { - TestStartBitrate(true, 2 * webrtc::Call::Config::kDefaultStartBitrateBps); -} - -TEST_F(WebRtcVideoEngine2Test, CreateChannelCanUseDecreasedStartBitrate) { - TestStartBitrate(true, webrtc::Call::Config::kDefaultStartBitrateBps / 2); -} - TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) { engine_.Init(rtc::Thread::Current()); rtc::scoped_ptr channel( @@ -877,25 +847,25 @@ class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test, return streams[streams.size() - 1]; } - void SetSendCodecsShouldWorkForBitrates(const char* min_bitrate, - const char* max_bitrate) { + void SetSendCodecsShouldWorkForBitrates(const char* min_bitrate_kbps, + int expected_min_bitrate_bps, + const char* start_bitrate_kbps, + int expected_start_bitrate_bps, + const char* max_bitrate_kbps, + int expected_max_bitrate_bps) { std::vector codecs; codecs.push_back(kVp8Codec); - codecs[0].params[kCodecParamMinBitrate] = min_bitrate; - codecs[0].params[kCodecParamMaxBitrate] = max_bitrate; + codecs[0].params[kCodecParamMinBitrate] = min_bitrate_kbps; + codecs[0].params[kCodecParamStartBitrate] = start_bitrate_kbps; + codecs[0].params[kCodecParamMaxBitrate] = max_bitrate_kbps; EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - FakeVideoSendStream* stream = AddSendStream(); - - std::vector video_streams = stream->GetVideoStreams(); - ASSERT_EQ(1u, video_streams.size()); - EXPECT_EQ(atoi(min_bitrate), video_streams.back().min_bitrate_bps / 1000); - EXPECT_EQ(atoi(max_bitrate), video_streams.back().max_bitrate_bps / 1000); - - VideoCodec codec; - EXPECT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_EQ(min_bitrate, codec.params[kCodecParamMinBitrate]); - EXPECT_EQ(max_bitrate, codec.params[kCodecParamMaxBitrate]); + EXPECT_EQ(expected_min_bitrate_bps, + fake_call_->GetConfig().stream_bitrates.min_bitrate_bps); + EXPECT_EQ(expected_start_bitrate_bps, + fake_call_->GetConfig().stream_bitrates.start_bitrate_bps); + EXPECT_EQ(expected_max_bitrate_bps, + fake_call_->GetConfig().stream_bitrates.max_bitrate_bps); } void TestSetSendRtpHeaderExtensions(const std::string& cricket_ext, @@ -1630,8 +1600,19 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsChangesExistingStreams) { EXPECT_EQ(kVp8Codec360p.height, streams[0].height); } -TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMinMaxBitrate) { - SetSendCodecsShouldWorkForBitrates("100", "200"); +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithBitrates) { + SetSendCodecsShouldWorkForBitrates("100", 100000, "150", 150000, "200", + 200000); +} + +TEST_F(WebRtcVideoChannel2Test, + SetSendCodecsWithoutBitratesUsesCorrectDefaults) { + SetSendCodecsShouldWorkForBitrates( + "", 0, "", -1, "", -1); +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsCapsMinAndStartBitrate) { + SetSendCodecsShouldWorkForBitrates("-1", 0, "-100", -1, "", -1); } TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectsMaxLessThanMinBitrate) { @@ -1641,8 +1622,25 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectsMaxLessThanMinBitrate) { EXPECT_FALSE(channel_->SetSendCodecs(video_codecs)); } -TEST_F(WebRtcVideoChannel2Test, SetSendCodecsAcceptLargeMinMaxBitrate) { - SetSendCodecsShouldWorkForBitrates("1000", "2000"); +TEST_F(WebRtcVideoChannel2Test, + SetMaxSendBandwidthShouldPreserveOtherBitrates) { + SetSendCodecsShouldWorkForBitrates("100", 100000, "150", 150000, "200", + 200000); + channel_->SetMaxSendBandwidth(300000); + EXPECT_EQ(100000, fake_call_->GetConfig().stream_bitrates.min_bitrate_bps) + << "Setting max bitrate should keep previous min bitrate."; + EXPECT_EQ(-1, fake_call_->GetConfig().stream_bitrates.start_bitrate_bps) + << "Setting max bitrate should not reset start bitrate."; + EXPECT_EQ(300000, fake_call_->GetConfig().stream_bitrates.max_bitrate_bps); +} + +TEST_F(WebRtcVideoChannel2Test, SetMaxSendBandwidthShouldBeRemovable) { + channel_->SetMaxSendBandwidth(300000); + EXPECT_EQ(300000, fake_call_->GetConfig().stream_bitrates.max_bitrate_bps); + // <= 0 means disable (infinite) max bitrate. + channel_->SetMaxSendBandwidth(0); + EXPECT_EQ(-1, fake_call_->GetConfig().stream_bitrates.max_bitrate_bps) + << "Setting zero max bitrate did not reset start bitrate."; } TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMaxQuantization) { diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.h b/talk/media/webrtc/webrtcvideoengine2_unittest.h index 48c4f64fdc..1826e9c505 100644 --- a/talk/media/webrtc/webrtcvideoengine2_unittest.h +++ b/talk/media/webrtc/webrtcvideoengine2_unittest.h @@ -129,9 +129,11 @@ class FakeCall : public webrtc::Call { virtual webrtc::Call::Stats GetStats() const OVERRIDE; + virtual void SetBitrateConfig( + const webrtc::Call::Config::BitrateConfig& bitrate_config) OVERRIDE; virtual void SignalNetworkState(webrtc::Call::NetworkState state) OVERRIDE; - const webrtc::Call::Config config_; + webrtc::Call::Config config_; webrtc::Call::NetworkState network_state_; std::vector codecs_; std::vector video_send_streams_; diff --git a/webrtc/call.h b/webrtc/call.h index c6596f86e5..920c00660d 100644 --- a/webrtc/call.h +++ b/webrtc/call.h @@ -65,8 +65,7 @@ class Call { : webrtc_config(NULL), send_transport(send_transport), voice_engine(NULL), - overuse_callback(NULL), - stream_start_bitrate_bps(kDefaultStartBitrateBps) {} + overuse_callback(NULL) {} static const int kDefaultStartBitrateBps; @@ -81,11 +80,20 @@ class Call { // captured frames. 'NULL' disables the callback. LoadObserver* overuse_callback; - // Start bitrate used before a valid bitrate estimate is calculated. + // Bitrate config used until valid bitrate estimates are calculated. Also + // used to cap total bitrate used. // Note: This is currently set only for video and is per-stream rather of // for the entire link. // TODO(pbos): Set start bitrate for entire Call. - int stream_start_bitrate_bps; + struct BitrateConfig { + BitrateConfig() + : min_bitrate_bps(0), + start_bitrate_bps(kDefaultStartBitrateBps), + max_bitrate_bps(-1) {} + int min_bitrate_bps; + int start_bitrate_bps; + int max_bitrate_bps; + } stream_bitrates; }; struct Stats { @@ -121,6 +129,13 @@ class Call { // pacing delay, etc. virtual Stats GetStats() const = 0; + // TODO(pbos): Like BitrateConfig above this is currently per-stream instead + // of maximum for entire Call. This should be fixed along with the above. + // Specifying a start bitrate (>0) will currently reset the current bitrate + // estimate. This is due to how the 'x-google-start-bitrate' flag is currently + // implemented. + virtual void SetBitrateConfig( + const Config::BitrateConfig& bitrate_config) = 0; virtual void SignalNetworkState(NetworkState state) = 0; virtual ~Call() {} diff --git a/webrtc/video/call.cc b/webrtc/video/call.cc index 2b4f76f4c5..512e82ca64 100644 --- a/webrtc/video/call.cc +++ b/webrtc/video/call.cc @@ -119,6 +119,8 @@ class Call : public webrtc::Call, public PacketReceiver { virtual DeliveryStatus DeliverPacket(const uint8_t* packet, size_t length) OVERRIDE; + virtual void SetBitrateConfig( + const webrtc::Call::Config::BitrateConfig& bitrate_config) OVERRIDE; virtual void SignalNetworkState(NetworkState state) OVERRIDE; private: @@ -176,6 +178,14 @@ Call::Call(webrtc::VideoEngine* video_engine, const Call::Config& config) assert(video_engine != NULL); assert(config.send_transport != NULL); + assert(config.stream_bitrates.min_bitrate_bps >= 0); + assert(config.stream_bitrates.start_bitrate_bps >= + config.stream_bitrates.min_bitrate_bps); + if (config.stream_bitrates.max_bitrate_bps != -1) { + assert(config.stream_bitrates.max_bitrate_bps >= + config.stream_bitrates.start_bitrate_bps); + } + if (config.overuse_callback) { overuse_observer_proxy_.reset( new CpuOveruseObserverProxy(config.overuse_callback)); @@ -213,15 +223,10 @@ VideoSendStream* Call::CreateVideoSendStream( // TODO(mflodman): Base the start bitrate on a current bandwidth estimate, if // the call has already started. - VideoSendStream* send_stream = - new VideoSendStream(config_.send_transport, - overuse_observer_proxy_.get(), - video_engine_, - config, - encoder_config, - suspended_send_ssrcs_, - base_channel_id_, - config_.stream_start_bitrate_bps); + VideoSendStream* send_stream = new VideoSendStream( + config_.send_transport, overuse_observer_proxy_.get(), video_engine_, + config, encoder_config, suspended_send_ssrcs_, base_channel_id_, + config_.stream_bitrates); // This needs to be taken before send_crit_ as both locks need to be held // while changing network state. @@ -342,6 +347,30 @@ Call::Stats Call::GetStats() const { return stats; } +void Call::SetBitrateConfig( + const webrtc::Call::Config::BitrateConfig& bitrate_config) { + assert(bitrate_config.min_bitrate_bps >= 0); + assert(bitrate_config.max_bitrate_bps == -1 || + bitrate_config.max_bitrate_bps > 0); + if (config_.stream_bitrates.min_bitrate_bps == + bitrate_config.min_bitrate_bps && + (bitrate_config.start_bitrate_bps <= 0 || + config_.stream_bitrates.start_bitrate_bps == + bitrate_config.start_bitrate_bps) && + config_.stream_bitrates.max_bitrate_bps == + bitrate_config.max_bitrate_bps) { + // Nothing new to set, early abort to avoid encoder reconfigurations. + return; + } + config_.stream_bitrates = bitrate_config; + ReadLockScoped read_lock(*send_crit_); + for (std::map::const_iterator it = + send_ssrcs_.begin(); + it != send_ssrcs_.end(); ++it) { + it->second->SetBitrateConfig(bitrate_config); + } +} + void Call::SignalNetworkState(NetworkState state) { // Take crit for entire function, it needs to be held while updating streams // to guarantee a consistent state across streams. diff --git a/webrtc/video/call_perf_tests.cc b/webrtc/video/call_perf_tests.cc index a6e619eefd..c898e251d5 100644 --- a/webrtc/video/call_perf_tests.cc +++ b/webrtc/video/call_perf_tests.cc @@ -640,7 +640,7 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { Call::Config GetSenderCallConfig() OVERRIDE { Call::Config config = EndToEndTest::GetSenderCallConfig(); - config.stream_start_bitrate_bps = kInitialBitrateKbps * 1000; + config.stream_bitrates.start_bitrate_bps = kInitialBitrateKbps * 1000; return config; } diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc index 769d3b16af..0871da403f 100644 --- a/webrtc/video/end_to_end_tests.cc +++ b/webrtc/video/end_to_end_tests.cc @@ -1566,7 +1566,7 @@ TEST_F(EndToEndTest, GetStats) { Call::Config GetSenderCallConfig() OVERRIDE { Call::Config config = EndToEndTest::GetSenderCallConfig(); - config.stream_start_bitrate_bps = kStartBitrateBps; + config.stream_bitrates.start_bitrate_bps = kStartBitrateBps; return config; } diff --git a/webrtc/video/loopback.cc b/webrtc/video/loopback.cc index 8013833dff..a1ebed1360 100644 --- a/webrtc/video/loopback.cc +++ b/webrtc/video/loopback.cc @@ -123,8 +123,12 @@ void Loopback() { pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs(); test::DirectTransport transport(pipe_config); Call::Config call_config(&transport); - call_config.stream_start_bitrate_bps = + call_config.stream_bitrates.min_bitrate_bps = + static_cast(flags::MinBitrate()) * 1000; + call_config.stream_bitrates.start_bitrate_bps = static_cast(flags::StartBitrate()) * 1000; + call_config.stream_bitrates.max_bitrate_bps = + static_cast(flags::MaxBitrate()) * 1000; scoped_ptr call(Call::Create(call_config)); // Loopback, call sends to itself. @@ -157,9 +161,9 @@ void Loopback() { VideoStream* stream = &encoder_config.streams[0]; stream->width = flags::Width(); stream->height = flags::Height(); - stream->min_bitrate_bps = static_cast(flags::MinBitrate()) * 1000; - stream->target_bitrate_bps = static_cast(flags::MaxBitrate()) * 1000; - stream->max_bitrate_bps = static_cast(flags::MaxBitrate()) * 1000; + stream->min_bitrate_bps = call_config.stream_bitrates.min_bitrate_bps; + stream->target_bitrate_bps = call_config.stream_bitrates.max_bitrate_bps; + stream->max_bitrate_bps = call_config.stream_bitrates.max_bitrate_bps; stream->max_framerate = 30; stream->max_qp = 56; diff --git a/webrtc/video/rampup_tests.cc b/webrtc/video/rampup_tests.cc index 59f0fd43e9..5e73abf7c7 100644 --- a/webrtc/video/rampup_tests.cc +++ b/webrtc/video/rampup_tests.cc @@ -403,7 +403,7 @@ void RampUpTest::RunRampUpTest(bool rtx, Call::Config call_config(&stream_observer); if (start_bitrate_bps != 0) { - call_config.stream_start_bitrate_bps = start_bitrate_bps; + call_config.stream_bitrates.start_bitrate_bps = start_bitrate_bps; stream_observer.set_start_bitrate_bps(start_bitrate_bps); } diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc index f9727fa348..5da26698f1 100644 --- a/webrtc/video/video_send_stream.cc +++ b/webrtc/video/video_send_stream.cc @@ -115,20 +115,26 @@ VideoSendStream::VideoSendStream( const VideoEncoderConfig& encoder_config, const std::map& suspended_ssrcs, int base_channel, - int start_bitrate_bps) + Call::Config::BitrateConfig bitrate_config) : transport_adapter_(transport), encoded_frame_proxy_(config.post_encode_callback), config_(config), - start_bitrate_bps_(start_bitrate_bps), + bitrate_config_(bitrate_config), suspended_ssrcs_(suspended_ssrcs), external_codec_(NULL), channel_(-1), - use_default_bitrate_(true), + use_config_bitrate_(true), stats_proxy_(config) { + // Duplicate assert checking of bitrate config. These should be checked in + // Call but are added here for verbosity. + assert(bitrate_config.min_bitrate_bps >= 0); + assert(bitrate_config.start_bitrate_bps >= bitrate_config.min_bitrate_bps); + if (bitrate_config.max_bitrate_bps != -1) + assert(bitrate_config.max_bitrate_bps >= bitrate_config.start_bitrate_bps); + video_engine_base_ = ViEBase::GetInterface(video_engine); video_engine_base_->CreateChannel(channel_, base_channel); assert(channel_ != -1); - assert(start_bitrate_bps_ > 0); rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine); assert(rtp_rtcp_ != NULL); @@ -385,10 +391,20 @@ bool VideoSendStream::ReconfigureVideoEncoder( video_codec.qpMax = std::max(video_codec.qpMax, static_cast(streams[i].max_qp)); } + // Clamp bitrates to the bitrate config. + if (video_codec.minBitrate < + static_cast(bitrate_config_.min_bitrate_bps / 1000)) { + video_codec.minBitrate = bitrate_config_.min_bitrate_bps / 1000; + } + if (bitrate_config_.max_bitrate_bps != -1 && + video_codec.maxBitrate > + static_cast(bitrate_config_.max_bitrate_bps / 1000)) { + video_codec.maxBitrate = bitrate_config_.max_bitrate_bps / 1000; + } unsigned int start_bitrate_bps; if (codec_->GetCodecTargetBitrate(channel_, &start_bitrate_bps) != 0 || - use_default_bitrate_) { - start_bitrate_bps = start_bitrate_bps_; + use_config_bitrate_) { + start_bitrate_bps = bitrate_config_.start_bitrate_bps; } video_codec.startBitrate = static_cast(start_bitrate_bps) / 1000; @@ -417,7 +433,8 @@ bool VideoSendStream::ReconfigureVideoEncoder( rtp_rtcp_->SetMinTransmitBitrate(channel_, config.min_transmit_bitrate_bps / 1000); - use_default_bitrate_ = false; + encoder_config_ = config; + use_config_bitrate_ = false; return true; } @@ -480,6 +497,19 @@ std::map VideoSendStream::GetRtpStates() const { return rtp_states; } +void VideoSendStream::SetBitrateConfig( + const Call::Config::BitrateConfig& bitrate_config) { + int last_start_bitrate_bps = bitrate_config_.start_bitrate_bps; + bitrate_config_ = bitrate_config; + if (bitrate_config_.start_bitrate_bps <= 0) { + bitrate_config_.start_bitrate_bps = last_start_bitrate_bps; + } else { + // Override start bitrate with bitrate from config. + use_config_bitrate_ = true; + } + ReconfigureVideoEncoder(encoder_config_); +} + void VideoSendStream::SignalNetworkState(Call::NetworkState state) { // When network goes up, enable RTCP status before setting transmission state. // When it goes down, disable RTCP afterwards. This ensures that any packets diff --git a/webrtc/video/video_send_stream.h b/webrtc/video/video_send_stream.h index 873785dc9e..56d0d3675d 100644 --- a/webrtc/video/video_send_stream.h +++ b/webrtc/video/video_send_stream.h @@ -49,7 +49,7 @@ class VideoSendStream : public webrtc::VideoSendStream, const VideoEncoderConfig& encoder_config, const std::map& suspended_ssrcs, int base_channel, - int start_bitrate); + Call::Config::BitrateConfig bitrate_config); virtual ~VideoSendStream(); @@ -72,6 +72,7 @@ class VideoSendStream : public webrtc::VideoSendStream, typedef std::map RtpStateMap; RtpStateMap GetRtpStates() const; + void SetBitrateConfig(const Call::Config::BitrateConfig& bitrate_config); void SignalNetworkState(Call::NetworkState state); int GetPacerQueuingDelayMs() const; @@ -81,7 +82,8 @@ class VideoSendStream : public webrtc::VideoSendStream, TransportAdapter transport_adapter_; EncodedFrameCallbackAdapter encoded_frame_proxy_; const VideoSendStream::Config config_; - const int start_bitrate_bps_; + VideoEncoderConfig encoder_config_; + Call::Config::BitrateConfig bitrate_config_; std::map suspended_ssrcs_; ViEBase* video_engine_base_; @@ -99,7 +101,7 @@ class VideoSendStream : public webrtc::VideoSendStream, // Used as a workaround to indicate that we should be using the configured // start bitrate initially, instead of the one reported by VideoEngine (which // defaults to too high). - bool use_default_bitrate_; + bool use_config_bitrate_; SendStatisticsProxy stats_proxy_; }; diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc index 9646f2bc92..6fedaf7276 100644 --- a/webrtc/video/video_send_stream_tests.cc +++ b/webrtc/video/video_send_stream_tests.cc @@ -1583,4 +1583,102 @@ TEST_F(VideoSendStreamTest, TranslatesTwoLayerScreencastToTargetBitrate) { RunBaseTest(&test); } + +TEST_F(VideoSendStreamTest, UsesCallStreamBitratesAndCanReconfigureBitrates) { + // These are chosen to be "kind of odd" to not be accidentally checked against + // default values. + static const int kMinBitrateKbps = 137; + static const int kStartBitrateKbps = 345; + static const int kLowerMaxBitrateKbps = 312; + static const int kMaxBitrateKbps = 413; + static const int kIncreasedStartBitrateKbps = 451; + static const int kIncreasedMaxBitrateKbps = 597; + class EncoderBitrateThresholdObserver : public test::SendTest, + public test::FakeEncoder { + public: + EncoderBitrateThresholdObserver() + : SendTest(kDefaultTimeoutMs), + FakeEncoder(Clock::GetRealTimeClock()), + num_initializations_(0) {} + + private: + virtual int32_t InitEncode(const VideoCodec* codecSettings, + int32_t numberOfCores, + size_t maxPayloadSize) OVERRIDE { + if (num_initializations_ == 0) { + EXPECT_EQ(static_cast(kMinBitrateKbps), + codecSettings->minBitrate); + EXPECT_EQ(static_cast(kStartBitrateKbps), + codecSettings->startBitrate); + EXPECT_EQ(static_cast(kMaxBitrateKbps), + codecSettings->maxBitrate); + observation_complete_->Set(); + } else if (num_initializations_ == 1) { + EXPECT_EQ(static_cast(kLowerMaxBitrateKbps), + codecSettings->maxBitrate); + // The start bitrate should be kept (-1) and capped to the max bitrate. + // Since this is not an end-to-end call no receiver should have been + // returning a REMB that could lower this estimate. + EXPECT_EQ(codecSettings->startBitrate, codecSettings->maxBitrate); + } else if (num_initializations_ == 2) { + EXPECT_EQ(static_cast(kIncreasedMaxBitrateKbps), + codecSettings->maxBitrate); + EXPECT_EQ(static_cast(kIncreasedStartBitrateKbps), + codecSettings->startBitrate); + } + ++num_initializations_; + return FakeEncoder::InitEncode(codecSettings, numberOfCores, + maxPayloadSize); + } + + virtual Call::Config GetSenderCallConfig() OVERRIDE { + Call::Config config(SendTransport()); + config.stream_bitrates.min_bitrate_bps = kMinBitrateKbps * 1000; + config.stream_bitrates.start_bitrate_bps = kStartBitrateKbps * 1000; + config.stream_bitrates.max_bitrate_bps = kMaxBitrateKbps * 1000; + return config; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = this; + // Set bitrates lower/higher than min/max to make sure they are properly + // capped. + encoder_config->streams.front().min_bitrate_bps = + (kMinBitrateKbps - 10) * 1000; + encoder_config->streams.front().max_bitrate_bps = + (kIncreasedMaxBitrateKbps + 10) * 1000; + } + + virtual void OnCallsCreated(Call* sender_call, + Call* receiver_call) OVERRIDE { + call_ = sender_call; + } + + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting encoder to be configured."; + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 0; + bitrate_config.start_bitrate_bps = -1; + bitrate_config.max_bitrate_bps = kLowerMaxBitrateKbps * 1000; + call_->SetBitrateConfig(bitrate_config); + EXPECT_EQ(2, num_initializations_) + << "Encoder should have been reconfigured with the new value."; + bitrate_config.start_bitrate_bps = kIncreasedStartBitrateKbps * 1000; + bitrate_config.max_bitrate_bps = kIncreasedMaxBitrateKbps * 1000; + call_->SetBitrateConfig(bitrate_config); + EXPECT_EQ(3, num_initializations_) + << "Encoder should have been reconfigured with the new value."; + } + + int num_initializations_; + webrtc::Call* call_; + } test; + + RunBaseTest(&test); +} } // namespace webrtc