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
This commit is contained in:
parent
b951eb12c9
commit
008731868a
@ -200,18 +200,8 @@ std::vector<webrtc::VideoStream> WebRtcVideoEncoderFactory2::CreateVideoStreams(
|
|||||||
stream.max_framerate =
|
stream.max_framerate =
|
||||||
codec.framerate != 0 ? codec.framerate : kDefaultVideoMaxFramerate;
|
codec.framerate != 0 ? codec.framerate : kDefaultVideoMaxFramerate;
|
||||||
|
|
||||||
int min_bitrate = kMinVideoBitrate;
|
stream.min_bitrate_bps = kMinVideoBitrate * 1000;
|
||||||
codec.GetParam(kCodecParamMinBitrate, &min_bitrate);
|
stream.target_bitrate_bps = stream.max_bitrate_bps = kMaxVideoBitrate * 1000;
|
||||||
// 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;
|
|
||||||
|
|
||||||
int max_qp = kDefaultQpMax;
|
int max_qp = kDefaultQpMax;
|
||||||
codec.GetParam(kCodecParamMaxQuantization, &max_qp);
|
codec.GetParam(kCodecParamMaxQuantization, &max_qp);
|
||||||
@ -703,11 +693,6 @@ WebRtcVideoChannel2::WebRtcVideoChannel2(
|
|||||||
config.voice_engine = voice_engine->voe()->engine();
|
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));
|
call_.reset(call_factory->CreateCall(config));
|
||||||
|
|
||||||
rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc;
|
rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc;
|
||||||
@ -721,8 +706,6 @@ void WebRtcVideoChannel2::SetDefaultOptions() {
|
|||||||
options_.suspend_below_min_bitrate.Set(false);
|
options_.suspend_below_min_bitrate.Set(false);
|
||||||
options_.use_payload_padding.Set(false);
|
options_.use_payload_padding.Set(false);
|
||||||
options_.video_noise_reduction.Set(true);
|
options_.video_noise_reduction.Set(true);
|
||||||
options_.video_start_bitrate.Set(
|
|
||||||
webrtc::Call::Config::kDefaultStartBitrateBps / 1000);
|
|
||||||
options_.screencast_min_bitrate.Set(0);
|
options_.screencast_min_bitrate.Set(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -841,6 +824,29 @@ bool WebRtcVideoChannel2::SetSendCodecs(const std::vector<VideoCodec>& codecs) {
|
|||||||
it->second->SetCodec(supported_codecs.front());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1276,9 +1282,19 @@ bool WebRtcVideoChannel2::SetSendRtpHeaderExtensions(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebRtcVideoChannel2::SetMaxSendBandwidth(int bps) {
|
bool WebRtcVideoChannel2::SetMaxSendBandwidth(int max_bitrate_bps) {
|
||||||
// TODO(pbos): Implement.
|
LOG(LS_INFO) << "SetMaxSendBandwidth: " << max_bitrate_bps << "bps.";
|
||||||
LOG(LS_VERBOSE) << "SetMaxSendBandwidth: " << 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,6 +491,7 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler,
|
|||||||
WebRtcVideoEncoderFactory2* const encoder_factory_;
|
WebRtcVideoEncoderFactory2* const encoder_factory_;
|
||||||
std::vector<VideoCodecSettings> recv_codecs_;
|
std::vector<VideoCodecSettings> recv_codecs_;
|
||||||
std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
|
std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
|
||||||
|
webrtc::Call::Config::BitrateConfig bitrate_config_;
|
||||||
VideoOptions options_;
|
VideoOptions options_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -299,6 +299,11 @@ webrtc::Call::Stats FakeCall::GetStats() const {
|
|||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FakeCall::SetBitrateConfig(
|
||||||
|
const webrtc::Call::Config::BitrateConfig& bitrate_config) {
|
||||||
|
config_.stream_bitrates = bitrate_config;
|
||||||
|
}
|
||||||
|
|
||||||
void FakeCall::SignalNetworkState(webrtc::Call::NetworkState state) {
|
void FakeCall::SignalNetworkState(webrtc::Call::NetworkState state) {
|
||||||
network_state_ = state;
|
network_state_ = state;
|
||||||
}
|
}
|
||||||
@ -350,8 +355,6 @@ class WebRtcVideoEngine2Test : public ::testing::Test {
|
|||||||
cricket::WebRtcVideoDecoderFactory* decoder_factory,
|
cricket::WebRtcVideoDecoderFactory* decoder_factory,
|
||||||
const std::vector<VideoCodec>& codecs);
|
const std::vector<VideoCodec>& codecs);
|
||||||
|
|
||||||
void TestStartBitrate(bool override_start_bitrate, int start_bitrate_bps);
|
|
||||||
|
|
||||||
WebRtcVideoEngine2 engine_;
|
WebRtcVideoEngine2 engine_;
|
||||||
VideoCodec default_codec_;
|
VideoCodec default_codec_;
|
||||||
VideoCodec default_red_codec_;
|
VideoCodec default_red_codec_;
|
||||||
@ -480,39 +483,6 @@ TEST_F(WebRtcVideoEngine2Test, SupportsAbsoluteSenderTimeHeaderExtension) {
|
|||||||
FAIL() << "Absolute Sender Time extension not in header-extension list.";
|
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<VideoMediaChannel> 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) {
|
TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) {
|
||||||
engine_.Init(rtc::Thread::Current());
|
engine_.Init(rtc::Thread::Current());
|
||||||
rtc::scoped_ptr<VideoMediaChannel> channel(
|
rtc::scoped_ptr<VideoMediaChannel> channel(
|
||||||
@ -877,25 +847,25 @@ class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test,
|
|||||||
return streams[streams.size() - 1];
|
return streams[streams.size() - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSendCodecsShouldWorkForBitrates(const char* min_bitrate,
|
void SetSendCodecsShouldWorkForBitrates(const char* min_bitrate_kbps,
|
||||||
const char* max_bitrate) {
|
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<VideoCodec> codecs;
|
std::vector<VideoCodec> codecs;
|
||||||
codecs.push_back(kVp8Codec);
|
codecs.push_back(kVp8Codec);
|
||||||
codecs[0].params[kCodecParamMinBitrate] = min_bitrate;
|
codecs[0].params[kCodecParamMinBitrate] = min_bitrate_kbps;
|
||||||
codecs[0].params[kCodecParamMaxBitrate] = max_bitrate;
|
codecs[0].params[kCodecParamStartBitrate] = start_bitrate_kbps;
|
||||||
|
codecs[0].params[kCodecParamMaxBitrate] = max_bitrate_kbps;
|
||||||
EXPECT_TRUE(channel_->SetSendCodecs(codecs));
|
EXPECT_TRUE(channel_->SetSendCodecs(codecs));
|
||||||
|
|
||||||
FakeVideoSendStream* stream = AddSendStream();
|
EXPECT_EQ(expected_min_bitrate_bps,
|
||||||
|
fake_call_->GetConfig().stream_bitrates.min_bitrate_bps);
|
||||||
std::vector<webrtc::VideoStream> video_streams = stream->GetVideoStreams();
|
EXPECT_EQ(expected_start_bitrate_bps,
|
||||||
ASSERT_EQ(1u, video_streams.size());
|
fake_call_->GetConfig().stream_bitrates.start_bitrate_bps);
|
||||||
EXPECT_EQ(atoi(min_bitrate), video_streams.back().min_bitrate_bps / 1000);
|
EXPECT_EQ(expected_max_bitrate_bps,
|
||||||
EXPECT_EQ(atoi(max_bitrate), video_streams.back().max_bitrate_bps / 1000);
|
fake_call_->GetConfig().stream_bitrates.max_bitrate_bps);
|
||||||
|
|
||||||
VideoCodec codec;
|
|
||||||
EXPECT_TRUE(channel_->GetSendCodec(&codec));
|
|
||||||
EXPECT_EQ(min_bitrate, codec.params[kCodecParamMinBitrate]);
|
|
||||||
EXPECT_EQ(max_bitrate, codec.params[kCodecParamMaxBitrate]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSetSendRtpHeaderExtensions(const std::string& cricket_ext,
|
void TestSetSendRtpHeaderExtensions(const std::string& cricket_ext,
|
||||||
@ -1630,8 +1600,19 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsChangesExistingStreams) {
|
|||||||
EXPECT_EQ(kVp8Codec360p.height, streams[0].height);
|
EXPECT_EQ(kVp8Codec360p.height, streams[0].height);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMinMaxBitrate) {
|
TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithBitrates) {
|
||||||
SetSendCodecsShouldWorkForBitrates("100", "200");
|
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) {
|
TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectsMaxLessThanMinBitrate) {
|
||||||
@ -1641,8 +1622,25 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectsMaxLessThanMinBitrate) {
|
|||||||
EXPECT_FALSE(channel_->SetSendCodecs(video_codecs));
|
EXPECT_FALSE(channel_->SetSendCodecs(video_codecs));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebRtcVideoChannel2Test, SetSendCodecsAcceptLargeMinMaxBitrate) {
|
TEST_F(WebRtcVideoChannel2Test,
|
||||||
SetSendCodecsShouldWorkForBitrates("1000", "2000");
|
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) {
|
TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMaxQuantization) {
|
||||||
|
@ -129,9 +129,11 @@ class FakeCall : public webrtc::Call {
|
|||||||
|
|
||||||
virtual webrtc::Call::Stats GetStats() const OVERRIDE;
|
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;
|
virtual void SignalNetworkState(webrtc::Call::NetworkState state) OVERRIDE;
|
||||||
|
|
||||||
const webrtc::Call::Config config_;
|
webrtc::Call::Config config_;
|
||||||
webrtc::Call::NetworkState network_state_;
|
webrtc::Call::NetworkState network_state_;
|
||||||
std::vector<webrtc::VideoCodec> codecs_;
|
std::vector<webrtc::VideoCodec> codecs_;
|
||||||
std::vector<FakeVideoSendStream*> video_send_streams_;
|
std::vector<FakeVideoSendStream*> video_send_streams_;
|
||||||
|
@ -65,8 +65,7 @@ class Call {
|
|||||||
: webrtc_config(NULL),
|
: webrtc_config(NULL),
|
||||||
send_transport(send_transport),
|
send_transport(send_transport),
|
||||||
voice_engine(NULL),
|
voice_engine(NULL),
|
||||||
overuse_callback(NULL),
|
overuse_callback(NULL) {}
|
||||||
stream_start_bitrate_bps(kDefaultStartBitrateBps) {}
|
|
||||||
|
|
||||||
static const int kDefaultStartBitrateBps;
|
static const int kDefaultStartBitrateBps;
|
||||||
|
|
||||||
@ -81,11 +80,20 @@ class Call {
|
|||||||
// captured frames. 'NULL' disables the callback.
|
// captured frames. 'NULL' disables the callback.
|
||||||
LoadObserver* overuse_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
|
// Note: This is currently set only for video and is per-stream rather of
|
||||||
// for the entire link.
|
// for the entire link.
|
||||||
// TODO(pbos): Set start bitrate for entire Call.
|
// 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 {
|
struct Stats {
|
||||||
@ -121,6 +129,13 @@ class Call {
|
|||||||
// pacing delay, etc.
|
// pacing delay, etc.
|
||||||
virtual Stats GetStats() const = 0;
|
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 void SignalNetworkState(NetworkState state) = 0;
|
||||||
|
|
||||||
virtual ~Call() {}
|
virtual ~Call() {}
|
||||||
|
@ -119,6 +119,8 @@ class Call : public webrtc::Call, public PacketReceiver {
|
|||||||
virtual DeliveryStatus DeliverPacket(const uint8_t* packet,
|
virtual DeliveryStatus DeliverPacket(const uint8_t* packet,
|
||||||
size_t length) OVERRIDE;
|
size_t length) OVERRIDE;
|
||||||
|
|
||||||
|
virtual void SetBitrateConfig(
|
||||||
|
const webrtc::Call::Config::BitrateConfig& bitrate_config) OVERRIDE;
|
||||||
virtual void SignalNetworkState(NetworkState state) OVERRIDE;
|
virtual void SignalNetworkState(NetworkState state) OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -176,6 +178,14 @@ Call::Call(webrtc::VideoEngine* video_engine, const Call::Config& config)
|
|||||||
assert(video_engine != NULL);
|
assert(video_engine != NULL);
|
||||||
assert(config.send_transport != 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) {
|
if (config.overuse_callback) {
|
||||||
overuse_observer_proxy_.reset(
|
overuse_observer_proxy_.reset(
|
||||||
new CpuOveruseObserverProxy(config.overuse_callback));
|
new CpuOveruseObserverProxy(config.overuse_callback));
|
||||||
@ -213,15 +223,10 @@ VideoSendStream* Call::CreateVideoSendStream(
|
|||||||
|
|
||||||
// TODO(mflodman): Base the start bitrate on a current bandwidth estimate, if
|
// TODO(mflodman): Base the start bitrate on a current bandwidth estimate, if
|
||||||
// the call has already started.
|
// the call has already started.
|
||||||
VideoSendStream* send_stream =
|
VideoSendStream* send_stream = new VideoSendStream(
|
||||||
new VideoSendStream(config_.send_transport,
|
config_.send_transport, overuse_observer_proxy_.get(), video_engine_,
|
||||||
overuse_observer_proxy_.get(),
|
config, encoder_config, suspended_send_ssrcs_, base_channel_id_,
|
||||||
video_engine_,
|
config_.stream_bitrates);
|
||||||
config,
|
|
||||||
encoder_config,
|
|
||||||
suspended_send_ssrcs_,
|
|
||||||
base_channel_id_,
|
|
||||||
config_.stream_start_bitrate_bps);
|
|
||||||
|
|
||||||
// This needs to be taken before send_crit_ as both locks need to be held
|
// This needs to be taken before send_crit_ as both locks need to be held
|
||||||
// while changing network state.
|
// while changing network state.
|
||||||
@ -342,6 +347,30 @@ Call::Stats Call::GetStats() const {
|
|||||||
return stats;
|
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<uint32_t, VideoSendStream*>::const_iterator it =
|
||||||
|
send_ssrcs_.begin();
|
||||||
|
it != send_ssrcs_.end(); ++it) {
|
||||||
|
it->second->SetBitrateConfig(bitrate_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Call::SignalNetworkState(NetworkState state) {
|
void Call::SignalNetworkState(NetworkState state) {
|
||||||
// Take crit for entire function, it needs to be held while updating streams
|
// Take crit for entire function, it needs to be held while updating streams
|
||||||
// to guarantee a consistent state across streams.
|
// to guarantee a consistent state across streams.
|
||||||
|
@ -640,7 +640,7 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) {
|
|||||||
|
|
||||||
Call::Config GetSenderCallConfig() OVERRIDE {
|
Call::Config GetSenderCallConfig() OVERRIDE {
|
||||||
Call::Config config = EndToEndTest::GetSenderCallConfig();
|
Call::Config config = EndToEndTest::GetSenderCallConfig();
|
||||||
config.stream_start_bitrate_bps = kInitialBitrateKbps * 1000;
|
config.stream_bitrates.start_bitrate_bps = kInitialBitrateKbps * 1000;
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1566,7 +1566,7 @@ TEST_F(EndToEndTest, GetStats) {
|
|||||||
|
|
||||||
Call::Config GetSenderCallConfig() OVERRIDE {
|
Call::Config GetSenderCallConfig() OVERRIDE {
|
||||||
Call::Config config = EndToEndTest::GetSenderCallConfig();
|
Call::Config config = EndToEndTest::GetSenderCallConfig();
|
||||||
config.stream_start_bitrate_bps = kStartBitrateBps;
|
config.stream_bitrates.start_bitrate_bps = kStartBitrateBps;
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +123,12 @@ void Loopback() {
|
|||||||
pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs();
|
pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs();
|
||||||
test::DirectTransport transport(pipe_config);
|
test::DirectTransport transport(pipe_config);
|
||||||
Call::Config call_config(&transport);
|
Call::Config call_config(&transport);
|
||||||
call_config.stream_start_bitrate_bps =
|
call_config.stream_bitrates.min_bitrate_bps =
|
||||||
|
static_cast<int>(flags::MinBitrate()) * 1000;
|
||||||
|
call_config.stream_bitrates.start_bitrate_bps =
|
||||||
static_cast<int>(flags::StartBitrate()) * 1000;
|
static_cast<int>(flags::StartBitrate()) * 1000;
|
||||||
|
call_config.stream_bitrates.max_bitrate_bps =
|
||||||
|
static_cast<int>(flags::MaxBitrate()) * 1000;
|
||||||
scoped_ptr<Call> call(Call::Create(call_config));
|
scoped_ptr<Call> call(Call::Create(call_config));
|
||||||
|
|
||||||
// Loopback, call sends to itself.
|
// Loopback, call sends to itself.
|
||||||
@ -157,9 +161,9 @@ void Loopback() {
|
|||||||
VideoStream* stream = &encoder_config.streams[0];
|
VideoStream* stream = &encoder_config.streams[0];
|
||||||
stream->width = flags::Width();
|
stream->width = flags::Width();
|
||||||
stream->height = flags::Height();
|
stream->height = flags::Height();
|
||||||
stream->min_bitrate_bps = static_cast<int>(flags::MinBitrate()) * 1000;
|
stream->min_bitrate_bps = call_config.stream_bitrates.min_bitrate_bps;
|
||||||
stream->target_bitrate_bps = static_cast<int>(flags::MaxBitrate()) * 1000;
|
stream->target_bitrate_bps = call_config.stream_bitrates.max_bitrate_bps;
|
||||||
stream->max_bitrate_bps = static_cast<int>(flags::MaxBitrate()) * 1000;
|
stream->max_bitrate_bps = call_config.stream_bitrates.max_bitrate_bps;
|
||||||
stream->max_framerate = 30;
|
stream->max_framerate = 30;
|
||||||
stream->max_qp = 56;
|
stream->max_qp = 56;
|
||||||
|
|
||||||
|
@ -403,7 +403,7 @@ void RampUpTest::RunRampUpTest(bool rtx,
|
|||||||
|
|
||||||
Call::Config call_config(&stream_observer);
|
Call::Config call_config(&stream_observer);
|
||||||
if (start_bitrate_bps != 0) {
|
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);
|
stream_observer.set_start_bitrate_bps(start_bitrate_bps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,20 +115,26 @@ VideoSendStream::VideoSendStream(
|
|||||||
const VideoEncoderConfig& encoder_config,
|
const VideoEncoderConfig& encoder_config,
|
||||||
const std::map<uint32_t, RtpState>& suspended_ssrcs,
|
const std::map<uint32_t, RtpState>& suspended_ssrcs,
|
||||||
int base_channel,
|
int base_channel,
|
||||||
int start_bitrate_bps)
|
Call::Config::BitrateConfig bitrate_config)
|
||||||
: transport_adapter_(transport),
|
: transport_adapter_(transport),
|
||||||
encoded_frame_proxy_(config.post_encode_callback),
|
encoded_frame_proxy_(config.post_encode_callback),
|
||||||
config_(config),
|
config_(config),
|
||||||
start_bitrate_bps_(start_bitrate_bps),
|
bitrate_config_(bitrate_config),
|
||||||
suspended_ssrcs_(suspended_ssrcs),
|
suspended_ssrcs_(suspended_ssrcs),
|
||||||
external_codec_(NULL),
|
external_codec_(NULL),
|
||||||
channel_(-1),
|
channel_(-1),
|
||||||
use_default_bitrate_(true),
|
use_config_bitrate_(true),
|
||||||
stats_proxy_(config) {
|
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_ = ViEBase::GetInterface(video_engine);
|
||||||
video_engine_base_->CreateChannel(channel_, base_channel);
|
video_engine_base_->CreateChannel(channel_, base_channel);
|
||||||
assert(channel_ != -1);
|
assert(channel_ != -1);
|
||||||
assert(start_bitrate_bps_ > 0);
|
|
||||||
|
|
||||||
rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine);
|
rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine);
|
||||||
assert(rtp_rtcp_ != NULL);
|
assert(rtp_rtcp_ != NULL);
|
||||||
@ -385,10 +391,20 @@ bool VideoSendStream::ReconfigureVideoEncoder(
|
|||||||
video_codec.qpMax = std::max(video_codec.qpMax,
|
video_codec.qpMax = std::max(video_codec.qpMax,
|
||||||
static_cast<unsigned int>(streams[i].max_qp));
|
static_cast<unsigned int>(streams[i].max_qp));
|
||||||
}
|
}
|
||||||
|
// Clamp bitrates to the bitrate config.
|
||||||
|
if (video_codec.minBitrate <
|
||||||
|
static_cast<unsigned int>(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<unsigned int>(bitrate_config_.max_bitrate_bps / 1000)) {
|
||||||
|
video_codec.maxBitrate = bitrate_config_.max_bitrate_bps / 1000;
|
||||||
|
}
|
||||||
unsigned int start_bitrate_bps;
|
unsigned int start_bitrate_bps;
|
||||||
if (codec_->GetCodecTargetBitrate(channel_, &start_bitrate_bps) != 0 ||
|
if (codec_->GetCodecTargetBitrate(channel_, &start_bitrate_bps) != 0 ||
|
||||||
use_default_bitrate_) {
|
use_config_bitrate_) {
|
||||||
start_bitrate_bps = start_bitrate_bps_;
|
start_bitrate_bps = bitrate_config_.start_bitrate_bps;
|
||||||
}
|
}
|
||||||
video_codec.startBitrate =
|
video_codec.startBitrate =
|
||||||
static_cast<unsigned int>(start_bitrate_bps) / 1000;
|
static_cast<unsigned int>(start_bitrate_bps) / 1000;
|
||||||
@ -417,7 +433,8 @@ bool VideoSendStream::ReconfigureVideoEncoder(
|
|||||||
rtp_rtcp_->SetMinTransmitBitrate(channel_,
|
rtp_rtcp_->SetMinTransmitBitrate(channel_,
|
||||||
config.min_transmit_bitrate_bps / 1000);
|
config.min_transmit_bitrate_bps / 1000);
|
||||||
|
|
||||||
use_default_bitrate_ = false;
|
encoder_config_ = config;
|
||||||
|
use_config_bitrate_ = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,6 +497,19 @@ std::map<uint32_t, RtpState> VideoSendStream::GetRtpStates() const {
|
|||||||
return rtp_states;
|
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) {
|
void VideoSendStream::SignalNetworkState(Call::NetworkState state) {
|
||||||
// When network goes up, enable RTCP status before setting transmission state.
|
// When network goes up, enable RTCP status before setting transmission state.
|
||||||
// When it goes down, disable RTCP afterwards. This ensures that any packets
|
// When it goes down, disable RTCP afterwards. This ensures that any packets
|
||||||
|
@ -49,7 +49,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
|
|||||||
const VideoEncoderConfig& encoder_config,
|
const VideoEncoderConfig& encoder_config,
|
||||||
const std::map<uint32_t, RtpState>& suspended_ssrcs,
|
const std::map<uint32_t, RtpState>& suspended_ssrcs,
|
||||||
int base_channel,
|
int base_channel,
|
||||||
int start_bitrate);
|
Call::Config::BitrateConfig bitrate_config);
|
||||||
|
|
||||||
virtual ~VideoSendStream();
|
virtual ~VideoSendStream();
|
||||||
|
|
||||||
@ -72,6 +72,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
|
|||||||
typedef std::map<uint32_t, RtpState> RtpStateMap;
|
typedef std::map<uint32_t, RtpState> RtpStateMap;
|
||||||
RtpStateMap GetRtpStates() const;
|
RtpStateMap GetRtpStates() const;
|
||||||
|
|
||||||
|
void SetBitrateConfig(const Call::Config::BitrateConfig& bitrate_config);
|
||||||
void SignalNetworkState(Call::NetworkState state);
|
void SignalNetworkState(Call::NetworkState state);
|
||||||
|
|
||||||
int GetPacerQueuingDelayMs() const;
|
int GetPacerQueuingDelayMs() const;
|
||||||
@ -81,7 +82,8 @@ class VideoSendStream : public webrtc::VideoSendStream,
|
|||||||
TransportAdapter transport_adapter_;
|
TransportAdapter transport_adapter_;
|
||||||
EncodedFrameCallbackAdapter encoded_frame_proxy_;
|
EncodedFrameCallbackAdapter encoded_frame_proxy_;
|
||||||
const VideoSendStream::Config config_;
|
const VideoSendStream::Config config_;
|
||||||
const int start_bitrate_bps_;
|
VideoEncoderConfig encoder_config_;
|
||||||
|
Call::Config::BitrateConfig bitrate_config_;
|
||||||
std::map<uint32_t, RtpState> suspended_ssrcs_;
|
std::map<uint32_t, RtpState> suspended_ssrcs_;
|
||||||
|
|
||||||
ViEBase* video_engine_base_;
|
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
|
// Used as a workaround to indicate that we should be using the configured
|
||||||
// start bitrate initially, instead of the one reported by VideoEngine (which
|
// start bitrate initially, instead of the one reported by VideoEngine (which
|
||||||
// defaults to too high).
|
// defaults to too high).
|
||||||
bool use_default_bitrate_;
|
bool use_config_bitrate_;
|
||||||
|
|
||||||
SendStatisticsProxy stats_proxy_;
|
SendStatisticsProxy stats_proxy_;
|
||||||
};
|
};
|
||||||
|
@ -1583,4 +1583,102 @@ TEST_F(VideoSendStreamTest, TranslatesTwoLayerScreencastToTargetBitrate) {
|
|||||||
|
|
||||||
RunBaseTest(&test);
|
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<unsigned int>(kMinBitrateKbps),
|
||||||
|
codecSettings->minBitrate);
|
||||||
|
EXPECT_EQ(static_cast<unsigned int>(kStartBitrateKbps),
|
||||||
|
codecSettings->startBitrate);
|
||||||
|
EXPECT_EQ(static_cast<unsigned int>(kMaxBitrateKbps),
|
||||||
|
codecSettings->maxBitrate);
|
||||||
|
observation_complete_->Set();
|
||||||
|
} else if (num_initializations_ == 1) {
|
||||||
|
EXPECT_EQ(static_cast<unsigned int>(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<unsigned int>(kIncreasedMaxBitrateKbps),
|
||||||
|
codecSettings->maxBitrate);
|
||||||
|
EXPECT_EQ(static_cast<unsigned int>(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<VideoReceiveStream::Config>* 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
|
} // namespace webrtc
|
||||||
|
Loading…
x
Reference in New Issue
Block a user