Make the audio channel communicate network state changes to the call.
This change enables voice-only calls to keep track of the network state.
This is only a partial fix - the last modality to change state controls
the state for the entire call, so a call with a failed video transport
will also stop sending audio packets. Handling this condition correctly
would require the call to keep track of network state for each media
type separately, and take care of conditions such as a failed video
channel getting removed, while a functioning audio channel remains.
BUG=webrtc:5307
Review URL: https://codereview.webrtc.org/1757683002
Cr-Commit-Position: refs/heads/master@{#12093}
diff --git a/webrtc/call.h b/webrtc/call.h
index 313c5e5..3ba473f 100644
--- a/webrtc/call.h
+++ b/webrtc/call.h
@@ -133,7 +133,12 @@
// 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 3fd7a93..5f9eb3f 100644
--- a/webrtc/call/call.cc
+++ b/webrtc/call/call.cc
@@ -86,7 +86,8 @@
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 @@
void UpdateSendHistograms() EXCLUSIVE_LOCKS_REQUIRED(&bitrate_crit_);
void UpdateReceiveHistograms();
+ void UpdateAggregateNetworkState();
Clock* const clock_;
@@ -127,7 +129,8 @@
Call::Config config_;
rtc::ThreadChecker configuration_thread_checker_;
- bool network_enabled_;
+ NetworkState audio_network_state_;
+ NetworkState video_network_state_;
std::unique_ptr<RWLockWrapper> receive_crit_;
// Audio and Video receive streams are owned by the client that creates them.
@@ -189,7 +192,8 @@
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 @@
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 @@
audio_send_stream->config().rtp.ssrc);
RTC_DCHECK(num_deleted == 1);
}
+ UpdateAggregateNetworkState();
delete audio_send_stream;
}
@@ -359,6 +364,8 @@
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 @@
ConfigureSync(sync_group);
}
}
+ UpdateAggregateNetworkState();
delete audio_receive_stream;
}
@@ -397,20 +405,18 @@
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 @@
suspended_video_send_ssrcs_[it->first] = it->second;
}
+ UpdateAggregateNetworkState();
delete send_stream_impl;
}
@@ -455,26 +462,24 @@
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 @@
RTC_CHECK(receive_stream_impl != nullptr);
ConfigureSync(receive_stream_impl->config().sync_group);
}
+ UpdateAggregateNetworkState();
delete receive_stream_impl;
}
@@ -549,27 +555,74 @@
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 c345565..2da9e0c 100644
--- a/webrtc/media/engine/fakewebrtccall.cc
+++ b/webrtc/media/engine/fakewebrtccall.cc
@@ -233,7 +233,8 @@
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 @@
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 @@
audio_send_streams_.end(),
static_cast<FakeAudioSendStream*>(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 @@
audio_receive_streams_.end(),
static_cast<FakeAudioReceiveStream*>(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 @@
video_send_streams_.end(),
static_cast<FakeVideoSendStream*>(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 @@
video_receive_streams_.end(),
static_cast<FakeVideoReceiveStream*>(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 @@
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 cf96cc5..b1e20ce 100644
--- a/webrtc/media/engine/fakewebrtccall.h
+++ b/webrtc/media/engine/fakewebrtccall.h
@@ -200,7 +200,7 @@
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 @@
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<FakeVideoSendStream*> video_send_streams_;
diff --git a/webrtc/media/engine/webrtcvideoengine2.cc b/webrtc/media/engine/webrtcvideoengine2.cc
index 3cff1e6..9dff1ee 100644
--- a/webrtc/media/engine/webrtcvideoengine2.cc
+++ b/webrtc/media/engine/webrtcvideoengine2.cc
@@ -1421,7 +1421,9 @@
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 f318fe5..b0145b4 100644
--- a/webrtc/media/engine/webrtcvideoengine2_unittest.cc
+++ b/webrtc/media/engine/webrtcvideoengine2_unittest.cc
@@ -2656,13 +2656,22 @@
}
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 4f6ae43..fbaab67 100644
--- a/webrtc/media/engine/webrtcvoiceengine.cc
+++ b/webrtc/media/engine/webrtcvoiceengine.cc
@@ -2419,6 +2419,14 @@
}
}
+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 a16d042..056b078 100644
--- a/webrtc/media/engine/webrtcvoiceengine.h
+++ b/webrtc/media/engine/webrtcvoiceengine.h
@@ -183,7 +183,7 @@
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 b538c6f..30be07d 100644
--- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc
+++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
@@ -3193,6 +3193,29 @@
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 b86b2c9..e9e330f 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -76,6 +76,35 @@
}
};
+ 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
receiver_done = true;
}
} else {
- if (receiver_rtcp_ > initial_receiver_rtcp)
+ if (receiver_rtcp_ > initial_receiver_rtcp + kNumAcceptedDowntimeRtcp)
receiver_done = true;
}
}
@@ -3338,26 +3392,15 @@
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<FrameType>* 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 @@
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 @@
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<FrameType>* 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<FrameType>* 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.";