diff --git a/webrtc/call.h b/webrtc/call.h index 313c5e58c1..3ba473fec0 100644 --- a/webrtc/call.h +++ b/webrtc/call.h @@ -133,7 +133,12 @@ class Call { // implemented. virtual void SetBitrateConfig( const Config::BitrateConfig& bitrate_config) = 0; - virtual void SignalNetworkState(NetworkState state) = 0; + + // TODO(skvlad): When the unbundled case with multiple streams for the same + // media type going over different networks is supported, track the state + // for each stream separately. Right now it's global per media type. + virtual void SignalChannelNetworkState(MediaType media, + NetworkState state) = 0; virtual void OnSentPacket(const rtc::SentPacket& sent_packet) = 0; diff --git a/webrtc/call/call.cc b/webrtc/call/call.cc index 3fd7a931d0..5f9eb3fd93 100644 --- a/webrtc/call/call.cc +++ b/webrtc/call/call.cc @@ -86,7 +86,8 @@ class Call : public webrtc::Call, public PacketReceiver, void SetBitrateConfig( const webrtc::Call::Config::BitrateConfig& bitrate_config) override; - void SignalNetworkState(NetworkState state) override; + + void SignalChannelNetworkState(MediaType media, NetworkState state) override; void OnSentPacket(const rtc::SentPacket& sent_packet) override; @@ -116,6 +117,7 @@ class Call : public webrtc::Call, public PacketReceiver, void UpdateSendHistograms() EXCLUSIVE_LOCKS_REQUIRED(&bitrate_crit_); void UpdateReceiveHistograms(); + void UpdateAggregateNetworkState(); Clock* const clock_; @@ -127,7 +129,8 @@ class Call : public webrtc::Call, public PacketReceiver, Call::Config config_; rtc::ThreadChecker configuration_thread_checker_; - bool network_enabled_; + NetworkState audio_network_state_; + NetworkState video_network_state_; std::unique_ptr receive_crit_; // Audio and Video receive streams are owned by the client that creates them. @@ -189,7 +192,8 @@ Call::Call(const Call::Config& config) call_stats_(new CallStats(clock_)), bitrate_allocator_(new BitrateAllocator()), config_(config), - network_enabled_(true), + audio_network_state_(kNetworkUp), + video_network_state_(kNetworkUp), receive_crit_(RWLockWrapper::CreateRWLock()), send_crit_(RWLockWrapper::CreateRWLock()), received_video_bytes_(0), @@ -317,14 +321,14 @@ webrtc::AudioSendStream* Call::CreateAudioSendStream( RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); AudioSendStream* send_stream = new AudioSendStream( config, config_.audio_state, congestion_controller_.get()); - if (!network_enabled_) - send_stream->SignalNetworkState(kNetworkDown); { WriteLockScoped write_lock(*send_crit_); RTC_DCHECK(audio_send_ssrcs_.find(config.rtp.ssrc) == audio_send_ssrcs_.end()); audio_send_ssrcs_[config.rtp.ssrc] = send_stream; } + send_stream->SignalNetworkState(audio_network_state_); + UpdateAggregateNetworkState(); return send_stream; } @@ -343,6 +347,7 @@ void Call::DestroyAudioSendStream(webrtc::AudioSendStream* send_stream) { audio_send_stream->config().rtp.ssrc); RTC_DCHECK(num_deleted == 1); } + UpdateAggregateNetworkState(); delete audio_send_stream; } @@ -359,6 +364,8 @@ webrtc::AudioReceiveStream* Call::CreateAudioReceiveStream( audio_receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream; ConfigureSync(config.sync_group); } + receive_stream->SignalNetworkState(audio_network_state_); + UpdateAggregateNetworkState(); return receive_stream; } @@ -382,6 +389,7 @@ void Call::DestroyAudioReceiveStream( ConfigureSync(sync_group); } } + UpdateAggregateNetworkState(); delete audio_receive_stream; } @@ -397,20 +405,18 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream( num_cpu_cores_, module_process_thread_.get(), call_stats_.get(), congestion_controller_.get(), bitrate_allocator_.get(), &remb_, config, encoder_config, suspended_video_send_ssrcs_); - - if (!network_enabled_) - send_stream->SignalNetworkState(kNetworkDown); - - WriteLockScoped write_lock(*send_crit_); - for (uint32_t ssrc : config.rtp.ssrcs) { - RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end()); - video_send_ssrcs_[ssrc] = send_stream; + { + WriteLockScoped write_lock(*send_crit_); + for (uint32_t ssrc : config.rtp.ssrcs) { + RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end()); + video_send_ssrcs_[ssrc] = send_stream; + } + video_send_streams_.insert(send_stream); } - video_send_streams_.insert(send_stream); - + send_stream->SignalNetworkState(video_network_state_); + UpdateAggregateNetworkState(); if (event_log_) event_log_->LogVideoSendStreamConfig(config); - return send_stream; } @@ -445,6 +451,7 @@ void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { suspended_video_send_ssrcs_[it->first] = it->second; } + UpdateAggregateNetworkState(); delete send_stream_impl; } @@ -455,26 +462,24 @@ webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream( VideoReceiveStream* receive_stream = new VideoReceiveStream( num_cpu_cores_, congestion_controller_.get(), config, voice_engine(), module_process_thread_.get(), call_stats_.get(), &remb_); + { + WriteLockScoped write_lock(*receive_crit_); + RTC_DCHECK(video_receive_ssrcs_.find(config.rtp.remote_ssrc) == + video_receive_ssrcs_.end()); + video_receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream; + // TODO(pbos): Configure different RTX payloads per receive payload. + VideoReceiveStream::Config::Rtp::RtxMap::const_iterator it = + config.rtp.rtx.begin(); + if (it != config.rtp.rtx.end()) + video_receive_ssrcs_[it->second.ssrc] = receive_stream; + video_receive_streams_.insert(receive_stream); - WriteLockScoped write_lock(*receive_crit_); - RTC_DCHECK(video_receive_ssrcs_.find(config.rtp.remote_ssrc) == - video_receive_ssrcs_.end()); - video_receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream; - // TODO(pbos): Configure different RTX payloads per receive payload. - VideoReceiveStream::Config::Rtp::RtxMap::const_iterator it = - config.rtp.rtx.begin(); - if (it != config.rtp.rtx.end()) - video_receive_ssrcs_[it->second.ssrc] = receive_stream; - video_receive_streams_.insert(receive_stream); - - ConfigureSync(config.sync_group); - - if (!network_enabled_) - receive_stream->SignalNetworkState(kNetworkDown); - + ConfigureSync(config.sync_group); + } + receive_stream->SignalNetworkState(video_network_state_); + UpdateAggregateNetworkState(); if (event_log_) event_log_->LogVideoReceiveStreamConfig(config); - return receive_stream; } @@ -503,6 +508,7 @@ void Call::DestroyVideoReceiveStream( RTC_CHECK(receive_stream_impl != nullptr); ConfigureSync(receive_stream_impl->config().sync_group); } + UpdateAggregateNetworkState(); delete receive_stream_impl; } @@ -549,27 +555,74 @@ void Call::SetBitrateConfig( bitrate_config.max_bitrate_bps); } -void Call::SignalNetworkState(NetworkState state) { +void Call::SignalChannelNetworkState(MediaType media, NetworkState state) { RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); - network_enabled_ = state == kNetworkUp; - congestion_controller_->SignalNetworkState(state); + switch (media) { + case MediaType::AUDIO: + audio_network_state_ = state; + break; + case MediaType::VIDEO: + video_network_state_ = state; + break; + case MediaType::ANY: + case MediaType::DATA: + RTC_NOTREACHED(); + break; + } + + UpdateAggregateNetworkState(); { - ReadLockScoped write_lock(*send_crit_); + ReadLockScoped read_lock(*send_crit_); for (auto& kv : audio_send_ssrcs_) { - kv.second->SignalNetworkState(state); + kv.second->SignalNetworkState(audio_network_state_); } for (auto& kv : video_send_ssrcs_) { - kv.second->SignalNetworkState(state); + kv.second->SignalNetworkState(video_network_state_); } } { - ReadLockScoped write_lock(*receive_crit_); + ReadLockScoped read_lock(*receive_crit_); + for (auto& kv : audio_receive_ssrcs_) { + kv.second->SignalNetworkState(audio_network_state_); + } for (auto& kv : video_receive_ssrcs_) { - kv.second->SignalNetworkState(state); + kv.second->SignalNetworkState(video_network_state_); } } } +void Call::UpdateAggregateNetworkState() { + RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); + + bool have_audio = false; + bool have_video = false; + { + ReadLockScoped read_lock(*send_crit_); + if (audio_send_ssrcs_.size() > 0) + have_audio = true; + if (video_send_ssrcs_.size() > 0) + have_video = true; + } + { + ReadLockScoped read_lock(*receive_crit_); + if (audio_receive_ssrcs_.size() > 0) + have_audio = true; + if (video_receive_ssrcs_.size() > 0) + have_video = true; + } + + NetworkState aggregate_state = kNetworkDown; + if ((have_video && video_network_state_ == kNetworkUp) || + (have_audio && audio_network_state_ == kNetworkUp)) { + aggregate_state = kNetworkUp; + } + + LOG(LS_INFO) << "UpdateAggregateNetworkState: aggregate_state=" + << (aggregate_state == kNetworkUp ? "up" : "down"); + + congestion_controller_->SignalNetworkState(aggregate_state); +} + void Call::OnSentPacket(const rtc::SentPacket& sent_packet) { if (first_packet_sent_ms_ == -1) first_packet_sent_ms_ = clock_->TimeInMilliseconds(); diff --git a/webrtc/media/engine/fakewebrtccall.cc b/webrtc/media/engine/fakewebrtccall.cc index c345565b1b..2da9e0c3f0 100644 --- a/webrtc/media/engine/fakewebrtccall.cc +++ b/webrtc/media/engine/fakewebrtccall.cc @@ -233,7 +233,8 @@ void FakeVideoReceiveStream::SetStats( FakeCall::FakeCall(const webrtc::Call::Config& config) : config_(config), - network_state_(webrtc::kNetworkUp), + audio_network_state_(webrtc::kNetworkUp), + video_network_state_(webrtc::kNetworkUp), num_created_send_streams_(0), num_created_receive_streams_(0) {} @@ -282,8 +283,22 @@ const FakeAudioReceiveStream* FakeCall::GetAudioReceiveStream(uint32_t ssrc) { return nullptr; } -webrtc::NetworkState FakeCall::GetNetworkState() const { - return network_state_; +webrtc::NetworkState FakeCall::GetNetworkState(webrtc::MediaType media) const { + switch (media) { + case webrtc::MediaType::AUDIO: + return audio_network_state_; + case webrtc::MediaType::VIDEO: + return video_network_state_; + case webrtc::MediaType::DATA: + case webrtc::MediaType::ANY: + ADD_FAILURE() << "GetNetworkState called with unknown parameter."; + return webrtc::kNetworkDown; + } + // Even though all the values for the enum class are listed above,the compiler + // will emit a warning as the method may be called with a value outside of the + // valid enum range, unless this case is also handled. + ADD_FAILURE() << "GetNetworkState called with unknown parameter."; + return webrtc::kNetworkDown; } webrtc::AudioSendStream* FakeCall::CreateAudioSendStream( @@ -299,7 +314,7 @@ void FakeCall::DestroyAudioSendStream(webrtc::AudioSendStream* send_stream) { audio_send_streams_.end(), static_cast(send_stream)); if (it == audio_send_streams_.end()) { - ADD_FAILURE() << "DestroyAudioSendStream called with unknown paramter."; + ADD_FAILURE() << "DestroyAudioSendStream called with unknown parameter."; } else { delete *it; audio_send_streams_.erase(it); @@ -319,7 +334,7 @@ void FakeCall::DestroyAudioReceiveStream( audio_receive_streams_.end(), static_cast(receive_stream)); if (it == audio_receive_streams_.end()) { - ADD_FAILURE() << "DestroyAudioReceiveStream called with unknown paramter."; + ADD_FAILURE() << "DestroyAudioReceiveStream called with unknown parameter."; } else { delete *it; audio_receive_streams_.erase(it); @@ -341,7 +356,7 @@ void FakeCall::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { video_send_streams_.end(), static_cast(send_stream)); if (it == video_send_streams_.end()) { - ADD_FAILURE() << "DestroyVideoSendStream called with unknown paramter."; + ADD_FAILURE() << "DestroyVideoSendStream called with unknown parameter."; } else { delete *it; video_send_streams_.erase(it); @@ -361,7 +376,7 @@ void FakeCall::DestroyVideoReceiveStream( video_receive_streams_.end(), static_cast(receive_stream)); if (it == video_receive_streams_.end()) { - ADD_FAILURE() << "DestroyVideoReceiveStream called with unknown paramter."; + ADD_FAILURE() << "DestroyVideoReceiveStream called with unknown parameter."; } else { delete *it; video_receive_streams_.erase(it); @@ -422,8 +437,20 @@ void FakeCall::SetBitrateConfig( config_.bitrate_config = bitrate_config; } -void FakeCall::SignalNetworkState(webrtc::NetworkState state) { - network_state_ = state; +void FakeCall::SignalChannelNetworkState(webrtc::MediaType media, + webrtc::NetworkState state) { + switch (media) { + case webrtc::MediaType::AUDIO: + audio_network_state_ = state; + break; + case webrtc::MediaType::VIDEO: + video_network_state_ = state; + break; + case webrtc::MediaType::DATA: + case webrtc::MediaType::ANY: + ADD_FAILURE() + << "SignalChannelNetworkState called with unknown parameter."; + } } void FakeCall::OnSentPacket(const rtc::SentPacket& sent_packet) { diff --git a/webrtc/media/engine/fakewebrtccall.h b/webrtc/media/engine/fakewebrtccall.h index cf96cc53f6..b1e20ce4c0 100644 --- a/webrtc/media/engine/fakewebrtccall.h +++ b/webrtc/media/engine/fakewebrtccall.h @@ -200,7 +200,7 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { const FakeAudioReceiveStream* GetAudioReceiveStream(uint32_t ssrc); rtc::SentPacket last_sent_packet() const { return last_sent_packet_; } - webrtc::NetworkState GetNetworkState() const; + webrtc::NetworkState GetNetworkState(webrtc::MediaType media) const; int GetNumCreatedSendStreams() const; int GetNumCreatedReceiveStreams() const; void SetStats(const webrtc::Call::Stats& stats); @@ -235,11 +235,13 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { void SetBitrateConfig( const webrtc::Call::Config::BitrateConfig& bitrate_config) override; - void SignalNetworkState(webrtc::NetworkState state) override; + void SignalChannelNetworkState(webrtc::MediaType media, + webrtc::NetworkState state) override; void OnSentPacket(const rtc::SentPacket& sent_packet) override; webrtc::Call::Config config_; - webrtc::NetworkState network_state_; + webrtc::NetworkState audio_network_state_; + webrtc::NetworkState video_network_state_; rtc::SentPacket last_sent_packet_; webrtc::Call::Stats stats_; std::vector video_send_streams_; diff --git a/webrtc/media/engine/webrtcvideoengine2.cc b/webrtc/media/engine/webrtcvideoengine2.cc index 3cff1e6fbd..9dff1ee197 100644 --- a/webrtc/media/engine/webrtcvideoengine2.cc +++ b/webrtc/media/engine/webrtcvideoengine2.cc @@ -1421,7 +1421,9 @@ void WebRtcVideoChannel2::OnRtcpReceived( void WebRtcVideoChannel2::OnReadyToSend(bool ready) { LOG(LS_VERBOSE) << "OnReadyToSend: " << (ready ? "Ready." : "Not ready."); - call_->SignalNetworkState(ready ? webrtc::kNetworkUp : webrtc::kNetworkDown); + call_->SignalChannelNetworkState( + webrtc::MediaType::VIDEO, + ready ? webrtc::kNetworkUp : webrtc::kNetworkDown); } bool WebRtcVideoChannel2::MuteStream(uint32_t ssrc, bool mute) { diff --git a/webrtc/media/engine/webrtcvideoengine2_unittest.cc b/webrtc/media/engine/webrtcvideoengine2_unittest.cc index f318fe5409..b0145b472b 100644 --- a/webrtc/media/engine/webrtcvideoengine2_unittest.cc +++ b/webrtc/media/engine/webrtcvideoengine2_unittest.cc @@ -2656,13 +2656,22 @@ TEST_F(WebRtcVideoChannel2Test, TestSetRecvRtcpReducedSize) { } TEST_F(WebRtcVideoChannel2Test, OnReadyToSendSignalsNetworkState) { - EXPECT_EQ(webrtc::kNetworkUp, fake_call_->GetNetworkState()); + EXPECT_EQ(webrtc::kNetworkUp, + fake_call_->GetNetworkState(webrtc::MediaType::VIDEO)); + EXPECT_EQ(webrtc::kNetworkUp, + fake_call_->GetNetworkState(webrtc::MediaType::AUDIO)); channel_->OnReadyToSend(false); - EXPECT_EQ(webrtc::kNetworkDown, fake_call_->GetNetworkState()); + EXPECT_EQ(webrtc::kNetworkDown, + fake_call_->GetNetworkState(webrtc::MediaType::VIDEO)); + EXPECT_EQ(webrtc::kNetworkUp, + fake_call_->GetNetworkState(webrtc::MediaType::AUDIO)); channel_->OnReadyToSend(true); - EXPECT_EQ(webrtc::kNetworkUp, fake_call_->GetNetworkState()); + EXPECT_EQ(webrtc::kNetworkUp, + fake_call_->GetNetworkState(webrtc::MediaType::VIDEO)); + EXPECT_EQ(webrtc::kNetworkUp, + fake_call_->GetNetworkState(webrtc::MediaType::AUDIO)); } TEST_F(WebRtcVideoChannel2Test, GetStatsReportsSentCodecName) { diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc index 4f6ae434c7..fbaab675ec 100644 --- a/webrtc/media/engine/webrtcvoiceengine.cc +++ b/webrtc/media/engine/webrtcvoiceengine.cc @@ -2419,6 +2419,14 @@ bool WebRtcVoiceMediaChannel::SetSendBitrateInternal(int bps) { } } +void WebRtcVoiceMediaChannel::OnReadyToSend(bool ready) { + RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); + LOG(LS_VERBOSE) << "OnReadyToSend: " << (ready ? "Ready." : "Not ready."); + call_->SignalChannelNetworkState( + webrtc::MediaType::AUDIO, + ready ? webrtc::kNetworkUp : webrtc::kNetworkDown); +} + bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { TRACE_EVENT0("webrtc", "WebRtcVoiceMediaChannel::GetStats"); RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); diff --git a/webrtc/media/engine/webrtcvoiceengine.h b/webrtc/media/engine/webrtcvoiceengine.h index a16d0428fe..056b0780ef 100644 --- a/webrtc/media/engine/webrtcvoiceengine.h +++ b/webrtc/media/engine/webrtcvoiceengine.h @@ -183,7 +183,7 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel, const rtc::PacketTime& packet_time) override; void OnRtcpReceived(rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time) override; - void OnReadyToSend(bool ready) override {} + void OnReadyToSend(bool ready) override; bool GetStats(VoiceMediaInfo* info) override; void SetRawAudioSink( diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc index b538c6ff75..30be07d213 100644 --- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc +++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc @@ -3193,6 +3193,29 @@ TEST_F(WebRtcVoiceEngineTestFake, SetRawAudioSinkDefaultRecvStream) { EXPECT_NE(nullptr, GetRecvStream(0x01).sink()); } +// Test that, just like the video channel, the voice channel communicates the +// network state to the call. +TEST_F(WebRtcVoiceEngineTestFake, OnReadyToSendSignalsNetworkState) { + EXPECT_TRUE(SetupEngine()); + + EXPECT_EQ(webrtc::kNetworkUp, + call_.GetNetworkState(webrtc::MediaType::AUDIO)); + EXPECT_EQ(webrtc::kNetworkUp, + call_.GetNetworkState(webrtc::MediaType::VIDEO)); + + channel_->OnReadyToSend(false); + EXPECT_EQ(webrtc::kNetworkDown, + call_.GetNetworkState(webrtc::MediaType::AUDIO)); + EXPECT_EQ(webrtc::kNetworkUp, + call_.GetNetworkState(webrtc::MediaType::VIDEO)); + + channel_->OnReadyToSend(true); + EXPECT_EQ(webrtc::kNetworkUp, + call_.GetNetworkState(webrtc::MediaType::AUDIO)); + EXPECT_EQ(webrtc::kNetworkUp, + call_.GetNetworkState(webrtc::MediaType::VIDEO)); +} + // Tests that the library initializes and shuts down properly. TEST(WebRtcVoiceEngineTest, StartupShutdown) { cricket::WebRtcVoiceEngine engine; diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc index b86b2c9d30..e9e330fdc6 100644 --- a/webrtc/video/end_to_end_tests.cc +++ b/webrtc/video/end_to_end_tests.cc @@ -76,6 +76,35 @@ class EndToEndTest : public test::CallTest { } }; + class RequiredTransport : public Transport { + public: + RequiredTransport(bool rtp_required, bool rtcp_required) + : need_rtp_(rtp_required), need_rtcp_(rtcp_required) {} + ~RequiredTransport() { + if (need_rtp_) { + ADD_FAILURE() << "Expected RTP packet not sent."; + } + if (need_rtcp_) { + ADD_FAILURE() << "Expected RTCP packet not sent."; + } + } + + private: + bool SendRtp(const uint8_t* packet, + size_t length, + const PacketOptions& options) override { + need_rtp_ = false; + return true; + } + + bool SendRtcp(const uint8_t* packet, size_t length) override { + need_rtcp_ = false; + return true; + } + bool need_rtp_; + bool need_rtcp_; + }; + void DecodesRetransmittedFrame(bool enable_rtx, bool enable_red); void ReceivesPliAndRecovers(int rtp_history_ms); void RespectsRtcpMode(RtcpMode rtcp_mode); @@ -83,6 +112,13 @@ class EndToEndTest : public test::CallTest { void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first); void TestRtpStatePreservation(bool use_rtx); void VerifyHistogramStats(bool use_rtx, bool use_red, bool screenshare); + void VerifyNewVideoSendStreamsRespectNetworkState( + MediaType network_to_bring_down, + VideoEncoder* encoder, + Transport* transport); + void VerifyNewVideoReceiveStreamsRespectNetworkState( + MediaType network_to_bring_down, + Transport* transport); }; TEST_F(EndToEndTest, ReceiverCanBeStartedTwice) { @@ -3193,8 +3229,16 @@ TEST_F(EndToEndTest, RespectsNetworkState) { // Wait for packets from both sender/receiver. WaitForPacketsOrSilence(false, false); + // Sender-side network down for audio; there should be no effect on video + sender_call_->SignalChannelNetworkState(MediaType::AUDIO, kNetworkDown); + WaitForPacketsOrSilence(false, false); + + // Receiver-side network down for audio; no change expected + receiver_call_->SignalChannelNetworkState(MediaType::AUDIO, kNetworkDown); + WaitForPacketsOrSilence(false, false); + // Sender-side network down. - sender_call_->SignalNetworkState(kNetworkDown); + sender_call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkDown); { rtc::CritScope lock(&test_crit_); // After network goes down we shouldn't be encoding more frames. @@ -3204,7 +3248,13 @@ TEST_F(EndToEndTest, RespectsNetworkState) { WaitForPacketsOrSilence(true, false); // Receiver-side network down. - receiver_call_->SignalNetworkState(kNetworkDown); + receiver_call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkDown); + WaitForPacketsOrSilence(true, true); + + // Network up for audio for both sides; video is still not expected to + // start + sender_call_->SignalChannelNetworkState(MediaType::AUDIO, kNetworkUp); + receiver_call_->SignalChannelNetworkState(MediaType::AUDIO, kNetworkUp); WaitForPacketsOrSilence(true, true); // Network back up again for both. @@ -3214,9 +3264,13 @@ TEST_F(EndToEndTest, RespectsNetworkState) { // network. sender_state_ = kNetworkUp; } - sender_call_->SignalNetworkState(kNetworkUp); - receiver_call_->SignalNetworkState(kNetworkUp); + sender_call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkUp); + receiver_call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkUp); WaitForPacketsOrSilence(false, false); + + // TODO(skvlad): add tests to verify that the audio streams are stopped + // when the network goes down for audio once the workaround in + // paced_sender.cc is removed. } int32_t Encode(const VideoFrame& input_image, @@ -3267,7 +3321,7 @@ TEST_F(EndToEndTest, RespectsNetworkState) { sender_done = true; } } else { - if (sender_rtp_ > initial_sender_rtp) + if (sender_rtp_ > initial_sender_rtp + kNumAcceptedDowntimeRtp) sender_done = true; } if (receiver_down) { @@ -3279,7 +3333,7 @@ TEST_F(EndToEndTest, RespectsNetworkState) { receiver_done = true; } } else { - if (receiver_rtcp_ > initial_receiver_rtcp) + if (receiver_rtcp_ > initial_receiver_rtcp + kNumAcceptedDowntimeRtcp) receiver_done = true; } } @@ -3338,26 +3392,15 @@ TEST_F(EndToEndTest, CallReportsRttForSender) { DestroyStreams(); } -TEST_F(EndToEndTest, NewSendStreamsRespectNetworkDown) { - class UnusedEncoder : public test::FakeEncoder { - public: - UnusedEncoder() : FakeEncoder(Clock::GetRealTimeClock()) {} - int32_t Encode(const VideoFrame& input_image, - const CodecSpecificInfo* codec_specific_info, - const std::vector* frame_types) override { - ADD_FAILURE() << "Unexpected frame encode."; - return test::FakeEncoder::Encode( - input_image, codec_specific_info, frame_types); - } - }; - +void EndToEndTest::VerifyNewVideoSendStreamsRespectNetworkState( + MediaType network_to_bring_down, + VideoEncoder* encoder, + Transport* transport) { CreateSenderCall(Call::Config()); - sender_call_->SignalNetworkState(kNetworkDown); + sender_call_->SignalChannelNetworkState(network_to_bring_down, kNetworkDown); - UnusedTransport transport; - CreateSendConfig(1, 0, &transport); - UnusedEncoder unused_encoder; - video_send_config_.encoder_settings.encoder = &unused_encoder; + CreateSendConfig(1, 0, transport); + video_send_config_.encoder_settings.encoder = encoder; CreateVideoStreams(); CreateFrameGeneratorCapturer(); @@ -3368,15 +3411,17 @@ TEST_F(EndToEndTest, NewSendStreamsRespectNetworkDown) { DestroyStreams(); } -TEST_F(EndToEndTest, NewReceiveStreamsRespectNetworkDown) { +void EndToEndTest::VerifyNewVideoReceiveStreamsRespectNetworkState( + MediaType network_to_bring_down, + Transport* transport) { CreateCalls(Call::Config(), Call::Config()); - receiver_call_->SignalNetworkState(kNetworkDown); + receiver_call_->SignalChannelNetworkState(network_to_bring_down, + kNetworkDown); test::DirectTransport sender_transport(sender_call_.get()); sender_transport.SetReceiver(receiver_call_->Receiver()); CreateSendConfig(1, 0, &sender_transport); - UnusedTransport transport; - CreateMatchingReceiveConfigs(&transport); + CreateMatchingReceiveConfigs(transport); CreateVideoStreams(); CreateFrameGeneratorCapturer(); @@ -3389,6 +3434,63 @@ TEST_F(EndToEndTest, NewReceiveStreamsRespectNetworkDown) { DestroyStreams(); } +TEST_F(EndToEndTest, NewVideoSendStreamsRespectVideoNetworkDown) { + class UnusedEncoder : public test::FakeEncoder { + public: + UnusedEncoder() : FakeEncoder(Clock::GetRealTimeClock()) {} + int32_t Encode(const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) override { + ADD_FAILURE() << "Unexpected frame encode."; + return test::FakeEncoder::Encode(input_image, codec_specific_info, + frame_types); + } + }; + + UnusedEncoder unused_encoder; + UnusedTransport unused_transport; + VerifyNewVideoSendStreamsRespectNetworkState( + MediaType::VIDEO, &unused_encoder, &unused_transport); +} + +TEST_F(EndToEndTest, NewVideoSendStreamsIgnoreAudioNetworkDown) { + class RequiredEncoder : public test::FakeEncoder { + public: + RequiredEncoder() + : FakeEncoder(Clock::GetRealTimeClock()), encoded_frame_(false) {} + ~RequiredEncoder() { + if (!encoded_frame_) { + ADD_FAILURE() << "Didn't encode an expected frame"; + } + } + int32_t Encode(const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) override { + encoded_frame_ = true; + return test::FakeEncoder::Encode(input_image, codec_specific_info, + frame_types); + } + + private: + bool encoded_frame_; + }; + + RequiredTransport required_transport(true /*rtp*/, false /*rtcp*/); + RequiredEncoder required_encoder; + VerifyNewVideoSendStreamsRespectNetworkState( + MediaType::AUDIO, &required_encoder, &required_transport); +} + +TEST_F(EndToEndTest, NewVideoReceiveStreamsRespectVideoNetworkDown) { + UnusedTransport transport; + VerifyNewVideoReceiveStreamsRespectNetworkState(MediaType::VIDEO, &transport); +} + +TEST_F(EndToEndTest, NewVideoReceiveStreamsIgnoreAudioNetworkDown) { + RequiredTransport transport(false /*rtp*/, true /*rtcp*/); + VerifyNewVideoReceiveStreamsRespectNetworkState(MediaType::AUDIO, &transport); +} + void VerifyEmptyNackConfig(const NackConfig& config) { EXPECT_EQ(0, config.rtp_history_ms) << "Enabling NACK requires rtcp-fb: nack negotiation.";