Allow applications to control audio send bitrate through RtpParameters.

This change builds on top of the refactoring in https://codereview.webrtc.org/1841083008/, and enables WebRTC client applications to control the max send bitrate for every audio stream through RtpParameters.

The AudioSendStream now stores the last codec spec, and whenever a global or per-stream bitrate limit changes, the effective limit (smaller of the two) is recomputed and the codec is reconfigured with that bitrate.

TBR=pthatcher
BUG=

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

Cr-Commit-Position: refs/heads/master@{#12290}
This commit is contained in:
skvlad 2016-04-07 22:59:22 -07:00 committed by Commit bot
parent e07a3af678
commit e0d4637bea
7 changed files with 271 additions and 69 deletions

View File

@ -3385,23 +3385,32 @@ TEST_F(WebRtcSessionTest, SetAudioPlayout) {
EXPECT_EQ(1, volume);
}
TEST_F(WebRtcSessionTest, AudioMaxSendBitrateNotImplemented) {
// This test verifies that RtpParameters for audio RtpSenders cannot be
// changed.
// TODO(skvlad): Update the test after adding support for bitrate limiting in
// WebRtcAudioSendStream.
TEST_F(WebRtcSessionTest, SetAudioMaxSendBitrate) {
Init();
SendAudioVideoStream1();
CreateAndSetRemoteOfferAndLocalAnswer();
cricket::FakeVoiceMediaChannel* channel = media_engine_->GetVoiceChannel(0);
ASSERT_TRUE(channel != NULL);
uint32_t send_ssrc = channel->send_streams()[0].first_ssrc();
EXPECT_EQ(-1, channel->max_bps());
webrtc::RtpParameters params = session_->GetAudioRtpParameters(send_ssrc);
EXPECT_EQ(1, params.encodings.size());
EXPECT_EQ(-1, params.encodings[0].max_bitrate_bps);
params.encodings[0].max_bitrate_bps = 1000;
EXPECT_TRUE(session_->SetAudioRtpParameters(send_ssrc, params));
EXPECT_EQ(0, params.encodings.size());
params.encodings.push_back(webrtc::RtpEncodingParameters());
EXPECT_FALSE(session_->SetAudioRtpParameters(send_ssrc, params));
// Read back the parameters and verify they have been changed.
params = session_->GetAudioRtpParameters(send_ssrc);
EXPECT_EQ(1, params.encodings.size());
EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps);
// Verify that the audio channel received the new parameters.
params = channel->GetRtpParameters(send_ssrc);
EXPECT_EQ(1, params.encodings.size());
EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps);
// Verify that the global bitrate limit has not been changed.
EXPECT_EQ(-1, channel->max_bps());
}
TEST_F(WebRtcSessionTest, SetAudioSend) {

View File

@ -904,6 +904,9 @@ class VoiceMediaChannel : public MediaChannel {
virtual ~VoiceMediaChannel() {}
virtual bool SetSendParameters(const AudioSendParameters& params) = 0;
virtual bool SetRecvParameters(const AudioRecvParameters& params) = 0;
virtual webrtc::RtpParameters GetRtpParameters(uint32_t ssrc) const = 0;
virtual bool SetRtpParameters(uint32_t ssrc,
const webrtc::RtpParameters& parameters) = 0;
// Starts or stops playout of received audio.
virtual bool SetPlayout(bool playout) = 0;
// Starts or stops sending (and potentially capture) of local audio.

View File

@ -1081,13 +1081,16 @@ int WebRtcVoiceEngine::CreateVoEChannel() {
class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
: public AudioSource::Sink {
public:
WebRtcAudioSendStream(int ch, webrtc::AudioTransport* voe_audio_transport,
uint32_t ssrc, const std::string& c_name,
WebRtcAudioSendStream(int ch,
webrtc::AudioTransport* voe_audio_transport,
uint32_t ssrc,
const std::string& c_name,
const std::vector<webrtc::RtpExtension>& extensions,
webrtc::Call* call)
: voe_audio_transport_(voe_audio_transport),
call_(call),
config_(nullptr) {
config_(nullptr),
rtp_parameters_(CreateRtpParametersWithOneEncoding()) {
RTC_DCHECK_GE(ch, 0);
// TODO(solenberg): Once we're not using FakeWebRtcVoiceEngine anymore:
// RTC_DCHECK(voe_audio_transport);
@ -1198,6 +1201,15 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
return config_.voe_channel_id;
}
const webrtc::RtpParameters& rtp_parameters() const {
return rtp_parameters_;
}
void set_rtp_parameters(const webrtc::RtpParameters& parameters) {
RTC_CHECK_EQ(1UL, parameters.encodings.size());
rtp_parameters_ = parameters;
}
private:
void UpdateSendState() {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
@ -1223,6 +1235,7 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
// goes away.
AudioSource* source_ = nullptr;
bool send_ = false;
webrtc::RtpParameters rtp_parameters_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WebRtcAudioSendStream);
};
@ -1359,7 +1372,7 @@ bool WebRtcVoiceMediaChannel::SetSendParameters(
}
}
if (!SetMaxSendBandwidth(params.max_bandwidth_bps)) {
if (!SetSendBitrate(params.max_bandwidth_bps)) {
return false;
}
return SetOptions(params.options);
@ -1393,6 +1406,51 @@ bool WebRtcVoiceMediaChannel::SetRecvParameters(
return true;
}
webrtc::RtpParameters WebRtcVoiceMediaChannel::GetRtpParameters(
uint32_t ssrc) const {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
auto it = send_streams_.find(ssrc);
if (it == send_streams_.end()) {
LOG(LS_WARNING) << "Attempting to get RTP parameters for stream with ssrc "
<< ssrc << " which doesn't exist.";
return webrtc::RtpParameters();
}
return it->second->rtp_parameters();
}
bool WebRtcVoiceMediaChannel::SetRtpParameters(
uint32_t ssrc,
const webrtc::RtpParameters& parameters) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
if (!ValidateRtpParameters(parameters)) {
return false;
}
auto it = send_streams_.find(ssrc);
if (it == send_streams_.end()) {
LOG(LS_WARNING) << "Attempting to set RTP parameters for stream with ssrc "
<< ssrc << " which doesn't exist.";
return false;
}
if (!SetChannelParameters(it->second->channel(), parameters)) {
LOG(LS_WARNING) << "Failed to set RtpParameters.";
return false;
}
it->second->set_rtp_parameters(parameters);
return true;
}
bool WebRtcVoiceMediaChannel::ValidateRtpParameters(
const webrtc::RtpParameters& rtp_parameters) {
if (rtp_parameters.encodings.size() != 1) {
LOG(LS_ERROR)
<< "Attempted to set RtpParameters without exactly one encoding";
return false;
}
return true;
}
bool WebRtcVoiceMediaChannel::SetOptions(const AudioOptions& options) {
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
LOG(LS_INFO) << "Setting voice channel options: "
@ -1587,7 +1645,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
// Cache the codecs in order to configure the channel created later.
for (const auto& ch : send_streams_) {
if (!SetSendCodecs(ch.second->channel())) {
if (!SetSendCodecs(ch.second->channel(), ch.second->rtp_parameters())) {
return false;
}
}
@ -1614,7 +1672,9 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
}
// Apply current codec settings to a single voe::Channel used for sending.
bool WebRtcVoiceMediaChannel::SetSendCodecs(int channel) {
bool WebRtcVoiceMediaChannel::SetSendCodecs(
int channel,
const webrtc::RtpParameters& rtp_parameters) {
// Disable VAD, FEC, and RED unless we know the other side wants them.
engine()->voe()->codec()->SetVADStatus(channel, false);
engine()->voe()->rtp()->SetNACKStatus(channel, false, 0);
@ -1682,10 +1742,9 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(int channel) {
}
}
}
if (send_bitrate_setting_) {
SetSendBitrateInternal(send_bitrate_bps_);
}
// TODO(solenberg): SetSendBitrate() yields another call to SetSendCodec().
// Check if it is possible to fuse with the previous call in this function.
SetChannelParameters(channel, rtp_parameters);
// Set the CN payloadtype and the VAD status.
if (send_codec_spec_.cng_payload_type != -1) {
@ -1880,13 +1939,14 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
// delete the channel in case failure happens below.
webrtc::AudioTransport* audio_transport =
engine()->voe()->base()->audio_transport();
send_streams_.insert(std::make_pair(ssrc, new WebRtcAudioSendStream(
channel, audio_transport, ssrc, sp.cname, send_rtp_extensions_, call_)));
WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
channel, audio_transport, ssrc, sp.cname, send_rtp_extensions_, call_);
send_streams_.insert(std::make_pair(ssrc, stream));
// Set the current codecs to be used for the new channel. We need to do this
// after adding the channel to send_channels_, because of how max bitrate is
// currently being configured by SetSendCodec().
if (HasSendCodec() && !SetSendCodecs(channel)) {
if (HasSendCodec() && !SetSendCodecs(channel, stream->rtp_parameters())) {
RemoveSendStream(ssrc);
return false;
}
@ -2310,18 +2370,34 @@ bool WebRtcVoiceMediaChannel::MuteStream(uint32_t ssrc, bool muted) {
return true;
}
// TODO(minyue): SetMaxSendBandwidth() is subject to be renamed to
// SetMaxSendBitrate() in future.
bool WebRtcVoiceMediaChannel::SetMaxSendBandwidth(int bps) {
LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetMaxSendBandwidth.";
return SetSendBitrateInternal(bps);
bool WebRtcVoiceMediaChannel::SetSendBitrate(int bps) {
LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetSendBitrate.";
send_bitrate_bps_ = bps;
for (const auto& kv : send_streams_) {
if (!SetChannelParameters(kv.second->channel(),
kv.second->rtp_parameters())) {
return false;
}
}
return true;
}
bool WebRtcVoiceMediaChannel::SetSendBitrateInternal(int bps) {
LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetSendBitrateInternal.";
bool WebRtcVoiceMediaChannel::SetChannelParameters(
int channel,
const webrtc::RtpParameters& parameters) {
RTC_CHECK_EQ(1UL, parameters.encodings.size());
return SetSendBitrate(
channel,
MinPositive(send_bitrate_bps_, parameters.encodings[0].max_bitrate_bps));
}
send_bitrate_setting_ = true;
send_bitrate_bps_ = bps;
bool WebRtcVoiceMediaChannel::SetSendBitrate(int channel, int bps) {
// Bitrate is auto by default.
// TODO(bemasc): Fix this so that if SetMaxSendBandwidth(50) is followed by
// SetMaxSendBandwith(0), the second call removes the previous limit.
if (bps <= 0)
return true;
if (!HasSendCodec()) {
LOG(LS_INFO) << "The send codec has not been set up yet. "
@ -2329,24 +2405,16 @@ bool WebRtcVoiceMediaChannel::SetSendBitrateInternal(int bps) {
return true;
}
// Bitrate is auto by default.
// TODO(bemasc): Fix this so that if SetMaxSendBandwidth(50) is followed by
// SetMaxSendBandwith(0), the second call removes the previous limit.
if (bps <= 0)
return true;
webrtc::CodecInst codec = send_codec_spec_.codec_inst;
bool is_multi_rate = WebRtcVoiceCodecs::IsCodecMultiRate(codec);
if (is_multi_rate) {
// If codec is multi-rate then just set the bitrate.
codec.rate = bps;
for (const auto& ch : send_streams_) {
if (!SetSendCodec(ch.second->channel(), codec)) {
LOG(LS_INFO) << "Failed to set codec " << codec.plname
<< " to bitrate " << bps << " bps.";
return false;
}
if (!SetSendCodec(channel, codec)) {
LOG(LS_INFO) << "Failed to set codec " << codec.plname << " to bitrate "
<< bps << " bps.";
return false;
}
return true;
} else {

View File

@ -146,6 +146,10 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
bool SetSendParameters(const AudioSendParameters& params) override;
bool SetRecvParameters(const AudioRecvParameters& params) override;
webrtc::RtpParameters GetRtpParameters(uint32_t ssrc) const override;
bool SetRtpParameters(uint32_t ssrc,
const webrtc::RtpParameters& parameters) override;
bool SetPlayout(bool playout) override;
bool PausePlayout();
bool ResumePlayout();
@ -206,10 +210,9 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
bool SetOptions(const AudioOptions& options);
bool SetRecvCodecs(const std::vector<AudioCodec>& codecs);
bool SetSendCodecs(const std::vector<AudioCodec>& codecs);
bool SetSendCodecs(int channel);
bool SetSendCodecs(int channel, const webrtc::RtpParameters& rtp_parameters);
void SetNack(int channel, bool nack_enabled);
bool SetSendCodec(int channel, const webrtc::CodecInst& send_codec);
bool SetMaxSendBandwidth(int bps);
bool SetLocalSource(uint32_t ssrc, AudioSource* source);
bool MuteStream(uint32_t ssrc, bool mute);
@ -223,16 +226,19 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
bool IsDefaultRecvStream(uint32_t ssrc) {
return default_recv_ssrc_ == static_cast<int64_t>(ssrc);
}
bool SetSendBitrateInternal(int bps);
bool SetSendBitrate(int bps);
bool SetChannelParameters(int channel,
const webrtc::RtpParameters& parameters);
bool SetSendBitrate(int channel, int bps);
bool HasSendCodec() const {
return send_codec_spec_.codec_inst.pltype != -1;
}
bool ValidateRtpParameters(const webrtc::RtpParameters& parameters);
rtc::ThreadChecker worker_thread_checker_;
WebRtcVoiceEngine* const engine_ = nullptr;
std::vector<AudioCodec> recv_codecs_;
bool send_bitrate_setting_ = false;
int send_bitrate_bps_ = 0;
AudioOptions options_;
rtc::Optional<int> dtmf_payload_type_;

View File

@ -214,6 +214,56 @@ class WebRtcVoiceEngineTestFake : public testing::Test {
EXPECT_EQ(expected_bitrate, temp_codec.rate);
}
// Sets the per-stream maximum bitrate limit for the specified SSRC.
bool SetMaxBitrateForStream(int32_t ssrc, int bitrate) {
webrtc::RtpParameters parameters = channel_->GetRtpParameters(ssrc);
EXPECT_EQ(1UL, parameters.encodings.size());
parameters.encodings[0].max_bitrate_bps = bitrate;
return channel_->SetRtpParameters(ssrc, parameters);
}
bool SetGlobalMaxBitrate(const cricket::AudioCodec& codec, int bitrate) {
cricket::AudioSendParameters send_parameters;
send_parameters.codecs.push_back(codec);
send_parameters.max_bandwidth_bps = bitrate;
return channel_->SetSendParameters(send_parameters);
}
int GetCodecBitrate(int32_t ssrc) {
cricket::WebRtcVoiceMediaChannel* media_channel =
static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
int channel = media_channel->GetSendChannelId(ssrc);
EXPECT_NE(-1, channel);
webrtc::CodecInst codec;
EXPECT_FALSE(voe_.GetSendCodec(channel, codec));
return codec.rate;
}
void SetAndExpectMaxBitrate(const cricket::AudioCodec& codec,
int global_max,
int stream_max,
bool expected_result,
int expected_codec_bitrate) {
// Clear the bitrate limit from the previous test case.
EXPECT_TRUE(SetMaxBitrateForStream(kSsrc1, -1));
// Attempt to set the requested bitrate limits.
EXPECT_TRUE(SetGlobalMaxBitrate(codec, global_max));
EXPECT_EQ(expected_result, SetMaxBitrateForStream(kSsrc1, stream_max));
// Verify that reading back the parameters gives results
// consistent with the Set() result.
webrtc::RtpParameters resulting_parameters =
channel_->GetRtpParameters(kSsrc1);
EXPECT_EQ(1UL, resulting_parameters.encodings.size());
EXPECT_EQ(expected_result ? stream_max : -1,
resulting_parameters.encodings[0].max_bitrate_bps);
// Verify that the codec settings have the expected bitrate.
EXPECT_EQ(expected_codec_bitrate, GetCodecBitrate(kSsrc1));
}
void TestSetSendRtpHeaderExtensions(const std::string& ext) {
EXPECT_TRUE(SetupSendStream());
@ -772,6 +822,88 @@ TEST_F(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthCbr) {
EXPECT_EQ(64000, codec.rate);
}
// Test that the per-stream bitrate limit and the global
// bitrate limit both apply.
TEST_F(WebRtcVoiceEngineTestFake, SetMaxBitratePerStream) {
EXPECT_TRUE(SetupSendStream());
// opus, default bitrate == 64000.
SetAndExpectMaxBitrate(kOpusCodec, 0, 0, true, 64000);
SetAndExpectMaxBitrate(kOpusCodec, 48000, 0, true, 48000);
SetAndExpectMaxBitrate(kOpusCodec, 48000, 64000, true, 48000);
SetAndExpectMaxBitrate(kOpusCodec, 64000, 48000, true, 48000);
// CBR codecs allow both maximums to exceed the bitrate.
SetAndExpectMaxBitrate(kPcmuCodec, 0, 0, true, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 64001, 0, true, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 0, 64001, true, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 64001, 64001, true, 64000);
// CBR codecs don't allow per stream maximums to be too low.
SetAndExpectMaxBitrate(kPcmuCodec, 0, 63999, false, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 64001, 63999, false, 64000);
}
// Test that an attempt to set RtpParameters for a stream that does not exist
// fails.
TEST_F(WebRtcVoiceEngineTestFake, CannotSetMaxBitrateForNonexistentStream) {
EXPECT_TRUE(SetupChannel());
webrtc::RtpParameters nonexistent_parameters =
channel_->GetRtpParameters(kSsrc1);
EXPECT_EQ(0, nonexistent_parameters.encodings.size());
nonexistent_parameters.encodings.push_back(webrtc::RtpEncodingParameters());
EXPECT_FALSE(channel_->SetRtpParameters(kSsrc1, nonexistent_parameters));
}
TEST_F(WebRtcVoiceEngineTestFake,
CannotSetRtpParametersWithIncorrectNumberOfEncodings) {
// This test verifies that setting RtpParameters succeeds only if
// the structure contains exactly one encoding.
// TODO(skvlad): Update this test when we start supporting setting parameters
// for each encoding individually.
EXPECT_TRUE(SetupSendStream());
// Setting RtpParameters with no encoding is expected to fail.
webrtc::RtpParameters parameters;
EXPECT_FALSE(channel_->SetRtpParameters(kSsrc1, parameters));
// Setting RtpParameters with exactly one encoding should succeed.
parameters.encodings.push_back(webrtc::RtpEncodingParameters());
EXPECT_TRUE(channel_->SetRtpParameters(kSsrc1, parameters));
// Two or more encodings should result in failure.
parameters.encodings.push_back(webrtc::RtpEncodingParameters());
EXPECT_FALSE(channel_->SetRtpParameters(kSsrc1, parameters));
}
// Test that SetRtpParameters configures the correct encoding channel for each
// SSRC.
TEST_F(WebRtcVoiceEngineTestFake, RtpParametersArePerStream) {
SetupForMultiSendStream();
// Create send streams.
for (uint32_t ssrc : kSsrcs4) {
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
}
// Configure one stream to be limited by the stream config, another to be
// limited by the global max, and the third one with no per-stream limit
// (still subject to the global limit).
EXPECT_TRUE(SetGlobalMaxBitrate(kOpusCodec, 64000));
EXPECT_TRUE(SetMaxBitrateForStream(kSsrcs4[0], 48000));
EXPECT_TRUE(SetMaxBitrateForStream(kSsrcs4[1], 96000));
EXPECT_TRUE(SetMaxBitrateForStream(kSsrcs4[2], -1));
EXPECT_EQ(48000, GetCodecBitrate(kSsrcs4[0]));
EXPECT_EQ(64000, GetCodecBitrate(kSsrcs4[1]));
EXPECT_EQ(64000, GetCodecBitrate(kSsrcs4[2]));
// Remove the global cap; the streams should switch to their respective
// maximums (or remain unchanged if there was no other limit on them.)
EXPECT_TRUE(SetGlobalMaxBitrate(kOpusCodec, -1));
EXPECT_EQ(48000, GetCodecBitrate(kSsrcs4[0]));
EXPECT_EQ(96000, GetCodecBitrate(kSsrcs4[1]));
EXPECT_EQ(64000, GetCodecBitrate(kSsrcs4[2]));
}
// Test that we apply codecs properly.
TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecs) {
EXPECT_TRUE(SetupSendStream());

View File

@ -1409,9 +1409,7 @@ webrtc::RtpParameters VoiceChannel::GetRtpParameters(uint32_t ssrc) const {
}
webrtc::RtpParameters VoiceChannel::GetRtpParameters_w(uint32_t ssrc) const {
// Not yet implemented.
// TODO(skvlad): Add support for limiting send bitrate for audio channels.
return webrtc::RtpParameters();
return media_channel()->GetRtpParameters(ssrc);
}
bool VoiceChannel::SetRtpParameters(uint32_t ssrc,
@ -1422,9 +1420,7 @@ bool VoiceChannel::SetRtpParameters(uint32_t ssrc,
bool VoiceChannel::SetRtpParameters_w(uint32_t ssrc,
webrtc::RtpParameters parameters) {
// Not yet implemented.
// TODO(skvlad): Add support for limiting send bitrate for audio channels.
return false;
return media_channel()->SetRtpParameters(ssrc, parameters);
}
bool VoiceChannel::GetStats(VoiceMediaInfo* stats) {

View File

@ -2357,24 +2357,12 @@ TEST_F(VoiceChannelTest, SendBundleToBundleWithRtcpMuxSecure) {
Base::SendBundleToBundle(kAudioPts, arraysize(kAudioPts), true, true);
}
TEST_F(VoiceChannelTest, GetRtpParametersIsNotImplemented) {
// These tests verify that the Get/SetRtpParameters methods for VoiceChannel
// always fail as they are not implemented.
// TODO(skvlad): Replace with full tests when support for bitrate limiting
// for audio RtpSenders is added.
CreateChannels(0, 0);
EXPECT_TRUE(
channel1_->SetLocalContent(&local_media_content1_, CA_OFFER, NULL));
webrtc::RtpParameters voice_parameters = channel1_->GetRtpParameters(kSsrc1);
EXPECT_EQ(0UL, voice_parameters.encodings.size());
TEST_F(VoiceChannelTest, DefaultMaxBitrateIsUnlimited) {
Base::DefaultMaxBitrateIsUnlimited();
}
TEST_F(VoiceChannelTest, SetRtpParametersIsNotImplemented) {
CreateChannels(0, 0);
EXPECT_TRUE(
channel1_->SetLocalContent(&local_media_content1_, CA_OFFER, NULL));
EXPECT_FALSE(
channel1_->SetRtpParameters(kSsrc1, BitrateLimitedParameters(1000)));
TEST_F(VoiceChannelTest, CanChangeMaxBitrate) {
Base::CanChangeMaxBitrate();
}
// VideoChannelTest