diff options
Diffstat (limited to 'third_party/libwebrtc/webrtc/call/call_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/webrtc/call/call_unittest.cc | 715 |
1 files changed, 715 insertions, 0 deletions
diff --git a/third_party/libwebrtc/webrtc/call/call_unittest.cc b/third_party/libwebrtc/webrtc/call/call_unittest.cc new file mode 100644 index 0000000000..7b16271584 --- /dev/null +++ b/third_party/libwebrtc/webrtc/call/call_unittest.cc @@ -0,0 +1,715 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <list> +#include <map> +#include <memory> +#include <utility> + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/test/mock_audio_mixer.h" +#include "call/audio_state.h" +#include "call/call.h" +#include "call/fake_rtp_transport_controller_send.h" +#include "logging/rtc_event_log/rtc_event_log.h" +#include "modules/audio_device/include/mock_audio_device.h" +#include "modules/audio_mixer/audio_mixer_impl.h" +#include "modules/congestion_controller/include/mock/mock_send_side_congestion_controller.h" +#include "modules/pacing/mock/mock_paced_sender.h" +#include "modules/rtp_rtcp/include/rtp_rtcp.h" +#include "rtc_base/ptr_util.h" +#include "test/fake_encoder.h" +#include "test/gtest.h" +#include "test/mock_audio_decoder_factory.h" +#include "test/mock_transport.h" +#include "test/mock_voice_engine.h" + +namespace { + +struct CallHelper { + explicit CallHelper( + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory = nullptr) + : voice_engine_(decoder_factory) { + webrtc::AudioState::Config audio_state_config; + audio_state_config.voice_engine = &voice_engine_; + audio_state_config.audio_mixer = webrtc::AudioMixerImpl::Create(); + audio_state_config.audio_processing = webrtc::AudioProcessing::Create(); + EXPECT_CALL(voice_engine_, audio_transport()); + webrtc::Call::Config config(&event_log_); + config.audio_state = webrtc::AudioState::Create(audio_state_config); + call_.reset(webrtc::Call::Create(config)); + } + + webrtc::Call* operator->() { return call_.get(); } + webrtc::test::MockVoiceEngine* voice_engine() { return &voice_engine_; } + + private: + testing::NiceMock<webrtc::test::MockVoiceEngine> voice_engine_; + webrtc::RtcEventLogNullImpl event_log_; + std::unique_ptr<webrtc::Call> call_; +}; +} // namespace + +namespace webrtc { + +TEST(CallTest, ConstructDestruct) { + CallHelper call; +} + +TEST(CallTest, CreateDestroy_AudioSendStream) { + CallHelper call; + AudioSendStream::Config config(nullptr); + config.rtp.ssrc = 42; + config.voe_channel_id = 123; + AudioSendStream* stream = call->CreateAudioSendStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyAudioSendStream(stream); +} + +TEST(CallTest, CreateDestroy_AudioReceiveStream) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + AudioReceiveStream::Config config; + config.rtp.remote_ssrc = 42; + config.voe_channel_id = 123; + config.decoder_factory = decoder_factory; + AudioReceiveStream* stream = call->CreateAudioReceiveStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyAudioReceiveStream(stream); +} + +TEST(CallTest, CreateDestroy_AudioSendStreams) { + CallHelper call; + AudioSendStream::Config config(nullptr); + config.voe_channel_id = 123; + std::list<AudioSendStream*> streams; + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.rtp.ssrc = ssrc; + AudioSendStream* stream = call->CreateAudioSendStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyAudioSendStream(s); + } + streams.clear(); + } +} + +TEST(CallTest, CreateDestroy_AudioReceiveStreams) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + AudioReceiveStream::Config config; + config.voe_channel_id = 123; + config.decoder_factory = decoder_factory; + std::list<AudioReceiveStream*> streams; + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.rtp.remote_ssrc = ssrc; + AudioReceiveStream* stream = call->CreateAudioReceiveStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyAudioReceiveStream(s); + } + streams.clear(); + } +} + +TEST(CallTest, CreateDestroy_AssociateAudioSendReceiveStreams_RecvFirst) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + ::testing::NiceMock<MockRtpRtcp> mock_rtp_rtcp; + + constexpr int kRecvChannelId = 101; + + // Set up the mock to create a channel proxy which we know of, so that we can + // add our expectations to it. + test::MockVoEChannelProxy* recv_channel_proxy = nullptr; + EXPECT_CALL(*call.voice_engine(), ChannelProxyFactory(testing::_)) + .WillRepeatedly(testing::Invoke([&](int channel_id) { + test::MockVoEChannelProxy* channel_proxy = + new testing::NiceMock<test::MockVoEChannelProxy>(); + EXPECT_CALL(*channel_proxy, GetAudioDecoderFactory()) + .WillRepeatedly(testing::ReturnRef(decoder_factory)); + EXPECT_CALL(*channel_proxy, SetReceiveCodecs(testing::_)) + .WillRepeatedly(testing::Invoke( + [](const std::map<int, SdpAudioFormat>& codecs) { + EXPECT_THAT(codecs, testing::IsEmpty()); + })); + EXPECT_CALL(*channel_proxy, GetRtpRtcp(testing::_, testing::_)) + .WillRepeatedly(testing::SetArgPointee<0>(&mock_rtp_rtcp)); + // If being called for the send channel, save a pointer to the channel + // proxy for later. + if (channel_id == kRecvChannelId) { + EXPECT_FALSE(recv_channel_proxy); + recv_channel_proxy = channel_proxy; + } + return channel_proxy; + })); + + AudioReceiveStream::Config recv_config; + recv_config.rtp.remote_ssrc = 42; + recv_config.rtp.local_ssrc = 777; + recv_config.voe_channel_id = kRecvChannelId; + recv_config.decoder_factory = decoder_factory; + AudioReceiveStream* recv_stream = call->CreateAudioReceiveStream(recv_config); + EXPECT_NE(recv_stream, nullptr); + + EXPECT_CALL(*recv_channel_proxy, AssociateSendChannel(testing::_)).Times(1); + AudioSendStream::Config send_config(nullptr); + send_config.rtp.ssrc = 777; + send_config.voe_channel_id = 123; + AudioSendStream* send_stream = call->CreateAudioSendStream(send_config); + EXPECT_NE(send_stream, nullptr); + + EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1); + call->DestroyAudioSendStream(send_stream); + + EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1); + call->DestroyAudioReceiveStream(recv_stream); +} + +TEST(CallTest, CreateDestroy_AssociateAudioSendReceiveStreams_SendFirst) { + rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory( + new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>); + CallHelper call(decoder_factory); + ::testing::NiceMock<MockRtpRtcp> mock_rtp_rtcp; + + constexpr int kRecvChannelId = 101; + + // Set up the mock to create a channel proxy which we know of, so that we can + // add our expectations to it. + test::MockVoEChannelProxy* recv_channel_proxy = nullptr; + EXPECT_CALL(*call.voice_engine(), ChannelProxyFactory(testing::_)) + .WillRepeatedly(testing::Invoke([&](int channel_id) { + test::MockVoEChannelProxy* channel_proxy = + new testing::NiceMock<test::MockVoEChannelProxy>(); + EXPECT_CALL(*channel_proxy, GetAudioDecoderFactory()) + .WillRepeatedly(testing::ReturnRef(decoder_factory)); + EXPECT_CALL(*channel_proxy, SetReceiveCodecs(testing::_)) + .WillRepeatedly(testing::Invoke( + [](const std::map<int, SdpAudioFormat>& codecs) { + EXPECT_THAT(codecs, testing::IsEmpty()); + })); + EXPECT_CALL(*channel_proxy, GetRtpRtcp(testing::_, testing::_)) + .WillRepeatedly(testing::SetArgPointee<0>(&mock_rtp_rtcp)); + // If being called for the send channel, save a pointer to the channel + // proxy for later. + if (channel_id == kRecvChannelId) { + EXPECT_FALSE(recv_channel_proxy); + recv_channel_proxy = channel_proxy; + // We need to set this expectation here since the channel proxy is + // created as a side effect of CreateAudioReceiveStream(). + EXPECT_CALL(*recv_channel_proxy, + AssociateSendChannel(testing::_)).Times(1); + } + return channel_proxy; + })); + + AudioSendStream::Config send_config(nullptr); + send_config.rtp.ssrc = 777; + send_config.voe_channel_id = 123; + AudioSendStream* send_stream = call->CreateAudioSendStream(send_config); + EXPECT_NE(send_stream, nullptr); + + AudioReceiveStream::Config recv_config; + recv_config.rtp.remote_ssrc = 42; + recv_config.rtp.local_ssrc = 777; + recv_config.voe_channel_id = kRecvChannelId; + recv_config.decoder_factory = decoder_factory; + AudioReceiveStream* recv_stream = call->CreateAudioReceiveStream(recv_config); + EXPECT_NE(recv_stream, nullptr); + + EXPECT_CALL(*recv_channel_proxy, DisassociateSendChannel()).Times(1); + call->DestroyAudioReceiveStream(recv_stream); + + call->DestroyAudioSendStream(send_stream); +} + +TEST(CallTest, CreateDestroy_FlexfecReceiveStream) { + CallHelper call; + MockTransport rtcp_send_transport; + FlexfecReceiveStream::Config config(&rtcp_send_transport); + config.payload_type = 118; + config.remote_ssrc = 38837212; + config.protected_media_ssrcs = {27273}; + + FlexfecReceiveStream* stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyFlexfecReceiveStream(stream); +} + +TEST(CallTest, CreateDestroy_FlexfecReceiveStreams) { + CallHelper call; + MockTransport rtcp_send_transport; + FlexfecReceiveStream::Config config(&rtcp_send_transport); + config.payload_type = 118; + std::list<FlexfecReceiveStream*> streams; + + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.remote_ssrc = ssrc; + config.protected_media_ssrcs = {ssrc + 1}; + FlexfecReceiveStream* stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyFlexfecReceiveStream(s); + } + streams.clear(); + } +} + +TEST(CallTest, MultipleFlexfecReceiveStreamsProtectingSingleVideoStream) { + CallHelper call; + MockTransport rtcp_send_transport; + FlexfecReceiveStream::Config config(&rtcp_send_transport); + config.payload_type = 118; + config.protected_media_ssrcs = {1324234}; + FlexfecReceiveStream* stream; + std::list<FlexfecReceiveStream*> streams; + + config.remote_ssrc = 838383; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + config.remote_ssrc = 424993; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + config.remote_ssrc = 99383; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + config.remote_ssrc = 5548; + stream = call->CreateFlexfecReceiveStream(config); + EXPECT_NE(stream, nullptr); + streams.push_back(stream); + + for (auto s : streams) { + call->DestroyFlexfecReceiveStream(s); + } +} + +namespace { +struct CallBitrateHelper { + CallBitrateHelper() : CallBitrateHelper(Call::Config::BitrateConfig()) {} + + explicit CallBitrateHelper(const Call::Config::BitrateConfig& bitrate_config) + : mock_cc_(Clock::GetRealTimeClock(), &event_log_, &pacer_) { + Call::Config config(&event_log_); + config.bitrate_config = bitrate_config; + call_.reset( + Call::Create(config, rtc::MakeUnique<FakeRtpTransportControllerSend>( + &packet_router_, &pacer_, &mock_cc_))); + } + + webrtc::Call* operator->() { return call_.get(); } + testing::NiceMock<test::MockSendSideCongestionController>& mock_cc() { + return mock_cc_; + } + + private: + webrtc::RtcEventLogNullImpl event_log_; + PacketRouter packet_router_; + testing::NiceMock<MockPacedSender> pacer_; + testing::NiceMock<test::MockSendSideCongestionController> mock_cc_; + std::unique_ptr<Call> call_; +}; +} // namespace + +TEST(CallBitrateTest, SetBitrateConfigWithValidConfigCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 1; + bitrate_config.start_bitrate_bps = 2; + bitrate_config.max_bitrate_bps = 3; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1, 2, 3)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithDifferentMinCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 10; + bitrate_config.start_bitrate_bps = 20; + bitrate_config.max_bitrate_bps = 30; + call->SetBitrateConfig(bitrate_config); + + bitrate_config.min_bitrate_bps = 11; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(11, -1, 30)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithDifferentStartCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 10; + bitrate_config.start_bitrate_bps = 20; + bitrate_config.max_bitrate_bps = 30; + call->SetBitrateConfig(bitrate_config); + + bitrate_config.start_bitrate_bps = 21; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(10, 21, 30)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithDifferentMaxCallsSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 10; + bitrate_config.start_bitrate_bps = 20; + bitrate_config.max_bitrate_bps = 30; + call->SetBitrateConfig(bitrate_config); + + bitrate_config.max_bitrate_bps = 31; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(10, -1, 31)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SetBitrateConfigWithSameConfigElidesSecondCall) { + CallBitrateHelper call; + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 1; + bitrate_config.start_bitrate_bps = 2; + bitrate_config.max_bitrate_bps = 3; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1, 2, 3)).Times(1); + call->SetBitrateConfig(bitrate_config); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, + SetBitrateConfigWithSameMinMaxAndNegativeStartElidesSecondCall) { + CallBitrateHelper call; + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 1; + bitrate_config.start_bitrate_bps = 2; + bitrate_config.max_bitrate_bps = 3; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1, 2, 3)).Times(1); + call->SetBitrateConfig(bitrate_config); + + bitrate_config.start_bitrate_bps = -1; + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallTest, RecreatingAudioStreamWithSameSsrcReusesRtpState) { + constexpr uint32_t kSSRC = 12345; + testing::NiceMock<test::MockAudioDeviceModule> mock_adm; + rtc::scoped_refptr<test::MockAudioMixer> mock_mixer( + new rtc::RefCountedObject<test::MockAudioMixer>); + + // There's similar functionality in cricket::VoEWrapper but it's not reachable + // from here. Since we're working on removing VoE interfaces, I doubt it's + // worth making VoEWrapper more easily available. + struct ScopedVoiceEngine { + ScopedVoiceEngine() + : voe(VoiceEngine::Create()), + base(VoEBase::GetInterface(voe)) {} + ~ScopedVoiceEngine() { + base->Release(); + EXPECT_TRUE(VoiceEngine::Delete(voe)); + } + + VoiceEngine* voe; + VoEBase* base; + }; + ScopedVoiceEngine voice_engine; + + AudioState::Config audio_state_config; + audio_state_config.voice_engine = voice_engine.voe; + audio_state_config.audio_mixer = mock_mixer; + audio_state_config.audio_processing = AudioProcessing::Create(); + voice_engine.base->Init(&mock_adm, audio_state_config.audio_processing.get(), + CreateBuiltinAudioDecoderFactory()); + auto audio_state = AudioState::Create(audio_state_config); + + RtcEventLogNullImpl event_log; + Call::Config call_config(&event_log); + call_config.audio_state = audio_state; + std::unique_ptr<Call> call(Call::Create(call_config)); + + auto create_stream_and_get_rtp_state = [&](uint32_t ssrc) { + AudioSendStream::Config config(nullptr); + config.rtp.ssrc = ssrc; + config.voe_channel_id = voice_engine.base->CreateChannel(); + AudioSendStream* stream = call->CreateAudioSendStream(config); + VoiceEngineImpl* voe_impl = static_cast<VoiceEngineImpl*>(voice_engine.voe); + auto channel_proxy = voe_impl->GetChannelProxy(config.voe_channel_id); + RtpRtcp* rtp_rtcp = nullptr; + RtpReceiver* rtp_receiver = nullptr; // Unused but required for call. + channel_proxy->GetRtpRtcp(&rtp_rtcp, &rtp_receiver); + const RtpState rtp_state = rtp_rtcp->GetRtpState(); + call->DestroyAudioSendStream(stream); + voice_engine.base->DeleteChannel(config.voe_channel_id); + return rtp_state; + }; + + const RtpState rtp_state1 = create_stream_and_get_rtp_state(kSSRC); + const RtpState rtp_state2 = create_stream_and_get_rtp_state(kSSRC); + + EXPECT_EQ(rtp_state1.sequence_number, rtp_state2.sequence_number); + EXPECT_EQ(rtp_state1.start_timestamp, rtp_state2.start_timestamp); + EXPECT_EQ(rtp_state1.timestamp, rtp_state2.timestamp); + EXPECT_EQ(rtp_state1.capture_time_ms, rtp_state2.capture_time_ms); + EXPECT_EQ(rtp_state1.last_timestamp_time_ms, + rtp_state2.last_timestamp_time_ms); + EXPECT_EQ(rtp_state1.media_has_been_sent, rtp_state2.media_has_been_sent); +} +TEST(CallBitrateTest, BiggerMaskMinUsed) { + CallBitrateHelper call; + Call::Config::BitrateConfigMask mask; + mask.min_bitrate_bps = rtc::Optional<int>(1234); + + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(*mask.min_bitrate_bps, testing::_, testing::_)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, BiggerConfigMinUsed) { + CallBitrateHelper call; + Call::Config::BitrateConfigMask mask; + mask.min_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1000, testing::_, testing::_)); + call->SetBitrateConfigMask(mask); + + Call::Config::BitrateConfig config; + config.min_bitrate_bps = 1234; + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1234, testing::_, testing::_)); + call->SetBitrateConfig(config); +} + +// The last call to set start should be used. +TEST(CallBitrateTest, LatestStartMaskPreferred) { + CallBitrateHelper call; + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1300); + + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(testing::_, *mask.start_bitrate_bps, testing::_)); + call->SetBitrateConfigMask(mask); + + Call::Config::BitrateConfig bitrate_config; + bitrate_config.start_bitrate_bps = 1200; + + EXPECT_CALL( + call.mock_cc(), + SetBweBitrates(testing::_, bitrate_config.start_bitrate_bps, testing::_)); + call->SetBitrateConfig(bitrate_config); +} + +TEST(CallBitrateTest, SmallerMaskMaxUsed) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = + rtc::Optional<int>(bitrate_config.start_bitrate_bps + 1000); + + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(testing::_, testing::_, *mask.max_bitrate_bps)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, SmallerConfigMaxUsed) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 1000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = + rtc::Optional<int>(bitrate_config.start_bitrate_bps + 2000); + + // Expect no calls because nothing changes + EXPECT_CALL(call.mock_cc(), + SetBweBitrates(testing::_, testing::_, testing::_)) + .Times(0); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, MaskStartLessThanConfigMinClamped) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(2000, 2000, testing::_)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, MaskStartGreaterThanConfigMaxClamped) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.start_bitrate_bps = 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = rtc::Optional<int>(1000); + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, -1, 1000)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, MaskMinGreaterThanConfigMaxClamped) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = 2000; + CallBitrateHelper call(bitrate_config); + + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = rtc::Optional<int>(1000); + + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1000, testing::_, 1000)); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, SettingMaskStartForcesUpdate) { + CallBitrateHelper call; + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + + // SetBweBitrates should be called twice with the same params since + // start_bitrate_bps is set. + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, 1000, testing::_)) + .Times(2); + call->SetBitrateConfigMask(mask); + call->SetBitrateConfigMask(mask); +} + +TEST(CallBitrateTest, SetBitrateConfigWithNoChangesDoesNotCallSetBweBitrates) { + CallBitrateHelper call; + + Call::Config::BitrateConfig config1; + config1.min_bitrate_bps = 0; + config1.start_bitrate_bps = 1000; + config1.max_bitrate_bps = -1; + + Call::Config::BitrateConfig config2; + config2.min_bitrate_bps = 0; + config2.start_bitrate_bps = -1; + config2.max_bitrate_bps = -1; + + // The second call should not call SetBweBitrates because it doesn't + // change any values. + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, 1000, -1)); + call->SetBitrateConfig(config1); + call->SetBitrateConfig(config2); +} + +// If SetBitrateConfig changes the max, but not the effective max, +// SetBweBitrates shouldn't be called, to avoid unnecessary encoder +// reconfigurations. +TEST(CallBitrateTest, SetBweBitratesNotCalledWhenEffectiveMaxUnchanged) { + CallBitrateHelper call; + + Call::Config::BitrateConfig config; + config.min_bitrate_bps = 0; + config.start_bitrate_bps = -1; + config.max_bitrate_bps = 2000; + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, testing::_, 2000)); + call->SetBitrateConfig(config); + + // Reduce effective max to 1000 with the mask. + Call::Config::BitrateConfigMask mask; + mask.max_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(testing::_, testing::_, 1000)); + call->SetBitrateConfigMask(mask); + + // This leaves the effective max unchanged, so SetBweBitrates shouldn't be + // called again. + config.max_bitrate_bps = 1000; + call->SetBitrateConfig(config); +} + +// When the "start bitrate" mask is removed, SetBweBitrates shouldn't be called +// again, since nothing's changing. +TEST(CallBitrateTest, SetBweBitratesNotCalledWhenStartMaskRemoved) { + CallBitrateHelper call; + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, 1000, -1)); + call->SetBitrateConfigMask(mask); + + mask.start_bitrate_bps.reset(); + call->SetBitrateConfigMask(mask); +} + +// Test that if SetBitrateConfig is called after SetBitrateConfigMask applies a +// "start" value, the SetBitrateConfig call won't apply that start value a +// second time. +TEST(CallBitrateTest, SetBitrateConfigAfterSetBitrateConfigMaskWithStart) { + CallBitrateHelper call; + + Call::Config::BitrateConfigMask mask; + mask.start_bitrate_bps = rtc::Optional<int>(1000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, 1000, -1)); + call->SetBitrateConfigMask(mask); + + Call::Config::BitrateConfig config; + config.min_bitrate_bps = 0; + config.start_bitrate_bps = -1; + config.max_bitrate_bps = 5000; + // The start value isn't changing, so SetBweBitrates should be called with + // -1. + EXPECT_CALL(call.mock_cc(), SetBweBitrates(0, -1, 5000)); + call->SetBitrateConfig(config); +} + +TEST(CallBitrateTest, SetBweBitratesNotCalledWhenClampedMinUnchanged) { + Call::Config::BitrateConfig bitrate_config; + bitrate_config.start_bitrate_bps = 500; + bitrate_config.max_bitrate_bps = 1000; + CallBitrateHelper call(bitrate_config); + + // Set min to 2000; it is clamped to the max (1000). + Call::Config::BitrateConfigMask mask; + mask.min_bitrate_bps = rtc::Optional<int>(2000); + EXPECT_CALL(call.mock_cc(), SetBweBitrates(1000, -1, 1000)); + call->SetBitrateConfigMask(mask); + + // Set min to 3000; the clamped value stays the same so nothing happens. + mask.min_bitrate_bps = rtc::Optional<int>(3000); + call->SetBitrateConfigMask(mask); +} + +} // namespace webrtc |