diff --git a/talk/app/webrtc/mediastreamsignaling.cc b/talk/app/webrtc/mediastreamsignaling.cc index e1b33f102c..c7fa67323a 100644 --- a/talk/app/webrtc/mediastreamsignaling.cc +++ b/talk/app/webrtc/mediastreamsignaling.cc @@ -198,14 +198,8 @@ void MediaStreamSignaling::TearDown() { bool MediaStreamSignaling::IsSctpSidAvailable(int sid) const { if (sid < 0 || sid > static_cast(cricket::kMaxSctpSid)) return false; - for (SctpDataChannels::const_iterator iter = sctp_data_channels_.begin(); - iter != sctp_data_channels_.end(); - ++iter) { - if ((*iter)->id() == sid) { - return false; - } - } - return true; + + return FindDataChannelBySid(sid) < 0; } // Gets the first unused odd/even id based on the DTLS role. If |role| is @@ -953,6 +947,17 @@ void MediaStreamSignaling::OnDtlsRoleReadyForSctp(talk_base::SSLRole role) { } } + +void MediaStreamSignaling::OnRemoteSctpDataChannelClosed(uint32 sid) { + int index = FindDataChannelBySid(sid); + if (index < 0) { + LOG(LS_WARNING) << "Unexpected sid " << sid + << " of the remotely closed DataChannel."; + return; + } + sctp_data_channels_[index]->Close(); +} + const MediaStreamSignaling::TrackInfo* MediaStreamSignaling::FindTrackInfo( const MediaStreamSignaling::TrackInfos& infos, @@ -967,4 +972,13 @@ MediaStreamSignaling::FindTrackInfo( return NULL; } +int MediaStreamSignaling::FindDataChannelBySid(int sid) const { + for (size_t i = 0; i < sctp_data_channels_.size(); ++i) { + if (sctp_data_channels_[i]->id() == sid) { + return static_cast(i); + } + } + return -1; +} + } // namespace webrtc diff --git a/talk/app/webrtc/mediastreamsignaling.h b/talk/app/webrtc/mediastreamsignaling.h index dc83b47dad..8051289ab2 100644 --- a/talk/app/webrtc/mediastreamsignaling.h +++ b/talk/app/webrtc/mediastreamsignaling.h @@ -37,6 +37,7 @@ #include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/app/webrtc/streamcollection.h" #include "talk/base/scoped_ref_ptr.h" +#include "talk/base/sigslot.h" #include "talk/session/media/mediasession.h" namespace talk_base { @@ -157,7 +158,7 @@ class MediaStreamSignalingObserver { // DataChannel label or SSRC. The DataChannel SSRC is updated with SSRC=0. // The DataChannel change state to kClosed. -class MediaStreamSignaling { +class MediaStreamSignaling : public sigslot::has_slots<> { public: MediaStreamSignaling(talk_base::Thread* signaling_thread, MediaStreamSignalingObserver* stream_observer, @@ -248,6 +249,7 @@ class MediaStreamSignaling { } void OnDataTransportCreatedForSctp(); void OnDtlsRoleReadyForSctp(talk_base::SSLRole role); + void OnRemoteSctpDataChannelClosed(uint32 sid); private: struct RemotePeerInfo { @@ -368,6 +370,10 @@ class MediaStreamSignaling { const std::string& stream_label, const std::string track_id) const; + // Returns the index of the specified SCTP DataChannel in sctp_data_channels_, + // or -1 if not found. + int FindDataChannelBySid(int sid) const; + RemotePeerInfo remote_info_; talk_base::Thread* signaling_thread_; DataChannelFactory* data_channel_factory_; @@ -388,6 +394,7 @@ class MediaStreamSignaling { typedef std::map > RtpDataChannels; typedef std::vector > SctpDataChannels; + RtpDataChannels rtp_data_channels_; SctpDataChannels sctp_data_channels_; }; diff --git a/talk/app/webrtc/mediastreamsignaling_unittest.cc b/talk/app/webrtc/mediastreamsignaling_unittest.cc index 03452738b8..14b68e95cf 100644 --- a/talk/app/webrtc/mediastreamsignaling_unittest.cc +++ b/talk/app/webrtc/mediastreamsignaling_unittest.cc @@ -1193,3 +1193,22 @@ TEST_F(MediaStreamSignalingTest, DuplicatedLabelFromOpenMessageAllowed) { params.ssrc = config.id; EXPECT_TRUE(signaling_->AddDataChannelFromOpenMessage(params, payload)); } + +// Verifies that a DataChannel closed remotely is closed locally. +TEST_F(MediaStreamSignalingTest, + SctpDataChannelClosedLocallyWhenClosedRemotely) { + webrtc::InternalDataChannelInit config; + config.id = 0; + + talk_base::scoped_refptr data_channel = + webrtc::DataChannel::Create( + data_channel_provider_.get(), cricket::DCT_SCTP, "a", config); + ASSERT_TRUE(data_channel.get() != NULL); + EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, + data_channel->state()); + + EXPECT_TRUE(signaling_->AddDataChannel(data_channel.get())); + + signaling_->OnRemoteSctpDataChannelClosed(config.id); + EXPECT_EQ(webrtc::DataChannelInterface::kClosed, data_channel->state()); +} diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc index 63f461ed3e..d3bae91c4c 100644 --- a/talk/app/webrtc/webrtcsession.cc +++ b/talk/app/webrtc/webrtcsession.cc @@ -1548,6 +1548,9 @@ bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) { mediastream_signaling_->OnDataTransportCreatedForSctp(); data_channel_->SignalDataReceived.connect( this, &WebRtcSession::OnDataChannelMessageReceived); + data_channel_->SignalStreamClosedRemotely.connect( + mediastream_signaling_, + &MediaStreamSignaling::OnRemoteSctpDataChannelClosed); } return true; } diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h index 02c3d003bc..2772cc6ad2 100644 --- a/talk/media/base/mediachannel.h +++ b/talk/media/base/mediachannel.h @@ -1277,6 +1277,8 @@ class DataMediaChannel : public MediaChannel { // Signal when the media channel is ready to send the stream. Arguments are: // writable(bool) sigslot::signal1 SignalReadyToSend; + // Signal for notifying that the remote side has closed the DataChannel. + sigslot::signal1 SignalStreamClosedRemotely; }; } // namespace cricket diff --git a/talk/media/sctp/sctpdataengine.cc b/talk/media/sctp/sctpdataengine.cc index c040e9daa6..017454f220 100644 --- a/talk/media/sctp/sctpdataengine.cc +++ b/talk/media/sctp/sctpdataengine.cc @@ -818,7 +818,7 @@ void SctpDataMediaChannel::OnStreamResetEvent( LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ << "): closing sid " << stream_id; open_streams_.erase(it); - SignalStreamClosed(stream_id); + SignalStreamClosedRemotely(stream_id); } else if ((it = queued_reset_streams_.find(stream_id)) != queued_reset_streams_.end()) { diff --git a/talk/media/sctp/sctpdataengine.h b/talk/media/sctp/sctpdataengine.h index 7561977c0c..2e8beecd13 100644 --- a/talk/media/sctp/sctpdataengine.h +++ b/talk/media/sctp/sctpdataengine.h @@ -190,9 +190,6 @@ class SctpDataMediaChannel : public DataMediaChannel, } const std::string& debug_name() const { return debug_name_; } - // Called with the SSID of a remote stream that's been closed. - sigslot::signal1 SignalStreamClosed; - private: sockaddr_conn GetSctpSockAddr(int port); diff --git a/talk/media/sctp/sctpdataengine_unittest.cc b/talk/media/sctp/sctpdataengine_unittest.cc index 3cbcfec4a5..092524b4cc 100644 --- a/talk/media/sctp/sctpdataengine_unittest.cc +++ b/talk/media/sctp/sctpdataengine_unittest.cc @@ -167,24 +167,24 @@ class SignalChannelClosedObserver : public sigslot::has_slots<> { public: SignalChannelClosedObserver() {} void BindSelf(cricket::SctpDataMediaChannel* channel) { - channel->SignalStreamClosed.connect( + channel->SignalStreamClosedRemotely.connect( this, &SignalChannelClosedObserver::OnStreamClosed); } - void OnStreamClosed(int stream) { + void OnStreamClosed(uint32 stream) { streams_.push_back(stream); } - int StreamCloseCount(int stream) { + int StreamCloseCount(uint32 stream) { return std::count(streams_.begin(), streams_.end(), stream); } - bool WasStreamClosed(int stream) { + bool WasStreamClosed(uint32 stream) { return std::find(streams_.begin(), streams_.end(), stream) != streams_.end(); } private: - std::vector streams_; + std::vector streams_; }; class SignalChannelClosedReopener : public sigslot::has_slots<> { diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc index b13d5e169d..575c759533 100644 --- a/talk/session/media/channel.cc +++ b/talk/session/media/channel.cc @@ -54,6 +54,7 @@ enum { MSG_READYTOSENDDATA, MSG_DATARECEIVED, MSG_FIRSTPACKETRECEIVED, + MSG_STREAMCLOSEDREMOTELY, }; // Value specified in RFC 5764. @@ -2148,6 +2149,8 @@ bool DataChannel::Init() { this, &DataChannel::OnDataChannelError); media_channel()->SignalReadyToSend.connect( this, &DataChannel::OnDataChannelReadyToSend); + media_channel()->SignalStreamClosedRemotely.connect( + this, &DataChannel::OnStreamClosedRemotely); srtp_filter()->SignalSrtpError.connect( this, &DataChannel::OnSrtpError); return true; @@ -2387,6 +2390,13 @@ void DataChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } + case MSG_STREAMCLOSEDREMOTELY: { + talk_base::TypedMessageData* data = + static_cast*>(pmsg->pdata); + SignalStreamClosedRemotely(data->data()); + delete data; + break; + } default: BaseChannel::OnMessage(pmsg); break; @@ -2473,4 +2483,10 @@ bool DataChannel::ShouldSetupDtlsSrtp() const { return (data_channel_type_ == DCT_RTP); } +void DataChannel::OnStreamClosedRemotely(uint32 sid) { + talk_base::TypedMessageData* message = + new talk_base::TypedMessageData(sid); + signaling_thread()->Post(this, MSG_STREAMCLOSEDREMOTELY, message); +} + } // namespace cricket diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h index 2aec552f72..d48a64e10d 100644 --- a/talk/session/media/channel.h +++ b/talk/session/media/channel.h @@ -640,6 +640,8 @@ class DataChannel : public BaseChannel { // That occurs when the channel is enabled, the transport is writable, // both local and remote descriptions are set, and the channel is unblocked. sigslot::signal1 SignalReadyToSendData; + // Signal for notifying that the remote side has closed the DataChannel. + sigslot::signal1 SignalStreamClosedRemotely; protected: // downcasts a MediaChannel. @@ -711,6 +713,7 @@ class DataChannel : public BaseChannel { void OnDataChannelError(uint32 ssrc, DataMediaChannel::Error error); void OnDataChannelReadyToSend(bool writable); void OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error); + void OnStreamClosedRemotely(uint32 sid); talk_base::scoped_ptr media_monitor_; // TODO(pthatcher): Make a separate SctpDataChannel and