diff --git a/webrtc/api/mediastreamprovider.h b/webrtc/api/mediastreamprovider.h index b508c39f7e..784a95423d 100644 --- a/webrtc/api/mediastreamprovider.h +++ b/webrtc/api/mediastreamprovider.h @@ -72,6 +72,9 @@ class AudioProviderInterface { uint32_t ssrc, const RtpParameters& parameters) = 0; + // Called when the first audio packet is received. + sigslot::signal0<> SignalFirstAudioPacketReceived; + protected: virtual ~AudioProviderInterface() {} }; @@ -105,6 +108,9 @@ class VideoProviderInterface { uint32_t ssrc, const RtpParameters& parameters) = 0; + // Called when the first video packet is received. + sigslot::signal0<> SignalFirstVideoPacketReceived; + protected: virtual ~VideoProviderInterface() {} }; diff --git a/webrtc/api/peerconnection_unittest.cc b/webrtc/api/peerconnection_unittest.cc index 20cd7a6439..4b06b90d01 100644 --- a/webrtc/api/peerconnection_unittest.cc +++ b/webrtc/api/peerconnection_unittest.cc @@ -146,6 +146,25 @@ class SignalingMessageReceiver { virtual ~SignalingMessageReceiver() {} }; +class MockRtpReceiverObserver : public webrtc::RtpReceiverObserverInterface { + public: + MockRtpReceiverObserver(cricket::MediaType media_type) + : expected_media_type_(media_type) {} + + void OnFirstPacketReceived(cricket::MediaType media_type) override { + ASSERT_EQ(expected_media_type_, media_type); + first_packet_received_ = true; + } + + bool first_packet_received() { return first_packet_received_; } + + virtual ~MockRtpReceiverObserver() {} + + private: + bool first_packet_received_ = false; + cricket::MediaType expected_media_type_; +}; + class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, public SignalingMessageReceiver, public ObserverInterface { @@ -780,6 +799,21 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, return pc()->ice_gathering_state(); } + std::vector> const& + rtp_receiver_observers() { + return rtp_receiver_observers_; + } + + void SetRtpReceiverObservers() { + rtp_receiver_observers_.clear(); + for (auto receiver : pc()->GetReceivers()) { + std::unique_ptr observer( + new MockRtpReceiverObserver(receiver->media_type())); + receiver->SetObserver(observer.get()); + rtp_receiver_observers_.push_back(std::move(observer)); + } + } + private: class DummyDtmfObserver : public DtmfSenderObserverInterface { public: @@ -870,6 +904,8 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, std::unique_ptr desc( webrtc::CreateSessionDescription("offer", msg, nullptr)); EXPECT_TRUE(DoSetRemoteDescription(desc.release())); + // Set the RtpReceiverObserver after receivers are created. + SetRtpReceiverObservers(); std::unique_ptr answer; EXPECT_TRUE(DoCreateAnswer(&answer)); std::string sdp; @@ -886,6 +922,8 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, std::unique_ptr desc( webrtc::CreateSessionDescription("answer", msg, nullptr)); EXPECT_TRUE(DoSetRemoteDescription(desc.release())); + // Set the RtpReceiverObserver after receivers are created. + SetRtpReceiverObservers(); } bool DoCreateOfferAnswer(std::unique_ptr* desc, @@ -1026,6 +1064,8 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, rtc::scoped_refptr data_channel_; std::unique_ptr data_observer_; + + std::vector> rtp_receiver_observers_; }; class P2PTestConductor : public testing::Test { @@ -1314,6 +1354,16 @@ class P2PTestConductor : public testing::Test { return old; } + bool AllObserversReceived( + const std::vector>& observers) { + for (auto& observer : observers) { + if (!observer->first_packet_received()) { + return false; + } + } + return true; + } + private: // |ss_| is used by |network_thread_| so it must be destroyed later. std::unique_ptr pss_; @@ -1332,6 +1382,33 @@ class P2PTestConductor : public testing::Test { // https://code.google.com/p/webrtc/issues/detail?id=1205 for details. #if !defined(THREAD_SANITIZER) +TEST_F(P2PTestConductor, TestRtpReceiverObserverCallbackFunction) { + ASSERT_TRUE(CreateTestClients()); + LocalP2PTest(); + EXPECT_TRUE_WAIT( + AllObserversReceived(initializing_client()->rtp_receiver_observers()), + kMaxWaitForFramesMs); + EXPECT_TRUE_WAIT( + AllObserversReceived(receiving_client()->rtp_receiver_observers()), + kMaxWaitForFramesMs); +} + +// The observers are expected to fire the signal even if they are set after the +// first packet is received. +TEST_F(P2PTestConductor, TestSetRtpReceiverObserverAfterFirstPacketIsReceived) { + ASSERT_TRUE(CreateTestClients()); + LocalP2PTest(); + // Reset the RtpReceiverObservers. + initializing_client()->SetRtpReceiverObservers(); + receiving_client()->SetRtpReceiverObservers(); + EXPECT_TRUE_WAIT( + AllObserversReceived(initializing_client()->rtp_receiver_observers()), + kMaxWaitForFramesMs); + EXPECT_TRUE_WAIT( + AllObserversReceived(receiving_client()->rtp_receiver_observers()), + kMaxWaitForFramesMs); +} + // This test sets up a Jsep call between two parties and test Dtmf. // TODO(holmer): Disabled due to sometimes crashing on buildbots. // See issue webrtc/2378. diff --git a/webrtc/api/rtpreceiver.cc b/webrtc/api/rtpreceiver.cc index 9df336e1f0..882bc2be8d 100644 --- a/webrtc/api/rtpreceiver.cc +++ b/webrtc/api/rtpreceiver.cc @@ -35,6 +35,8 @@ AudioRtpReceiver::AudioRtpReceiver(MediaStreamInterface* stream, track_->GetSource()->RegisterAudioObserver(this); Reconfigure(); stream->AddTrack(track_); + provider_->SignalFirstAudioPacketReceived.connect( + this, &AudioRtpReceiver::OnFirstAudioPacketReceived); } AudioRtpReceiver::~AudioRtpReceiver() { @@ -83,6 +85,22 @@ void AudioRtpReceiver::Reconfigure() { provider_->SetAudioPlayout(ssrc_, track_->enabled()); } +void AudioRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) { + observer_ = observer; + // If received the first packet before setting the observer, call the + // observer. + if (received_first_packet_) { + observer_->OnFirstPacketReceived(media_type()); + } +} + +void AudioRtpReceiver::OnFirstAudioPacketReceived() { + if (observer_) { + observer_->OnFirstPacketReceived(media_type()); + } + received_first_packet_ = true; +} + VideoRtpReceiver::VideoRtpReceiver(MediaStreamInterface* stream, const std::string& track_id, rtc::Thread* worker_thread, @@ -104,6 +122,8 @@ VideoRtpReceiver::VideoRtpReceiver(MediaStreamInterface* stream, source_->SetState(MediaSourceInterface::kLive); provider_->SetVideoPlayout(ssrc_, true, &broadcaster_); stream->AddTrack(track_); + provider_->SignalFirstVideoPacketReceived.connect( + this, &VideoRtpReceiver::OnFirstVideoPacketReceived); } VideoRtpReceiver::~VideoRtpReceiver() { @@ -132,4 +152,20 @@ void VideoRtpReceiver::Stop() { provider_ = nullptr; } +void VideoRtpReceiver::SetObserver(RtpReceiverObserverInterface* observer) { + observer_ = observer; + // If received the first packet before setting the observer, call the + // observer. + if (received_first_packet_) { + observer_->OnFirstPacketReceived(media_type()); + } +} + +void VideoRtpReceiver::OnFirstVideoPacketReceived() { + if (observer_) { + observer_->OnFirstPacketReceived(media_type()); + } + received_first_packet_ = true; +} + } // namespace webrtc diff --git a/webrtc/api/rtpreceiver.h b/webrtc/api/rtpreceiver.h index 001264d7a7..3b1fd926cb 100644 --- a/webrtc/api/rtpreceiver.h +++ b/webrtc/api/rtpreceiver.h @@ -22,6 +22,7 @@ #include "webrtc/api/remoteaudiosource.h" #include "webrtc/api/videotracksource.h" #include "webrtc/base/basictypes.h" +#include "webrtc/base/sigslot.h" #include "webrtc/media/base/videobroadcaster.h" namespace webrtc { @@ -34,7 +35,8 @@ class RtpReceiverInternal : public RtpReceiverInterface { class AudioRtpReceiver : public ObserverInterface, public AudioSourceInterface::AudioObserver, - public rtc::RefCountedObject { + public rtc::RefCountedObject, + public sigslot::has_slots<> { public: AudioRtpReceiver(MediaStreamInterface* stream, const std::string& track_id, @@ -66,17 +68,25 @@ class AudioRtpReceiver : public ObserverInterface, // RtpReceiverInternal implementation. void Stop() override; + void SetObserver(RtpReceiverObserverInterface* observer) override; + + cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_AUDIO; } + private: void Reconfigure(); + void OnFirstAudioPacketReceived(); const std::string id_; const uint32_t ssrc_; AudioProviderInterface* provider_; // Set to null in Stop(). const rtc::scoped_refptr track_; bool cached_track_enabled_; + RtpReceiverObserverInterface* observer_ = nullptr; + bool received_first_packet_ = false; }; -class VideoRtpReceiver : public rtc::RefCountedObject { +class VideoRtpReceiver : public rtc::RefCountedObject, + public sigslot::has_slots<> { public: VideoRtpReceiver(MediaStreamInterface* stream, const std::string& track_id, @@ -103,7 +113,13 @@ class VideoRtpReceiver : public rtc::RefCountedObject { // RtpReceiverInternal implementation. void Stop() override; + void SetObserver(RtpReceiverObserverInterface* observer) override; + + cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_VIDEO; } + private: + void OnFirstVideoPacketReceived(); + std::string id_; uint32_t ssrc_; VideoProviderInterface* provider_; @@ -115,6 +131,8 @@ class VideoRtpReceiver : public rtc::RefCountedObject { // the VideoRtpReceiver is stopped. rtc::scoped_refptr source_; rtc::scoped_refptr track_; + RtpReceiverObserverInterface* observer_ = nullptr; + bool received_first_packet_ = false; }; } // namespace webrtc diff --git a/webrtc/api/rtpreceiverinterface.h b/webrtc/api/rtpreceiverinterface.h index 49888381e1..4943023111 100644 --- a/webrtc/api/rtpreceiverinterface.h +++ b/webrtc/api/rtpreceiverinterface.h @@ -20,9 +20,18 @@ #include "webrtc/api/proxy.h" #include "webrtc/base/refcount.h" #include "webrtc/base/scoped_ref_ptr.h" +#include "webrtc/pc/mediasession.h" namespace webrtc { +class RtpReceiverObserverInterface { + public: + virtual void OnFirstPacketReceived(cricket::MediaType media_type) = 0; + + protected: + virtual ~RtpReceiverObserverInterface() {} +}; + class RtpReceiverInterface : public rtc::RefCountInterface { public: virtual rtc::scoped_refptr track() const = 0; @@ -37,6 +46,10 @@ class RtpReceiverInterface : public rtc::RefCountInterface { virtual RtpParameters GetParameters() const = 0; virtual bool SetParameters(const RtpParameters& parameters) = 0; + virtual void SetObserver(RtpReceiverObserverInterface* observer) = 0; + + virtual cricket::MediaType media_type() = 0; + protected: virtual ~RtpReceiverInterface() {} }; @@ -47,6 +60,8 @@ PROXY_CONSTMETHOD0(rtc::scoped_refptr, track) PROXY_CONSTMETHOD0(std::string, id) PROXY_CONSTMETHOD0(RtpParameters, GetParameters); PROXY_METHOD1(bool, SetParameters, const RtpParameters&) +PROXY_METHOD1(void, SetObserver, RtpReceiverObserverInterface*); +PROXY_METHOD0(cricket::MediaType, media_type); END_SIGNALING_PROXY() } // namespace webrtc diff --git a/webrtc/api/webrtcsession.cc b/webrtc/api/webrtcsession.cc index a7c712c6a1..b9a3fc8453 100644 --- a/webrtc/api/webrtcsession.cc +++ b/webrtc/api/webrtcsession.cc @@ -1767,6 +1767,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content, voice_channel_->SignalDtlsSetupFailure.connect( this, &WebRtcSession::OnDtlsSetupFailure); + voice_channel_->SignalFirstPacketReceived.connect( + this, &WebRtcSession::OnChannelFirstPacketReceived); SignalVoiceChannelCreated(); voice_channel_->SignalSentPacket.connect(this, @@ -1790,6 +1792,8 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content, } video_channel_->SignalDtlsSetupFailure.connect( this, &WebRtcSession::OnDtlsSetupFailure); + video_channel_->SignalFirstPacketReceived.connect( + this, &WebRtcSession::OnChannelFirstPacketReceived); SignalVideoChannelCreated(); video_channel_->SignalSentPacket.connect(this, @@ -1831,6 +1835,21 @@ void WebRtcSession::OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp) { rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp); } +void WebRtcSession::OnChannelFirstPacketReceived( + cricket::BaseChannel* channel) { + ASSERT(signaling_thread()->IsCurrent()); + + if (!received_first_audio_packet_ && + channel->media_type() == cricket::MEDIA_TYPE_AUDIO) { + received_first_audio_packet_ = true; + SignalFirstAudioPacketReceived(); + } else if (!received_first_video_packet_ && + channel->media_type() == cricket::MEDIA_TYPE_VIDEO) { + received_first_video_packet_ = true; + SignalFirstVideoPacketReceived(); + } +} + void WebRtcSession::OnDataChannelMessageReceived( cricket::DataChannel* channel, const cricket::ReceiveDataParams& params, diff --git a/webrtc/api/webrtcsession.h b/webrtc/api/webrtcsession.h index 9ba7605e67..76af6c7c3d 100644 --- a/webrtc/api/webrtcsession.h +++ b/webrtc/api/webrtcsession.h @@ -310,6 +310,8 @@ class WebRtcSession : public AudioProviderInterface, void OnCertificateReady( const rtc::scoped_refptr& certificate); void OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp); + // Called when the channel received the first packet. + void OnChannelFirstPacketReceived(cricket::BaseChannel*); // For unit test. bool waiting_for_certificate_for_testing() const; @@ -527,6 +529,9 @@ class WebRtcSession : public AudioProviderInterface, // Declares the RTCP mux policy for the WebRTCSession. PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy_; + bool received_first_video_packet_ = false; + bool received_first_audio_packet_ = false; + RTC_DISALLOW_COPY_AND_ASSIGN(WebRtcSession); }; } // namespace webrtc diff --git a/webrtc/pc/channel.h b/webrtc/pc/channel.h index 5dca31804d..211f810b85 100644 --- a/webrtc/pc/channel.h +++ b/webrtc/pc/channel.h @@ -168,6 +168,8 @@ class BaseChannel SrtpFilter* srtp_filter() { return &srtp_filter_; } + virtual cricket::MediaType media_type() = 0; + protected: virtual MediaChannel* media_channel() const { return media_channel_; } // Sets the |transport_channel_| (and |rtcp_transport_channel_|, if |rtcp_| is @@ -436,6 +438,7 @@ class VoiceChannel : public BaseChannel { webrtc::RtpParameters GetRtpReceiveParameters_w(uint32_t ssrc) const; bool SetRtpReceiveParameters_w(uint32_t ssrc, webrtc::RtpParameters parameters); + cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_AUDIO; } private: // overrides from BaseChannel @@ -520,6 +523,7 @@ class VideoChannel : public BaseChannel { webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const; bool SetRtpReceiveParameters(uint32_t ssrc, const webrtc::RtpParameters& parameters); + cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_VIDEO; } private: // overrides from BaseChannel @@ -591,6 +595,7 @@ class DataChannel : public BaseChannel { sigslot::signal1 SignalReadyToSendData; // Signal for notifying that the remote side has closed the DataChannel. sigslot::signal1 SignalStreamClosedRemotely; + cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_DATA; } protected: // downcasts a MediaChannel.