diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc index 97d274da2..44c1bf8ad 100644 --- a/talk/media/webrtc/webrtcvideoengine2.cc +++ b/talk/media/webrtc/webrtcvideoengine2.cc @@ -1153,7 +1153,9 @@ void WebRtcVideoChannel2::OnRtcpReceived( } void WebRtcVideoChannel2::OnReadyToSend(bool ready) { - LOG(LS_VERBOSE) << "OnReadySend: " << (ready ? "Ready." : "Not ready."); + LOG(LS_VERBOSE) << "OnReadyToSend: " << (ready ? "Ready." : "Not ready."); + call_->SignalNetworkState(ready ? webrtc::Call::kNetworkUp + : webrtc::Call::kNetworkDown); } bool WebRtcVideoChannel2::MuteStream(uint32 ssrc, bool mute) { diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc index 4f69f6d3c..2178a6889 100644 --- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc +++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc @@ -157,7 +157,9 @@ void FakeVideoReceiveStream::Stop() { void FakeVideoReceiveStream::GetCurrentReceiveCodec(webrtc::VideoCodec* codec) { } -FakeCall::FakeCall() { SetVideoCodecs(GetDefaultVideoCodecs()); } +FakeCall::FakeCall() : network_state_(kNetworkUp) { + SetVideoCodecs(GetDefaultVideoCodecs()); +} FakeCall::~FakeCall() { EXPECT_EQ(0u, video_send_streams_.size()); @@ -218,6 +220,10 @@ std::vector<webrtc::VideoCodec> FakeCall::GetDefaultVideoCodecs() { return codecs; } +webrtc::Call::NetworkState FakeCall::GetNetworkState() const { + return network_state_; +} + webrtc::VideoSendStream* FakeCall::CreateVideoSendStream( const webrtc::VideoSendStream::Config& config, const std::vector<webrtc::VideoStream>& video_streams, @@ -274,6 +280,10 @@ uint32_t FakeCall::ReceiveBitrateEstimate() { return 0; } +void FakeCall::SignalNetworkState(webrtc::Call::NetworkState state) { + network_state_ = state; +} + FakeWebRtcVideoChannel2::FakeWebRtcVideoChannel2( FakeCall* call, WebRtcVideoEngine2* engine, @@ -289,6 +299,7 @@ FakeWebRtcVideoChannel2::~FakeWebRtcVideoChannel2() { VoiceMediaChannel* FakeWebRtcVideoChannel2::GetVoiceChannel() { return voice_channel_; } + FakeCall* FakeWebRtcVideoChannel2::GetFakeCall() { return fake_call_; } @@ -1614,8 +1625,17 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_UpdateEncoderCodecsAfterSetFactory) { FAIL() << "Not implemented."; // TODO(pbos): Implement. } -TEST_F(WebRtcVideoChannel2Test, DISABLED_OnReadyToSend) { - FAIL() << "Not implemented."; // TODO(pbos): Implement. +TEST_F(WebRtcVideoChannel2Test, OnReadyToSendSignalsNetworkState) { + EXPECT_EQ(webrtc::Call::kNetworkUp, + fake_channel_->GetFakeCall()->GetNetworkState()); + + channel_->OnReadyToSend(false); + EXPECT_EQ(webrtc::Call::kNetworkDown, + fake_channel_->GetFakeCall()->GetNetworkState()); + + channel_->OnReadyToSend(true); + EXPECT_EQ(webrtc::Call::kNetworkUp, + fake_channel_->GetFakeCall()->GetNetworkState()); } TEST_F(WebRtcVideoChannel2Test, DISABLED_CaptureFrameTimestampToNtpTimestamp) { diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.h b/talk/media/webrtc/webrtcvideoengine2_unittest.h index 54e6f0621..5aaa3e33d 100644 --- a/talk/media/webrtc/webrtcvideoengine2_unittest.h +++ b/talk/media/webrtc/webrtcvideoengine2_unittest.h @@ -103,6 +103,8 @@ class FakeCall : public webrtc::Call { std::vector<webrtc::VideoCodec> GetDefaultVideoCodecs(); + webrtc::Call::NetworkState GetNetworkState() const; + private: virtual webrtc::VideoSendStream* CreateVideoSendStream( const webrtc::VideoSendStream::Config& config, @@ -122,6 +124,9 @@ class FakeCall : public webrtc::Call { virtual uint32_t SendBitrateEstimate() OVERRIDE; virtual uint32_t ReceiveBitrateEstimate() OVERRIDE; + virtual void SignalNetworkState(webrtc::Call::NetworkState state) OVERRIDE; + + webrtc::Call::NetworkState network_state_; std::vector<webrtc::VideoCodec> codecs_; std::vector<FakeVideoSendStream*> video_send_streams_; std::vector<FakeVideoReceiveStream*> video_receive_streams_; diff --git a/webrtc/call.h b/webrtc/call.h index 86cf1c6ae..303007401 100644 --- a/webrtc/call.h +++ b/webrtc/call.h @@ -56,6 +56,10 @@ class OveruseCallback { // etc. class Call { public: + enum NetworkState { + kNetworkUp, + kNetworkDown, + }; struct Config { explicit Config(newapi::Transport* send_transport) : webrtc_config(NULL), @@ -111,6 +115,8 @@ class Call { // differ from the actual receive bitrate. virtual uint32_t ReceiveBitrateEstimate() = 0; + virtual void SignalNetworkState(NetworkState state) = 0; + virtual ~Call() {} }; } // namespace webrtc diff --git a/webrtc/video/call.cc b/webrtc/video/call.cc index cd29d6273..8b71acfdd 100644 --- a/webrtc/video/call.cc +++ b/webrtc/video/call.cc @@ -28,6 +28,8 @@ #include "webrtc/video_engine/include/vie_base.h" #include "webrtc/video_engine/include/vie_codec.h" #include "webrtc/video_engine/include/vie_rtp_rtcp.h" +#include "webrtc/video_engine/include/vie_network.h" +#include "webrtc/video_engine/include/vie_rtp_rtcp.h" namespace webrtc { const char* RtpExtension::kTOffset = "urn:ietf:params:rtp-hdrext:toffset"; @@ -93,18 +95,26 @@ class Call : public webrtc::Call, public PacketReceiver { virtual DeliveryStatus DeliverPacket(const uint8_t* packet, size_t length) OVERRIDE; + virtual void SignalNetworkState(NetworkState state) OVERRIDE; + private: DeliveryStatus DeliverRtcp(const uint8_t* packet, size_t length); DeliveryStatus DeliverRtp(const uint8_t* packet, size_t length); Call::Config config_; - std::map<uint32_t, VideoReceiveStream*> receive_ssrcs_ - GUARDED_BY(receive_lock_); - scoped_ptr<RWLockWrapper> receive_lock_; + // Needs to be held while write-locking |receive_crit_| or |send_crit_|. This + // ensures that we have a consistent network state signalled to all senders + // and receivers. + scoped_ptr<CriticalSectionWrapper> network_enabled_crit_; + bool network_enabled_ GUARDED_BY(network_enabled_crit_); - std::map<uint32_t, VideoSendStream*> send_ssrcs_ GUARDED_BY(send_lock_); - scoped_ptr<RWLockWrapper> send_lock_; + scoped_ptr<RWLockWrapper> receive_crit_; + std::map<uint32_t, VideoReceiveStream*> receive_ssrcs_ + GUARDED_BY(receive_crit_); + + scoped_ptr<RWLockWrapper> send_crit_; + std::map<uint32_t, VideoSendStream*> send_ssrcs_ GUARDED_BY(send_crit_); scoped_ptr<CpuOveruseObserverProxy> overuse_observer_proxy_; @@ -135,8 +145,10 @@ const int kDefaultVideoStreamBitrateBps = 300000; Call::Call(webrtc::VideoEngine* video_engine, const Call::Config& config) : config_(config), - receive_lock_(RWLockWrapper::CreateRWLock()), - send_lock_(RWLockWrapper::CreateRWLock()), + network_enabled_crit_(CriticalSectionWrapper::CreateCriticalSection()), + network_enabled_(true), + receive_crit_(RWLockWrapper::CreateRWLock()), + send_crit_(RWLockWrapper::CreateRWLock()), video_engine_(video_engine), base_channel_id_(-1) { assert(video_engine != NULL); @@ -192,11 +204,16 @@ VideoSendStream* Call::CreateVideoSendStream( config_.start_bitrate_bps != -1 ? config_.start_bitrate_bps : kDefaultVideoStreamBitrateBps); - WriteLockScoped write_lock(*send_lock_); + // This needs to be taken before send_crit_ as both locks need to be held + // while changing network state. + CriticalSectionScoped lock(network_enabled_crit_.get()); + WriteLockScoped write_lock(*send_crit_); for (size_t i = 0; i < config.rtp.ssrcs.size(); ++i) { assert(send_ssrcs_.find(config.rtp.ssrcs[i]) == send_ssrcs_.end()); send_ssrcs_[config.rtp.ssrcs[i]] = send_stream; } + if (!network_enabled_) + send_stream->SignalNetworkState(kNetworkDown); return send_stream; } @@ -207,7 +224,7 @@ void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { VideoSendStream* send_stream_impl = NULL; { - WriteLockScoped write_lock(*send_lock_); + WriteLockScoped write_lock(*send_crit_); std::map<uint32_t, VideoSendStream*>::iterator it = send_ssrcs_.begin(); while (it != send_ssrcs_.end()) { if (it->second == static_cast<VideoSendStream*>(send_stream)) { @@ -240,7 +257,10 @@ VideoReceiveStream* Call::CreateVideoReceiveStream( config_.voice_engine, base_channel_id_); - WriteLockScoped write_lock(*receive_lock_); + // This needs to be taken before receive_crit_ as both locks need to be held + // while changing network state. + CriticalSectionScoped lock(network_enabled_crit_.get()); + WriteLockScoped write_lock(*receive_crit_); assert(receive_ssrcs_.find(config.rtp.remote_ssrc) == receive_ssrcs_.end()); receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream; // TODO(pbos): Configure different RTX payloads per receive payload. @@ -249,6 +269,8 @@ VideoReceiveStream* Call::CreateVideoReceiveStream( if (it != config.rtp.rtx.end()) receive_ssrcs_[it->second.ssrc] = receive_stream; + if (!network_enabled_) + receive_stream->SignalNetworkState(kNetworkDown); return receive_stream; } @@ -258,7 +280,7 @@ void Call::DestroyVideoReceiveStream( VideoReceiveStream* receive_stream_impl = NULL; { - WriteLockScoped write_lock(*receive_lock_); + WriteLockScoped write_lock(*receive_crit_); // Remove all ssrcs pointing to a receive stream. As RTX retransmits on a // separate SSRC there can be either one or two. std::map<uint32_t, VideoReceiveStream*>::iterator it = @@ -289,6 +311,31 @@ uint32_t Call::ReceiveBitrateEstimate() { return 0; } +void Call::SignalNetworkState(NetworkState state) { + // Take crit for entire function, it needs to be held while updating streams + // to guarantee a consistent state across streams. + CriticalSectionScoped lock(network_enabled_crit_.get()); + network_enabled_ = state == kNetworkUp; + { + ReadLockScoped write_lock(*send_crit_); + for (std::map<uint32_t, VideoSendStream*>::iterator it = + send_ssrcs_.begin(); + it != send_ssrcs_.end(); + ++it) { + it->second->SignalNetworkState(state); + } + } + { + ReadLockScoped write_lock(*receive_crit_); + for (std::map<uint32_t, VideoReceiveStream*>::iterator it = + receive_ssrcs_.begin(); + it != receive_ssrcs_.end(); + ++it) { + it->second->SignalNetworkState(state); + } + } +} + PacketReceiver::DeliveryStatus Call::DeliverRtcp(const uint8_t* packet, size_t length) { // TODO(pbos): Figure out what channel needs it actually. @@ -297,7 +344,7 @@ PacketReceiver::DeliveryStatus Call::DeliverRtcp(const uint8_t* packet, // there's no receiver of the packet. bool rtcp_delivered = false; { - ReadLockScoped read_lock(*receive_lock_); + ReadLockScoped read_lock(*receive_crit_); for (std::map<uint32_t, VideoReceiveStream*>::iterator it = receive_ssrcs_.begin(); it != receive_ssrcs_.end(); @@ -308,7 +355,7 @@ PacketReceiver::DeliveryStatus Call::DeliverRtcp(const uint8_t* packet, } { - ReadLockScoped read_lock(*send_lock_); + ReadLockScoped read_lock(*send_crit_); for (std::map<uint32_t, VideoSendStream*>::iterator it = send_ssrcs_.begin(); it != send_ssrcs_.end(); @@ -329,7 +376,7 @@ PacketReceiver::DeliveryStatus Call::DeliverRtp(const uint8_t* packet, const uint8_t* ptr = &packet[8]; uint32_t ssrc = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; - ReadLockScoped read_lock(*receive_lock_); + ReadLockScoped read_lock(*receive_crit_); std::map<uint32_t, VideoReceiveStream*>::iterator it = receive_ssrcs_.find(ssrc); diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc index 739944ad5..11ce64252 100644 --- a/webrtc/video/end_to_end_tests.cc +++ b/webrtc/video/end_to_end_tests.cc @@ -43,6 +43,7 @@ namespace webrtc { +static const unsigned long kSilenceTimeoutMs = 2000; static const int kRedPayloadType = 118; static const int kUlpfecPayloadType = 119; @@ -56,6 +57,19 @@ class EndToEndTest : public test::CallTest { } protected: + class UnusedTransport : public newapi::Transport { + private: + virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { + ADD_FAILURE() << "Unexpected RTP sent."; + return false; + } + + virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE { + ADD_FAILURE() << "Unexpected RTCP sent."; + return false; + } + }; + void DecodesRetransmittedFrame(bool retransmit_over_rtx); void ReceivesPliAndRecovers(int rtp_history_ms); void RespectsRtcpMode(newapi::RtcpMode rtcp_mode); @@ -1840,6 +1854,228 @@ TEST_F(EndToEndTest, RestartingSendStreamPreservesRtpStatesWithRtx) { TestRtpStatePreservation(true); } +TEST_F(EndToEndTest, RespectsNetworkState) { + // TODO(pbos): Remove accepted downtime packets etc. when signaling network + // down blocks until no more packets will be sent. + + // Pacer will send from its packet list and then send required padding before + // checking paused_ again. This should be enough for one round of pacing, + // otherwise increase. + static const int kNumAcceptedDowntimeRtp = 5; + // A single RTCP may be in the pipeline. + static const int kNumAcceptedDowntimeRtcp = 1; + class NetworkStateTest : public test::EndToEndTest, public test::FakeEncoder { + public: + NetworkStateTest() + : EndToEndTest(kDefaultTimeoutMs), + FakeEncoder(Clock::GetRealTimeClock()), + test_crit_(CriticalSectionWrapper::CreateCriticalSection()), + encoded_frames_(EventWrapper::Create()), + sender_packets_(EventWrapper::Create()), + receiver_packets_(EventWrapper::Create()), + sender_state_(Call::kNetworkUp), + down_sender_rtp_(0), + down_sender_rtcp_(0), + receiver_state_(Call::kNetworkUp), + down_receiver_rtcp_(0), + down_frames_(0) {} + + virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { + CriticalSectionScoped lock(test_crit_.get()); + if (sender_state_ == Call::kNetworkDown) { + ++down_sender_rtp_; + EXPECT_LE(down_sender_rtp_, kNumAcceptedDowntimeRtp) + << "RTP sent during sender-side downtime."; + if (down_sender_rtp_> kNumAcceptedDowntimeRtp) + sender_packets_->Set(); + } else { + sender_packets_->Set(); + } + return SEND_PACKET; + } + + virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { + CriticalSectionScoped lock(test_crit_.get()); + if (sender_state_ == Call::kNetworkDown) { + ++down_sender_rtcp_; + EXPECT_LE(down_sender_rtcp_, kNumAcceptedDowntimeRtcp) + << "RTCP sent during sender-side downtime."; + if (down_sender_rtcp_ > kNumAcceptedDowntimeRtcp) + sender_packets_->Set(); + } else { + sender_packets_->Set(); + } + return SEND_PACKET; + } + + virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) OVERRIDE { + ADD_FAILURE() << "Unexpected receiver RTP, should not be sending."; + return SEND_PACKET; + } + + virtual Action OnReceiveRtcp(const uint8_t* packet, + size_t length) OVERRIDE { + CriticalSectionScoped lock(test_crit_.get()); + if (receiver_state_ == Call::kNetworkDown) { + ++down_receiver_rtcp_; + EXPECT_LE(down_receiver_rtcp_, kNumAcceptedDowntimeRtcp) + << "RTCP sent during receiver-side downtime."; + if (down_receiver_rtcp_ > kNumAcceptedDowntimeRtcp) + receiver_packets_->Set(); + } else { + receiver_packets_->Set(); + } + return SEND_PACKET; + } + + virtual void OnCallsCreated(Call* sender_call, + Call* receiver_call) OVERRIDE { + sender_call_ = sender_call; + receiver_call_ = receiver_call; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + std::vector<VideoStream>* video_streams) OVERRIDE { + send_config->encoder_settings.encoder = this; + } + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, encoded_frames_->Wait(kDefaultTimeoutMs)) + << "No frames received by the encoder."; + EXPECT_EQ(kEventSignaled, sender_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for send-side packets."; + EXPECT_EQ(kEventSignaled, receiver_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for receiver-side packets."; + + // Sender-side network down. + sender_call_->SignalNetworkState(Call::kNetworkDown); + { + CriticalSectionScoped lock(test_crit_.get()); + sender_packets_->Reset(); // Earlier packets should not count. + sender_state_ = Call::kNetworkDown; + } + EXPECT_EQ(kEventTimeout, sender_packets_->Wait(kSilenceTimeoutMs)) + << "Packets sent during sender-network downtime."; + EXPECT_EQ(kEventSignaled, receiver_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for receiver-side packets."; + // Receiver-side network down. + receiver_call_->SignalNetworkState(Call::kNetworkDown); + { + CriticalSectionScoped lock(test_crit_.get()); + receiver_packets_->Reset(); // Earlier packets should not count. + receiver_state_ = Call::kNetworkDown; + } + EXPECT_EQ(kEventTimeout, receiver_packets_->Wait(kSilenceTimeoutMs)) + << "Packets sent during receiver-network downtime."; + + // Network back up again for both. + { + CriticalSectionScoped lock(test_crit_.get()); + sender_packets_->Reset(); // Earlier packets should not count. + receiver_packets_->Reset(); // Earlier packets should not count. + sender_state_ = receiver_state_ = Call::kNetworkUp; + } + sender_call_->SignalNetworkState(Call::kNetworkUp); + receiver_call_->SignalNetworkState(Call::kNetworkUp); + EXPECT_EQ(kEventSignaled, sender_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for send-side packets."; + EXPECT_EQ(kEventSignaled, receiver_packets_->Wait(kDefaultTimeoutMs)) + << "Timed out waiting for receiver-side packets."; + } + + virtual int32_t Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector<VideoFrameType>* frame_types) + OVERRIDE { + { + CriticalSectionScoped lock(test_crit_.get()); + if (sender_state_ == Call::kNetworkDown) { + ++down_frames_; + EXPECT_LE(down_frames_, 1) + << "Encoding more than one frame while network is down."; + if (down_frames_ > 1) + encoded_frames_->Set(); + } else { + encoded_frames_->Set(); + } + } + return test::FakeEncoder::Encode( + input_image, codec_specific_info, frame_types); + } + + private: + const scoped_ptr<CriticalSectionWrapper> test_crit_; + scoped_ptr<EventWrapper> encoded_frames_; + scoped_ptr<EventWrapper> sender_packets_; + scoped_ptr<EventWrapper> receiver_packets_; + Call* sender_call_; + Call* receiver_call_; + Call::NetworkState sender_state_ GUARDED_BY(test_crit_); + int down_sender_rtp_ GUARDED_BY(test_crit_); + int down_sender_rtcp_ GUARDED_BY(test_crit_); + Call::NetworkState receiver_state_ GUARDED_BY(test_crit_); + int down_receiver_rtcp_ GUARDED_BY(test_crit_); + int down_frames_ GUARDED_BY(test_crit_); + } test; + + RunBaseTest(&test); +} + +TEST_F(EndToEndTest, NewSendStreamsRespectNetworkDown) { + class UnusedEncoder : public test::FakeEncoder { + public: + UnusedEncoder() : FakeEncoder(Clock::GetRealTimeClock()) {} + virtual int32_t Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector<VideoFrameType>* frame_types) + OVERRIDE { + ADD_FAILURE() << "Unexpected frame encode."; + return test::FakeEncoder::Encode( + input_image, codec_specific_info, frame_types); + } + }; + + UnusedTransport transport; + CreateSenderCall(Call::Config(&transport)); + sender_call_->SignalNetworkState(Call::kNetworkDown); + + CreateSendConfig(1); + UnusedEncoder unused_encoder; + send_config_.encoder_settings.encoder = &unused_encoder; + CreateStreams(); + CreateFrameGeneratorCapturer(); + + Start(); + SleepMs(kSilenceTimeoutMs); + Stop(); + + DestroyStreams(); +} + +TEST_F(EndToEndTest, NewReceiveStreamsRespectNetworkDown) { + test::DirectTransport sender_transport; + CreateSenderCall(Call::Config(&sender_transport)); + UnusedTransport transport; + CreateReceiverCall(Call::Config(&transport)); + sender_transport.SetReceiver(receiver_call_->Receiver()); + + receiver_call_->SignalNetworkState(Call::kNetworkDown); + + CreateSendConfig(1); + CreateMatchingReceiveConfigs(); + CreateStreams(); + CreateFrameGeneratorCapturer(); + + Start(); + SleepMs(kSilenceTimeoutMs); + Stop(); + + sender_transport.StopSending(); + + DestroyStreams(); +} } // namespace webrtc #endif // !WEBRTC_ANDROID diff --git a/webrtc/video/video_receive_stream.cc b/webrtc/video/video_receive_stream.cc index 9c3298c55..41a800fab 100644 --- a/webrtc/video/video_receive_stream.cc +++ b/webrtc/video/video_receive_stream.cc @@ -52,14 +52,7 @@ VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine, // TODO(pbos): This is not fine grained enough... rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0); rtp_rtcp_->SetKeyFrameRequestMethod(channel_, kViEKeyFrameRequestPliRtcp); - switch (config_.rtp.rtcp_mode) { - case newapi::kRtcpCompound: - rtp_rtcp_->SetRTCPStatus(channel_, kRtcpCompound_RFC4585); - break; - case newapi::kRtcpReducedSize: - rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNonCompound_RFC5506); - break; - } + SetRtcpMode(config_.rtp.rtcp_mode); assert(config_.rtp.remote_ssrc != 0); // TODO(pbos): What's an appropriate local_ssrc for receive-only streams? @@ -264,5 +257,24 @@ int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id, return 0; } + +void VideoReceiveStream::SignalNetworkState(Call::NetworkState state) { + if (state == Call::kNetworkUp) + SetRtcpMode(config_.rtp.rtcp_mode); + network_->SetNetworkTransmissionState(channel_, state == Call::kNetworkUp); + if (state == Call::kNetworkDown) + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNone); +} + +void VideoReceiveStream::SetRtcpMode(newapi::RtcpMode mode) { + switch (mode) { + case newapi::kRtcpCompound: + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpCompound_RFC4585); + break; + case newapi::kRtcpReducedSize: + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNonCompound_RFC5506); + break; + } +} } // namespace internal } // namespace webrtc diff --git a/webrtc/video/video_receive_stream.h b/webrtc/video/video_receive_stream.h index c45ebac80..689482877 100644 --- a/webrtc/video/video_receive_stream.h +++ b/webrtc/video/video_receive_stream.h @@ -13,6 +13,7 @@ #include <vector> +#include "webrtc/call.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/modules/video_render/include/video_render_defines.h" #include "webrtc/system_wrappers/interface/clock.h" @@ -61,11 +62,14 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream, virtual int32_t RenderFrame(const uint32_t stream_id, I420VideoFrame& video_frame) OVERRIDE; - public: + void SignalNetworkState(Call::NetworkState state); + virtual bool DeliverRtcp(const uint8_t* packet, size_t length); virtual bool DeliverRtp(const uint8_t* packet, size_t length); private: + void SetRtcpMode(newapi::RtcpMode mode); + TransportAdapter transport_adapter_; EncodedFrameCallbackAdapter encoded_frame_proxy_; const VideoReceiveStream::Config config_; diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc index 6597029a9..624aca822 100644 --- a/webrtc/video/video_send_stream.cc +++ b/webrtc/video/video_send_stream.cc @@ -460,5 +460,16 @@ std::map<uint32_t, RtpState> VideoSendStream::GetRtpStates() const { return rtp_states; } +void VideoSendStream::SignalNetworkState(Call::NetworkState state) { + // When network goes up, enable RTCP status before setting transmission state. + // When it goes down, disable RTCP afterwards. This ensures that any packets + // sent due to the network state changed will not be dropped. + if (state == Call::kNetworkUp) + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpCompound_RFC4585); + network_->SetNetworkTransmissionState(channel_, state == Call::kNetworkUp); + if (state == Call::kNetworkDown) + rtp_rtcp_->SetRTCPStatus(channel_, kRtcpNone); +} + } // namespace internal } // namespace webrtc diff --git a/webrtc/video/video_send_stream.h b/webrtc/video/video_send_stream.h index f1f8f7d1a..130c1c5ca 100644 --- a/webrtc/video/video_send_stream.h +++ b/webrtc/video/video_send_stream.h @@ -14,6 +14,7 @@ #include <map> #include <vector> +#include "webrtc/call.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/video/encoded_frame_callback_adapter.h" @@ -72,6 +73,8 @@ class VideoSendStream : public webrtc::VideoSendStream, typedef std::map<uint32_t, RtpState> RtpStateMap; RtpStateMap GetRtpStates() const; + void SignalNetworkState(Call::NetworkState state); + private: void ConfigureSsrcs(); TransportAdapter transport_adapter_;