From 75737c0c6a85ee8a45492d87b5a558444cd1e6de Mon Sep 17 00:00:00 2001 From: Steve Anton Date: Mon, 6 Nov 2017 10:37:17 -0800 Subject: [PATCH] Merge WebRtcSession into PeerConnection This literally copies & pastes the code from WebRtcSession into PeerConnection as private methods. The only other changes were to inline the WebRtcSession construction/initialization/destruction into PeerConnection and fix issues using rtc::Bind on the reference-counted PeerConnection. Bug: webrtc:8323 Change-Id: Ib3f071ac10d18566a21a3b04813b1d4ec691ef3c Reviewed-on: https://webrtc-review.googlesource.com/15160 Commit-Queue: Steve Anton Reviewed-by: Peter Thatcher Reviewed-by: Zhi Huang Cr-Commit-Position: refs/heads/master@{#20574} --- pc/BUILD.gn | 3 - pc/peerconnection.cc | 2495 ++++++++++++++++++++++++++++-- pc/peerconnection.h | 490 +++++- pc/rtcstatscollector_unittest.cc | 175 ++- pc/statscollector.cc | 30 +- pc/statscollector_unittest.cc | 209 ++- pc/test/mock_peerconnection.h | 28 +- pc/test/mock_webrtcsession.h | 66 - pc/webrtcsession.cc | 2388 ---------------------------- pc/webrtcsession.h | 527 ------- 10 files changed, 3026 insertions(+), 3385 deletions(-) delete mode 100644 pc/test/mock_webrtcsession.h delete mode 100644 pc/webrtcsession.cc delete mode 100644 pc/webrtcsession.h diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 450e6e0d35..8e0db29b74 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -154,8 +154,6 @@ rtc_static_library("peerconnection") { "videotracksource.h", "webrtcsdp.cc", "webrtcsdp.h", - "webrtcsession.cc", - "webrtcsession.h", "webrtcsessiondescriptionfactory.cc", "webrtcsessiondescriptionfactory.h", ] @@ -315,7 +313,6 @@ if (rtc_include_tests) { "test/fakevideotracksource.h", "test/mock_datachannel.h", "test/mock_peerconnection.h", - "test/mock_webrtcsession.h", "test/mockpeerconnectionobservers.h", "test/peerconnectiontestwrapper.cc", "test/peerconnectiontestwrapper.h", diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 3889222e03..8e3672994d 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -11,6 +11,7 @@ #include "pc/peerconnection.h" #include +#include #include #include @@ -24,6 +25,7 @@ #include "logging/rtc_event_log/rtc_event_log.h" #include "media/sctp/sctptransport.h" #include "pc/audiotrack.h" +#include "pc/channel.h" #include "pc/channelmanager.h" #include "pc/dtmfsender.h" #include "pc/mediastream.h" @@ -31,6 +33,7 @@ #include "pc/remoteaudiosource.h" #include "pc/rtpreceiver.h" #include "pc/rtpsender.h" +#include "pc/sctputils.h" #include "pc/streamcollection.h" #include "pc/videocapturertracksource.h" #include "pc/videotrack.h" @@ -45,21 +48,47 @@ #include "system_wrappers/include/clock.h" #include "system_wrappers/include/field_trial.h" +using cricket::ContentInfo; +using cricket::ContentInfos; +using cricket::MediaContentDescription; +using cricket::SessionDescription; +using cricket::TransportInfo; + +using cricket::LOCAL_PORT_TYPE; +using cricket::STUN_PORT_TYPE; +using cricket::RELAY_PORT_TYPE; +using cricket::PRFLX_PORT_TYPE; + namespace webrtc { -namespace { +// Error messages +const char kBundleWithoutRtcpMux[] = + "rtcp-mux must be enabled when BUNDLE " + "is enabled."; +const char kCreateChannelFailed[] = "Failed to create channels."; +const char kInvalidCandidates[] = "Description contains invalid candidates."; +const char kInvalidSdp[] = "Invalid session description."; +const char kMlineMismatchInAnswer[] = + "The order of m-lines in answer doesn't match order in offer. Rejecting " + "answer."; +const char kMlineMismatchInSubsequentOffer[] = + "The order of m-lines in subsequent offer doesn't match order from " + "previous offer/answer."; +const char kPushDownTDFailed[] = "Failed to push down transport description:"; +const char kSdpWithoutDtlsFingerprint[] = + "Called with SDP without DTLS fingerprint."; +const char kSdpWithoutSdesCrypto[] = "Called with SDP without SDES crypto."; +const char kSdpWithoutIceUfragPwd[] = + "Called with SDP without ice-ufrag and ice-pwd."; +const char kSessionError[] = "Session error code: "; +const char kSessionErrorDesc[] = "Session error description: "; +const char kDtlsSrtpSetupFailureRtp[] = + "Couldn't set up DTLS-SRTP on RTP channel."; +const char kDtlsSrtpSetupFailureRtcp[] = + "Couldn't set up DTLS-SRTP on RTCP channel."; +const char kEnableBundleFailed[] = "Failed to enable BUNDLE."; -using webrtc::DataChannel; -using webrtc::MediaConstraintsInterface; -using webrtc::MediaStreamInterface; -using webrtc::PeerConnectionInterface; -using webrtc::RTCError; -using webrtc::RTCErrorType; -using webrtc::RtpSenderInternal; -using webrtc::RtpSenderInterface; -using webrtc::RtpSenderProxy; -using webrtc::RtpSenderProxyWithInternal; -using webrtc::StreamCollection; +namespace { static const char kDefaultStreamLabel[] = "default"; static const char kDefaultAudioTrackLabel[] = "defaulta0"; @@ -199,7 +228,7 @@ uint32_t ConvertIceTransportTypeToCandidateFilter( } // Helper method to set a voice/video channel on all applicable senders -// and receivers when one is created/destroyed by WebRtcSession. +// and receivers when one is created/destroyed by PeerConnection. // // Used by On(Voice|Video)Channel(Created|Destroyed) template contents().size() > new_desc->contents().size()) { + return false; + } + + for (size_t i = 0; i < existing_desc->contents().size(); ++i) { + if (new_desc->contents()[i].name != existing_desc->contents()[i].name) { + return false; + } + const MediaContentDescription* new_desc_mdesc = + static_cast( + new_desc->contents()[i].description); + const MediaContentDescription* existing_desc_mdesc = + static_cast( + existing_desc->contents()[i].description); + if (new_desc_mdesc->type() != existing_desc_mdesc->type()) { + return false; + } + } + return true; +} + +bool MediaSectionsHaveSameCount(const SessionDescription* desc1, + const SessionDescription* desc2) { + if (!desc1 || !desc2) { + return false; + } + return desc1->contents().size() == desc2->contents().size(); +} + +// Checks that each non-rejected content has SDES crypto keys or a DTLS +// fingerprint, unless it's in a BUNDLE group, in which case only the +// BUNDLE-tag section (first media section/description in the BUNDLE group) +// needs a ufrag and pwd. Mismatches, such as replying with a DTLS fingerprint +// to SDES keys, will be caught in JsepTransport negotiation, and backstopped +// by Channel's |srtp_required| check. +bool VerifyCrypto(const SessionDescription* desc, + bool dtls_enabled, + std::string* error) { + const cricket::ContentGroup* bundle = + desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + const ContentInfos& contents = desc->contents(); + for (size_t index = 0; index < contents.size(); ++index) { + const ContentInfo* cinfo = &contents[index]; + if (cinfo->rejected) { + continue; + } + if (bundle && bundle->HasContentName(cinfo->name) && + cinfo->name != *(bundle->FirstContentName())) { + // This isn't the first media section in the BUNDLE group, so it's not + // required to have crypto attributes, since only the crypto attributes + // from the first section actually get used. + continue; + } + + // If the content isn't rejected or bundled into another m= section, crypto + // must be present. + const MediaContentDescription* media = + static_cast(cinfo->description); + const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); + if (!media || !tinfo) { + // Something is not right. + LOG(LS_ERROR) << kInvalidSdp; + *error = kInvalidSdp; + return false; + } + if (dtls_enabled) { + if (!tinfo->description.identity_fingerprint) { + LOG(LS_WARNING) << "Session description must have DTLS fingerprint if " + "DTLS enabled."; + *error = kSdpWithoutDtlsFingerprint; + return false; + } + } else { + if (media->cryptos().empty()) { + LOG(LS_WARNING) + << "Session description must have SDES when DTLS disabled."; + *error = kSdpWithoutSdesCrypto; + return false; + } + } + } + + return true; +} + +// Checks that each non-rejected content has ice-ufrag and ice-pwd set, unless +// it's in a BUNDLE group, in which case only the BUNDLE-tag section (first +// media section/description in the BUNDLE group) needs a ufrag and pwd. +bool VerifyIceUfragPwdPresent(const SessionDescription* desc) { + const cricket::ContentGroup* bundle = + desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + const ContentInfos& contents = desc->contents(); + for (size_t index = 0; index < contents.size(); ++index) { + const ContentInfo* cinfo = &contents[index]; + if (cinfo->rejected) { + continue; + } + if (bundle && bundle->HasContentName(cinfo->name) && + cinfo->name != *(bundle->FirstContentName())) { + // This isn't the first media section in the BUNDLE group, so it's not + // required to have ufrag/password, since only the ufrag/password from + // the first section actually get used. + continue; + } + + // If the content isn't rejected or bundled into another m= section, + // ice-ufrag and ice-pwd must be present. + const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); + if (!tinfo) { + // Something is not right. + LOG(LS_ERROR) << kInvalidSdp; + return false; + } + if (tinfo->description.ice_ufrag.empty() || + tinfo->description.ice_pwd.empty()) { + LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; + return false; + } + } + return true; +} + +bool GetTrackIdBySsrc(const SessionDescription* session_description, + uint32_t ssrc, + std::string* track_id) { + RTC_DCHECK(track_id != NULL); + + const cricket::ContentInfo* audio_info = + cricket::GetFirstAudioContent(session_description); + if (audio_info) { + const cricket::MediaContentDescription* audio_content = + static_cast( + audio_info->description); + + const auto* found = + cricket::GetStreamBySsrc(audio_content->streams(), ssrc); + if (found) { + *track_id = found->id; + return true; + } + } + + const cricket::ContentInfo* video_info = + cricket::GetFirstVideoContent(session_description); + if (video_info) { + const cricket::MediaContentDescription* video_content = + static_cast( + video_info->description); + + const auto* found = + cricket::GetStreamBySsrc(video_content->streams(), ssrc); + if (found) { + *track_id = found->id; + return true; + } + } + return false; +} + +// Get the SCTP port out of a SessionDescription. +// Return -1 if not found. +int GetSctpPort(const SessionDescription* session_description) { + const ContentInfo* content_info = GetFirstDataContent(session_description); + RTC_DCHECK(content_info); + if (!content_info) { + return -1; + } + const cricket::DataContentDescription* data = + static_cast( + (content_info->description)); + std::string value; + cricket::DataCodec match_pattern(cricket::kGoogleSctpDataCodecPlType, + cricket::kGoogleSctpDataCodecName); + for (const cricket::DataCodec& codec : data->codecs()) { + if (!codec.Matches(match_pattern)) { + continue; + } + if (codec.GetParam(cricket::kCodecParamPort, &value)) { + return rtc::FromString(value); + } + } + return -1; +} + +bool BadSdp(const std::string& source, + const std::string& type, + const std::string& reason, + std::string* err_desc) { + std::ostringstream desc; + desc << "Failed to set " << source; + if (!type.empty()) { + desc << " " << type; + } + desc << " sdp: " << reason; + + if (err_desc) { + *err_desc = desc.str(); + } + LOG(LS_ERROR) << desc.str(); + return false; +} + +bool BadSdp(cricket::ContentSource source, + const std::string& type, + const std::string& reason, + std::string* err_desc) { + if (source == cricket::CS_LOCAL) { + return BadSdp("local", type, reason, err_desc); + } else { + return BadSdp("remote", type, reason, err_desc); + } +} + +bool BadLocalSdp(const std::string& type, + const std::string& reason, + std::string* err_desc) { + return BadSdp(cricket::CS_LOCAL, type, reason, err_desc); +} + +bool BadRemoteSdp(const std::string& type, + const std::string& reason, + std::string* err_desc) { + return BadSdp(cricket::CS_REMOTE, type, reason, err_desc); +} + +bool BadOfferSdp(cricket::ContentSource source, + const std::string& reason, + std::string* err_desc) { + return BadSdp(source, SessionDescriptionInterface::kOffer, reason, err_desc); +} + +bool BadPranswerSdp(cricket::ContentSource source, + const std::string& reason, + std::string* err_desc) { + return BadSdp(source, SessionDescriptionInterface::kPrAnswer, reason, + err_desc); +} + +bool BadAnswerSdp(cricket::ContentSource source, + const std::string& reason, + std::string* err_desc) { + return BadSdp(source, SessionDescriptionInterface::kAnswer, reason, err_desc); +} + +std::string BadStateErrMsg(PeerConnectionInterface::SignalingState state) { + std::ostringstream desc; + desc << "Called in wrong state: " << GetSignalingStateString(state); + return desc.str(); +} + +#define GET_STRING_OF_ERROR_CODE(err) \ + case webrtc::PeerConnection::err: \ + result = #err; \ + break; + +std::string GetErrorCodeString(webrtc::PeerConnection::Error err) { + std::string result; + switch (err) { + GET_STRING_OF_ERROR_CODE(ERROR_NONE) + GET_STRING_OF_ERROR_CODE(ERROR_CONTENT) + GET_STRING_OF_ERROR_CODE(ERROR_TRANSPORT) + default: + RTC_NOTREACHED(); + break; + } + return result; +} + +std::string MakeErrorString(const std::string& error, const std::string& desc) { + std::ostringstream ret; + ret << error << " " << desc; + return ret.str(); +} + +std::string MakeTdErrorString(const std::string& desc) { + return MakeErrorString(kPushDownTDFailed, desc); +} + +// Returns true if |new_desc| requests an ICE restart (i.e., new ufrag/pwd). +bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc, + const SessionDescriptionInterface* new_desc, + const std::string& content_name) { + if (!old_desc) { + return false; + } + const SessionDescription* new_sd = new_desc->description(); + const SessionDescription* old_sd = old_desc->description(); + const ContentInfo* cinfo = new_sd->GetContentByName(content_name); + if (!cinfo || cinfo->rejected) { + return false; + } + // If the content isn't rejected, check if ufrag and password has changed. + const cricket::TransportDescription* new_transport_desc = + new_sd->GetTransportDescriptionByName(content_name); + const cricket::TransportDescription* old_transport_desc = + old_sd->GetTransportDescriptionByName(content_name); + if (!new_transport_desc || !old_transport_desc) { + // No transport description exists. This is not an ICE restart. + return false; + } + if (cricket::IceCredentialsChanged( + old_transport_desc->ice_ufrag, old_transport_desc->ice_pwd, + new_transport_desc->ice_ufrag, new_transport_desc->ice_pwd)) { + LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name + << "."; + return true; + } + return false; +} + +} // namespace + bool PeerConnectionInterface::RTCConfiguration::operator==( const PeerConnectionInterface::RTCConfiguration& o) const { // This static_assert prevents us from accidentally breaking operator==. @@ -422,7 +827,7 @@ PeerConnection::PeerConnection(PeerConnectionFactory* factory, PeerConnection::~PeerConnection() { TRACE_EVENT0("webrtc", "PeerConnection::~PeerConnection"); RTC_DCHECK(signaling_thread()->IsCurrent()); - // Need to detach RTP senders/receivers from WebRtcSession, + // Need to detach RTP senders/receivers from PeerConnection, // since it's about to be destroyed. for (const auto& sender : senders_) { sender->internal()->Stop(); @@ -436,11 +841,37 @@ PeerConnection::~PeerConnection() { stats_collector_->WaitForPendingRequest(); stats_collector_ = nullptr; } - // Now destroy session_ before destroying other members, - // because its destruction fires signals (such as VoiceChannelDestroyed) - // which will trigger some final actions in PeerConnection... - owned_session_.reset(nullptr); - session_ = nullptr; + + // Destroy video channels first since they may have a pointer to a voice + // channel. + for (auto* channel : video_channels_) { + DestroyVideoChannel(channel); + } + for (auto* channel : voice_channels_) { + DestroyVoiceChannel(channel); + } + if (rtp_data_channel_) { + DestroyDataChannel(); + } + + // Note: Cannot use rtc::Bind to create a functor to invoke because it will + // grab a reference to this PeerConnection. The RefCountedObject vtable will + // have already been destroyed (since it is a subclass of PeerConnection) and + // using rtc::Bind will cause "Pure virtual function called" error to appear. + + if (sctp_transport_) { + OnDataChannelDestroyed(); + network_thread()->Invoke(RTC_FROM_HERE, + [this] { DestroySctpTransport_n(); }); + } + + LOG(LS_INFO) << "Session: " << session_id() << " is destroyed."; + + webrtc_session_desc_factory_.reset(); + sctp_invoker_.reset(); + sctp_factory_.reset(); + transport_controller_.reset(); + // port_allocator_ lives on the network thread and should be destroyed there. network_thread()->Invoke(RTC_FROM_HERE, [this] { port_allocator_.reset(); }); @@ -487,25 +918,104 @@ bool PeerConnection::Initialize( return false; } - owned_session_.reset( - new WebRtcSession(this, - std::unique_ptr( - factory_->CreateTransportController( - port_allocator_.get(), - configuration.redetermine_role_on_ice_restart)), - factory_->CreateSctpTransportInternalFactory())); - session_ = owned_session_.get(); + // RFC 3264: The numeric value of the session id and version in the + // o line MUST be representable with a "64 bit signed integer". + // Due to this constraint session id |session_id_| is max limited to + // LLONG_MAX. + session_id_ = rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX); + transport_controller_.reset(factory_->CreateTransportController( + port_allocator_.get(), configuration.redetermine_role_on_ice_restart)); + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED); + transport_controller_->SignalConnectionState.connect( + this, &PeerConnection::OnTransportControllerConnectionState); + transport_controller_->SignalGatheringState.connect( + this, &PeerConnection::OnTransportControllerGatheringState); + transport_controller_->SignalCandidatesGathered.connect( + this, &PeerConnection::OnTransportControllerCandidatesGathered); + transport_controller_->SignalCandidatesRemoved.connect( + this, &PeerConnection::OnTransportControllerCandidatesRemoved); + transport_controller_->SignalDtlsHandshakeError.connect( + this, &PeerConnection::OnTransportControllerDtlsHandshakeError); + + sctp_factory_ = factory_->CreateSctpTransportInternalFactory(); stats_.reset(new StatsCollector(this)); stats_collector_ = RTCStatsCollector::Create(this); - // Need to set configuration before initializing WebRtcSession because it will - // reach back to fetch the media config. configuration_ = configuration; - // Initialize the WebRtcSession. It creates transport channels etc. - session_->Initialize(factory_->options(), std::move(cert_generator), - configuration, this); + const PeerConnectionFactoryInterface::Options& options = factory_->options(); + + transport_controller_->SetSslMaxProtocolVersion(options.ssl_max_version); + + // Obtain a certificate from RTCConfiguration if any were provided (optional). + rtc::scoped_refptr certificate; + if (!configuration.certificates.empty()) { + // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of + // just picking the first one. The decision should be made based on the DTLS + // handshake. The DTLS negotiations need to know about all certificates. + certificate = configuration.certificates[0]; + } + + SetIceConfig(ParseIceConfig(configuration)); + + if (options.disable_encryption) { + dtls_enabled_ = false; + } else { + // Enable DTLS by default if we have an identity store or a certificate. + dtls_enabled_ = (cert_generator || certificate); + // |configuration| can override the default |dtls_enabled_| value. + if (configuration.enable_dtls_srtp) { + dtls_enabled_ = *(configuration.enable_dtls_srtp); + } + } + + // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. + // It takes precendence over the disable_sctp_data_channels + // PeerConnectionFactoryInterface::Options. + if (configuration.enable_rtp_data_channel) { + data_channel_type_ = cricket::DCT_RTP; + } else { + // DTLS has to be enabled to use SCTP. + if (!options.disable_sctp_data_channels && dtls_enabled_) { + data_channel_type_ = cricket::DCT_SCTP; + } + } + + video_options_.screencast_min_bitrate_kbps = + configuration.screencast_min_bitrate; + audio_options_.combined_audio_video_bwe = + configuration.combined_audio_video_bwe; + + audio_options_.audio_jitter_buffer_max_packets = + rtc::Optional(configuration.audio_jitter_buffer_max_packets); + + audio_options_.audio_jitter_buffer_fast_accelerate = + rtc::Optional(configuration.audio_jitter_buffer_fast_accelerate); + + // Whether the certificate generator/certificate is null or not determines + // what PeerConnectionDescriptionFactory will do, so make sure that we give it + // the right instructions by clearing the variables if needed. + if (!dtls_enabled_) { + cert_generator.reset(); + certificate = nullptr; + } else if (certificate) { + // Favor generated certificate over the certificate generator. + cert_generator.reset(); + } + + webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( + signaling_thread(), channel_manager(), this, session_id(), + std::move(cert_generator), certificate)); + webrtc_session_desc_factory_->SignalCertificateReady.connect( + this, &PeerConnection::OnCertificateReady); + + if (options.disable_encryption) { + webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); + } + + webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( + options.crypto_options.enable_encrypted_rtp_header_extensions); return true; } @@ -614,7 +1124,7 @@ rtc::scoped_refptr PeerConnection::AddTrack( new_sender = RtpSenderProxyWithInternal::Create( signaling_thread(), new AudioRtpSender(static_cast(track), - session_->voice_channel(), stats_.get())); + voice_channel(), stats_.get())); if (!streams.empty()) { new_sender->internal()->set_stream_id(streams[0]->label()); } @@ -627,7 +1137,7 @@ rtc::scoped_refptr PeerConnection::AddTrack( new_sender = RtpSenderProxyWithInternal::Create( signaling_thread(), new VideoRtpSender(static_cast(track), - session_->video_channel())); + video_channel())); if (!streams.empty()) { new_sender->internal()->set_stream_id(streams[0]->label()); } @@ -693,11 +1203,10 @@ rtc::scoped_refptr PeerConnection::CreateSender( rtc::scoped_refptr> new_sender; if (kind == MediaStreamTrackInterface::kAudioKind) { new_sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), - new AudioRtpSender(session_->voice_channel(), stats_.get())); + signaling_thread(), new AudioRtpSender(voice_channel(), stats_.get())); } else if (kind == MediaStreamTrackInterface::kVideoKind) { new_sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), new VideoRtpSender(session_->video_channel())); + signaling_thread(), new VideoRtpSender(video_channel())); } else { LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind; return new_sender; @@ -789,7 +1298,7 @@ PeerConnection::CreateDataChannel( // Trigger the onRenegotiationNeeded event for every new RTP DataChannel, or // the first SCTP DataChannel. - if (session_->data_channel_type() == cricket::DCT_RTP || first_datachannel) { + if (data_channel_type() == cricket::DCT_RTP || first_datachannel) { observer_->OnRenegotiationNeeded(); } @@ -838,7 +1347,7 @@ void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer, cricket::MediaSessionOptions session_options; GetOptionsForOffer(options, &session_options); - session_->CreateOffer(observer, options, session_options); + CreateOffer(observer, options, session_options); } void PeerConnection::CreateAnswer( @@ -878,9 +1387,8 @@ void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer, return; } - if (session_->remote_description() && - session_->remote_description()->type() != - SessionDescriptionInterface::kOffer) { + if (remote_description() && + remote_description()->type() != SessionDescriptionInterface::kOffer) { std::string error = "CreateAnswer called without remote offer."; LOG(LS_ERROR) << error; PostCreateSessionDescriptionFailure(observer, error); @@ -890,7 +1398,7 @@ void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer, cricket::MediaSessionOptions session_options; GetOptionsForAnswer(options, &session_options); - session_->CreateAnswer(observer, session_options); + CreateAnswer(observer, session_options); } void PeerConnection::SetLocalDescription( @@ -921,7 +1429,7 @@ void PeerConnection::SetLocalDescription( // streams that might be removed by updating the session description. stats_->UpdateStats(kStatsOutputLevelStandard); std::string error; - if (!session_->SetLocalDescription(std::move(desc_temp), &error)) { + if (!SetLocalDescription(std::move(desc_temp), &error)) { PostSetSessionDescriptionFailure(observer, error); return; } @@ -929,8 +1437,7 @@ void PeerConnection::SetLocalDescription( // If setting the description decided our SSL role, allocate any necessary // SCTP sids. rtc::SSLRole role; - if (session_->data_channel_type() == cricket::DCT_SCTP && - session_->GetSctpSslRole(&role)) { + if (data_channel_type() == cricket::DCT_SCTP && GetSctpSslRole(&role)) { AllocateSctpSids(role); } @@ -986,7 +1493,7 @@ void PeerConnection::SetLocalDescription( // MaybeStartGathering needs to be called after posting // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates // before signaling that SetLocalDescription completed. - session_->MaybeStartGathering(); + MaybeStartGathering(); if (desc->type() == SessionDescriptionInterface::kAnswer) { // TODO(deadbeef): We already had to hop to the network thread for @@ -1026,7 +1533,7 @@ void PeerConnection::SetRemoteDescription( // streams that might be removed by updating the session description. stats_->UpdateStats(kStatsOutputLevelStandard); std::string error; - if (!session_->SetRemoteDescription(std::move(desc_temp), &error)) { + if (!SetRemoteDescription(std::move(desc_temp), &error)) { PostSetSessionDescriptionFailure(observer, error); return; } @@ -1034,8 +1541,7 @@ void PeerConnection::SetRemoteDescription( // If setting the description decided our SSL role, allocate any necessary // SCTP sids. rtc::SSLRole role; - if (session_->data_channel_type() == cricket::DCT_SCTP && - session_->GetSctpSslRole(&role)) { + if (data_channel_type() == cricket::DCT_SCTP && GetSctpSslRole(&role)) { AllocateSctpSids(role); } @@ -1064,11 +1570,9 @@ void PeerConnection::SetRemoteDescription( // TODO(steveanton): When removing RTP senders/receivers in response to a // rejected media section, there is some cleanup logic that expects the voice/ // video channel to still be set. But in this method the voice/video channel - // would have been destroyed by WebRtcSession's SetRemoteDescription method - // above, so the cleanup that relies on them fails to run. This is hard to fix - // with WebRtcSession and PeerConnection separated, but once the classes are - // merged it will be easy to call RemoveTracks right before destroying the - // voice/video channels. + // would have been destroyed by the SetRemoteDescription caller above so the + // cleanup that relies on them fails to run. The RemoveTracks calls should be + // moved to right before the DestroyChannel calls to fix this. // Find all audio rtp streams and create corresponding remote AudioTracks // and MediaStreams. @@ -1140,9 +1644,8 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, RTCError* error) { TRACE_EVENT0("webrtc", "PeerConnection::SetConfiguration"); - if (session_->local_description() && - configuration.ice_candidate_pool_size != - configuration_.ice_candidate_pool_size) { + if (local_description() && configuration.ice_candidate_pool_size != + configuration_.ice_candidate_pool_size) { LOG(LS_ERROR) << "Can't change candidate pool size after calling " "SetLocalDescription."; return SafeSetError(RTCErrorType::INVALID_MODIFICATION, error); @@ -1205,12 +1708,12 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, if (modified_config.servers != configuration_.servers || modified_config.type != configuration_.type || modified_config.prune_turn_ports != configuration_.prune_turn_ports) { - session_->SetNeedsIceRestartFlag(); + SetNeedsIceRestartFlag(); } if (modified_config.ice_check_min_interval != configuration_.ice_check_min_interval) { - session_->SetIceConfig(session_->ParseIceConfig(modified_config)); + SetIceConfig(ParseIceConfig(modified_config)); } configuration_ = modified_config; @@ -1223,21 +1726,21 @@ bool PeerConnection::AddIceCandidate( if (IsClosed()) { return false; } - return session_->ProcessIceMessage(ice_candidate); + return ProcessIceMessage(ice_candidate); } bool PeerConnection::RemoveIceCandidates( const std::vector& candidates) { TRACE_EVENT0("webrtc", "PeerConnection::RemoveIceCandidates"); - return session_->RemoveRemoteIceCandidates(candidates); + return RemoveRemoteIceCandidates(candidates); } void PeerConnection::RegisterUMAObserver(UMAObserver* observer) { TRACE_EVENT0("webrtc", "PeerConnection::RegisterUmaObserver"); uma_observer_ = observer; - if (session_) { - session_->transport_controller()->SetMetricsObserver(uma_observer_); + if (transport_controller()) { + transport_controller()->SetMetricsObserver(uma_observer_); } // Send information about IPv4/IPv6 status. @@ -1346,14 +1849,10 @@ void PeerConnection::SetAudioRecording(bool recording) { std::unique_ptr PeerConnection::GetRemoteAudioSSLCertificate() { - if (!session_) { + if (!voice_channel()) { return nullptr; } - auto* voice_channel = session_->voice_channel(); - if (!voice_channel) { - return nullptr; - } - return GetRemoteSSLCertificate(voice_channel->transport_name()); + return GetRemoteSSLCertificate(voice_channel()->transport_name()); } bool PeerConnection::StartRtcEventLog(rtc::PlatformFile file, @@ -1384,31 +1883,33 @@ void PeerConnection::StopRtcEventLog() { } const SessionDescriptionInterface* PeerConnection::local_description() const { - return session_->local_description(); + return pending_local_description_ ? pending_local_description_.get() + : current_local_description_.get(); } const SessionDescriptionInterface* PeerConnection::remote_description() const { - return session_->remote_description(); + return pending_remote_description_ ? pending_remote_description_.get() + : current_remote_description_.get(); } const SessionDescriptionInterface* PeerConnection::current_local_description() const { - return session_->current_local_description(); + return current_local_description_.get(); } const SessionDescriptionInterface* PeerConnection::current_remote_description() const { - return session_->current_remote_description(); + return current_remote_description_.get(); } const SessionDescriptionInterface* PeerConnection::pending_local_description() const { - return session_->pending_local_description(); + return pending_local_description_.get(); } const SessionDescriptionInterface* PeerConnection::pending_remote_description() const { - return session_->pending_remote_description(); + return pending_remote_description_.get(); } void PeerConnection::Close() { @@ -1417,7 +1918,13 @@ void PeerConnection::Close() { // streams before the channels are closed. stats_->UpdateStats(kStatsOutputLevelStandard); - session_->Close(); + ChangeSignalingState(PeerConnectionInterface::kClosed); + RemoveUnusedChannels(nullptr); + RTC_DCHECK(voice_channels_.empty()); + RTC_DCHECK(video_channels_.empty()); + RTC_DCHECK(!rtp_data_channel_); + RTC_DCHECK(!sctp_transport_); + network_thread()->Invoke( RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, @@ -1477,7 +1984,7 @@ void PeerConnection::CreateAudioReceiver(MediaStreamInterface* stream, rtc::scoped_refptr> receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), - new AudioRtpReceiver(track_id, ssrc, session_->voice_channel())); + new AudioRtpReceiver(track_id, ssrc, voice_channel())); stream->AddTrack( static_cast(receiver->internal()->track().get())); receivers_.push_back(receiver); @@ -1491,9 +1998,8 @@ void PeerConnection::CreateVideoReceiver(MediaStreamInterface* stream, uint32_t ssrc) { rtc::scoped_refptr> receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), - new VideoRtpReceiver(track_id, worker_thread(), ssrc, - session_->video_channel())); + signaling_thread(), new VideoRtpReceiver(track_id, worker_thread(), + ssrc, video_channel())); stream->AddTrack( static_cast(receiver->internal()->track().get())); receivers_.push_back(receiver); @@ -1533,8 +2039,8 @@ void PeerConnection::AddAudioTrack(AudioTrackInterface* track, rtc::scoped_refptr> new_sender = RtpSenderProxyWithInternal::Create( signaling_thread(), - new AudioRtpSender(track, {stream->label()}, - session_->voice_channel(), stats_.get())); + new AudioRtpSender(track, {stream->label()}, voice_channel(), + stats_.get())); senders_.push_back(new_sender); // If the sender has already been configured in SDP, we call SetSsrc, // which will connect the sender to the underlying transport. This can @@ -1578,8 +2084,8 @@ void PeerConnection::AddVideoTrack(VideoTrackInterface* track, // Normal case; we've never seen this track before. rtc::scoped_refptr> new_sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), new VideoRtpSender(track, {stream->label()}, - session_->video_channel())); + signaling_thread(), + new VideoRtpSender(track, {stream->label()}, video_channel())); senders_.push_back(new_sender); const TrackInfo* track_info = FindTrackInfo(local_video_tracks_, stream->label(), track->id()); @@ -1761,9 +2267,9 @@ void PeerConnection::GetOptionsForOffer( // If a current description exists, generate m= sections in the same order, // using the first audio/video/data section that appears and rejecting // extraneous ones. - if (session_->local_description()) { + if (local_description()) { GenerateMediaDescriptionOptions( - session_->local_description(), + local_description(), cricket::RtpTransceiverDirection(send_audio, recv_audio), cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index, &video_index, &data_index, session_options); @@ -1821,9 +2327,8 @@ void PeerConnection::GetOptionsForOffer( // negotiated by default and the unit tests in WebRtcDataBrowserTest will fail // when building with chromium. We want to leave RTP data channels broken, so // people won't try to use them. - if (!rtp_data_channels_.empty() || - session_->data_channel_type() != cricket::DCT_RTP) { - session_options->data_channel_type = session_->data_channel_type(); + if (!rtp_data_channels_.empty() || data_channel_type() != cricket::DCT_RTP) { + session_options->data_channel_type = data_channel_type(); } session_options->rtcp_cname = rtcp_cname_; @@ -1855,15 +2360,15 @@ void PeerConnection::GetOptionsForAnswer( rtc::Optional audio_index; rtc::Optional video_index; rtc::Optional data_index; - if (session_->remote_description()) { + if (remote_description()) { // The pending remote description should be an offer. - RTC_DCHECK(session_->remote_description()->type() == + RTC_DCHECK(remote_description()->type() == SessionDescriptionInterface::kOffer); // Generate m= sections that match those in the offer. // Note that mediasession.cc will handle intersection our preferred // direction with the offered direction. GenerateMediaDescriptionOptions( - session_->remote_description(), + remote_description(), cricket::RtpTransceiverDirection(send_audio, recv_audio), cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index, &video_index, &data_index, session_options); @@ -1893,9 +2398,8 @@ void PeerConnection::GetOptionsForAnswer( // the RTP data channels would be successfully negotiated by default and the // unit tests in WebRtcDataBrowserTest will fail when building with chromium. // We want to leave RTP data channels broken, so people won't try to use them. - if (!rtp_data_channels_.empty() || - session_->data_channel_type() != cricket::DCT_RTP) { - session_options->data_channel_type = session_->data_channel_type(); + if (!rtp_data_channels_.empty() || data_channel_type() != cricket::DCT_RTP) { + session_options->data_channel_type = data_channel_type(); } session_options->rtcp_cname = rtcp_cname_; @@ -2288,17 +2792,17 @@ rtc::scoped_refptr PeerConnection::InternalCreateDataChannel( if (IsClosed()) { return nullptr; } - if (session_->data_channel_type() == cricket::DCT_NONE) { + if (data_channel_type() == cricket::DCT_NONE) { LOG(LS_ERROR) << "InternalCreateDataChannel: Data is not supported in this call."; return nullptr; } InternalDataChannelInit new_config = config ? (*config) : InternalDataChannelInit(); - if (session_->data_channel_type() == cricket::DCT_SCTP) { + if (data_channel_type() == cricket::DCT_SCTP) { if (new_config.id < 0) { rtc::SSLRole role; - if ((session_->GetSctpSslRole(&role)) && + if ((GetSctpSslRole(&role)) && !sid_allocator_.AllocateSid(role, &new_config.id)) { LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel."; return nullptr; @@ -2310,8 +2814,8 @@ rtc::scoped_refptr PeerConnection::InternalCreateDataChannel( } } - rtc::scoped_refptr channel(DataChannel::Create( - session_, session_->data_channel_type(), label, new_config)); + rtc::scoped_refptr channel( + DataChannel::Create(this, data_channel_type(), label, new_config)); if (!channel) { sid_allocator_.ReleaseSid(new_config.id); return nullptr; @@ -2373,8 +2877,7 @@ void PeerConnection::OnSctpDataChannelClosed(DataChannel* channel) { void PeerConnection::OnVoiceChannelCreated() { SetChannelOnSendersAndReceivers( - session_->voice_channel(), senders_, receivers_, - cricket::MEDIA_TYPE_AUDIO); + voice_channel(), senders_, receivers_, cricket::MEDIA_TYPE_AUDIO); } void PeerConnection::OnVoiceChannelDestroyed() { @@ -2385,8 +2888,7 @@ void PeerConnection::OnVoiceChannelDestroyed() { void PeerConnection::OnVideoChannelCreated() { SetChannelOnSendersAndReceivers( - session_->video_channel(), senders_, receivers_, - cricket::MEDIA_TYPE_VIDEO); + video_channel(), senders_, receivers_, cricket::MEDIA_TYPE_VIDEO); } void PeerConnection::OnVideoChannelDestroyed() { @@ -2607,4 +3109,1783 @@ void PeerConnection::StopRtcEventLog_w() { } } +cricket::BaseChannel* PeerConnection::GetChannel( + const std::string& content_name) { + if (voice_channel() && voice_channel()->content_name() == content_name) { + return voice_channel(); + } + if (video_channel() && video_channel()->content_name() == content_name) { + return video_channel(); + } + if (rtp_data_channel() && + rtp_data_channel()->content_name() == content_name) { + return rtp_data_channel(); + } + return nullptr; +} + +bool PeerConnection::GetSctpSslRole(rtc::SSLRole* role) { + if (!local_description() || !remote_description()) { + LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " + << "SSL Role of the SCTP transport."; + return false; + } + if (!sctp_transport_) { + LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the " + << "SSL Role of the SCTP transport."; + return false; + } + + return transport_controller_->GetSslRole(*sctp_transport_name_, role); +} + +bool PeerConnection::GetSslRole(const std::string& content_name, + rtc::SSLRole* role) { + if (!local_description() || !remote_description()) { + LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " + << "SSL Role of the session."; + return false; + } + + return transport_controller_->GetSslRole(GetTransportName(content_name), + role); +} + +void PeerConnection::CreateOffer( + CreateSessionDescriptionObserver* observer, + const PeerConnectionInterface::RTCOfferAnswerOptions& options, + const cricket::MediaSessionOptions& session_options) { + webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); +} + +void PeerConnection::CreateAnswer( + CreateSessionDescriptionObserver* observer, + const cricket::MediaSessionOptions& session_options) { + webrtc_session_desc_factory_->CreateAnswer(observer, session_options); +} + +bool PeerConnection::SetLocalDescription( + std::unique_ptr desc, + std::string* err_desc) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + + // Validate SDP. + if (!ValidateSessionDescription(desc.get(), cricket::CS_LOCAL, err_desc)) { + return false; + } + + // Update the initial_offerer flag if this session is the initial_offerer. + Action action = GetAction(desc->type()); + if (!initial_offerer_.has_value()) { + initial_offerer_.emplace(action == kOffer); + if (*initial_offerer_) { + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + } else { + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED); + } + } + + if (action == kAnswer) { + current_local_description_ = std::move(desc); + pending_local_description_ = nullptr; + current_remote_description_ = std::move(pending_remote_description_); + } else { + pending_local_description_ = std::move(desc); + } + + // Transport and Media channels will be created only when offer is set. + if (action == kOffer && !CreateChannels(local_description()->description())) { + // TODO(mallinath) - Handle CreateChannel failure, as new local description + // is applied. Restore back to old description. + return BadLocalSdp(local_description()->type(), kCreateChannelFailed, + err_desc); + } + + // Remove unused channels if MediaContentDescription is rejected. + RemoveUnusedChannels(local_description()->description()); + + if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) { + return false; + } + if (remote_description()) { + // Now that we have a local description, we can push down remote candidates. + UseCandidatesInSessionDescription(remote_description()); + } + + pending_ice_restarts_.clear(); + if (error() != ERROR_NONE) { + return BadLocalSdp(local_description()->type(), GetSessionErrorMsg(), + err_desc); + } + return true; +} + +bool PeerConnection::SetRemoteDescription( + std::unique_ptr desc, + std::string* err_desc) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + + // Validate SDP. + if (!ValidateSessionDescription(desc.get(), cricket::CS_REMOTE, err_desc)) { + return false; + } + + // Hold this pointer so candidates can be copied to it later in the method. + SessionDescriptionInterface* desc_ptr = desc.get(); + + const SessionDescriptionInterface* old_remote_description = + remote_description(); + // Grab ownership of the description being replaced for the remainder of this + // method, since it's used below as |old_remote_description|. + std::unique_ptr replaced_remote_description; + Action action = GetAction(desc->type()); + if (action == kAnswer) { + replaced_remote_description = pending_remote_description_ + ? std::move(pending_remote_description_) + : std::move(current_remote_description_); + current_remote_description_ = std::move(desc); + pending_remote_description_ = nullptr; + current_local_description_ = std::move(pending_local_description_); + } else { + replaced_remote_description = std::move(pending_remote_description_); + pending_remote_description_ = std::move(desc); + } + + // Transport and Media channels will be created only when offer is set. + if (action == kOffer && + !CreateChannels(remote_description()->description())) { + // TODO(mallinath) - Handle CreateChannel failure, as new local description + // is applied. Restore back to old description. + return BadRemoteSdp(remote_description()->type(), kCreateChannelFailed, + err_desc); + } + + // Remove unused channels if MediaContentDescription is rejected. + RemoveUnusedChannels(remote_description()->description()); + + // NOTE: Candidates allocation will be initiated only when SetLocalDescription + // is called. + if (!UpdateSessionState(action, cricket::CS_REMOTE, err_desc)) { + return false; + } + + if (local_description() && + !UseCandidatesInSessionDescription(remote_description())) { + return BadRemoteSdp(remote_description()->type(), kInvalidCandidates, + err_desc); + } + + if (old_remote_description) { + for (const cricket::ContentInfo& content : + old_remote_description->description()->contents()) { + // Check if this new SessionDescription contains new ICE ufrag and + // password that indicates the remote peer requests an ICE restart. + // TODO(deadbeef): When we start storing both the current and pending + // remote description, this should reset pending_ice_restarts and compare + // against the current description. + if (CheckForRemoteIceRestart(old_remote_description, remote_description(), + content.name)) { + if (action == kOffer) { + pending_ice_restarts_.insert(content.name); + } + } else { + // We retain all received candidates only if ICE is not restarted. + // When ICE is restarted, all previous candidates belong to an old + // generation and should not be kept. + // TODO(deadbeef): This goes against the W3C spec which says the remote + // description should only contain candidates from the last set remote + // description plus any candidates added since then. We should remove + // this once we're sure it won't break anything. + WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( + old_remote_description, content.name, desc_ptr); + } + } + } + + if (error() != ERROR_NONE) { + return BadRemoteSdp(remote_description()->type(), GetSessionErrorMsg(), + err_desc); + } + + // Set the the ICE connection state to connecting since the connection may + // become writable with peer reflexive candidates before any remote candidate + // is signaled. + // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix + // is to have a new signal the indicates a change in checking state from the + // transport and expose a new checking() member from transport that can be + // read to determine the current checking state. The existing SignalConnecting + // actually means "gathering candidates", so cannot be be used here. + if (remote_description()->type() != SessionDescriptionInterface::kOffer && + ice_connection_state() == PeerConnectionInterface::kIceConnectionNew) { + SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); + } + return true; +} + +// TODO(steveanton): Eventually it'd be nice to store the channels as a single +// vector of BaseChannel pointers instead of separate voice and video channel +// vectors. At that point, this will become a simple getter. +std::vector PeerConnection::Channels() const { + std::vector channels; + channels.insert(channels.end(), voice_channels_.begin(), + voice_channels_.end()); + channels.insert(channels.end(), video_channels_.begin(), + video_channels_.end()); + if (rtp_data_channel_) { + channels.push_back(rtp_data_channel_); + } + return channels; +} + +void PeerConnection::SetError(Error error, const std::string& error_desc) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + if (error != error_) { + error_ = error; + error_desc_ = error_desc; + } +} + +bool PeerConnection::UpdateSessionState(Action action, + cricket::ContentSource source, + std::string* err_desc) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + + // If there's already a pending error then no state transition should happen. + // But all call-sites should be verifying this before calling us! + RTC_DCHECK(error() == ERROR_NONE); + std::string td_err; + if (action == kOffer) { + if (!PushdownTransportDescription(source, cricket::CA_OFFER, &td_err)) { + return BadOfferSdp(source, MakeTdErrorString(td_err), err_desc); + } + ChangeSignalingState(source == cricket::CS_LOCAL + ? PeerConnectionInterface::kHaveLocalOffer + : PeerConnectionInterface::kHaveRemoteOffer); + if (!PushdownMediaDescription(cricket::CA_OFFER, source, err_desc)) { + SetError(ERROR_CONTENT, *err_desc); + } + if (error() != ERROR_NONE) { + return BadOfferSdp(source, GetSessionErrorMsg(), err_desc); + } + } else if (action == kPrAnswer) { + if (!PushdownTransportDescription(source, cricket::CA_PRANSWER, &td_err)) { + return BadPranswerSdp(source, MakeTdErrorString(td_err), err_desc); + } + EnableChannels(); + ChangeSignalingState(source == cricket::CS_LOCAL + ? PeerConnectionInterface::kHaveLocalPrAnswer + : PeerConnectionInterface::kHaveRemotePrAnswer); + if (!PushdownMediaDescription(cricket::CA_PRANSWER, source, err_desc)) { + SetError(ERROR_CONTENT, *err_desc); + } + if (error() != ERROR_NONE) { + return BadPranswerSdp(source, GetSessionErrorMsg(), err_desc); + } + } else if (action == kAnswer) { + const cricket::ContentGroup* local_bundle = + local_description()->description()->GetGroupByName( + cricket::GROUP_TYPE_BUNDLE); + const cricket::ContentGroup* remote_bundle = + remote_description()->description()->GetGroupByName( + cricket::GROUP_TYPE_BUNDLE); + if (local_bundle && remote_bundle) { + // The answerer decides the transport to bundle on. + const cricket::ContentGroup* answer_bundle = + (source == cricket::CS_LOCAL ? local_bundle : remote_bundle); + if (!EnableBundle(*answer_bundle)) { + LOG(LS_WARNING) << "Failed to enable BUNDLE."; + return BadAnswerSdp(source, kEnableBundleFailed, err_desc); + } + } + // Only push down the transport description after enabling BUNDLE; we don't + // want to push down a description on a transport about to be destroyed. + if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) { + return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc); + } + EnableChannels(); + ChangeSignalingState(PeerConnectionInterface::kStable); + if (!PushdownMediaDescription(cricket::CA_ANSWER, source, err_desc)) { + SetError(ERROR_CONTENT, *err_desc); + } + if (error() != ERROR_NONE) { + return BadAnswerSdp(source, GetSessionErrorMsg(), err_desc); + } + } + return true; +} + +PeerConnection::Action PeerConnection::GetAction(const std::string& type) { + if (type == SessionDescriptionInterface::kOffer) { + return PeerConnection::kOffer; + } else if (type == SessionDescriptionInterface::kPrAnswer) { + return PeerConnection::kPrAnswer; + } else if (type == SessionDescriptionInterface::kAnswer) { + return PeerConnection::kAnswer; + } + RTC_NOTREACHED() << "unknown action type"; + return PeerConnection::kOffer; +} + +bool PeerConnection::PushdownMediaDescription(cricket::ContentAction action, + cricket::ContentSource source, + std::string* err) { + const SessionDescription* sdesc = + (source == cricket::CS_LOCAL ? local_description() : remote_description()) + ->description(); + RTC_DCHECK(sdesc); + bool all_success = true; + for (auto* channel : Channels()) { + // TODO(steveanton): Add support for multiple channels of the same type. + const ContentInfo* content_info = + cricket::GetFirstMediaContent(sdesc->contents(), channel->media_type()); + if (!content_info) { + continue; + } + const MediaContentDescription* content_desc = + static_cast(content_info->description); + if (content_desc && !content_info->rejected) { + bool success = (source == cricket::CS_LOCAL) + ? channel->SetLocalContent(content_desc, action, err) + : channel->SetRemoteContent(content_desc, action, err); + if (!success) { + all_success = false; + break; + } + } + } + // Need complete offer/answer with an SCTP m= section before starting SCTP, + // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19 + if (sctp_transport_ && local_description() && remote_description() && + cricket::GetFirstDataContent(local_description()->description()) && + cricket::GetFirstDataContent(remote_description()->description())) { + all_success &= network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&PeerConnection::PushdownSctpParameters_n, this, source)); + } + return all_success; +} + +bool PeerConnection::PushdownSctpParameters_n(cricket::ContentSource source) { + RTC_DCHECK(network_thread()->IsCurrent()); + RTC_DCHECK(local_description()); + RTC_DCHECK(remote_description()); + // Apply the SCTP port (which is hidden inside a DataCodec structure...) + // When we support "max-message-size", that would also be pushed down here. + return sctp_transport_->Start( + GetSctpPort(local_description()->description()), + GetSctpPort(remote_description()->description())); +} + +bool PeerConnection::PushdownTransportDescription(cricket::ContentSource source, + cricket::ContentAction action, + std::string* error_desc) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + + if (source == cricket::CS_LOCAL) { + return PushdownLocalTransportDescription(local_description()->description(), + action, error_desc); + } + return PushdownRemoteTransportDescription(remote_description()->description(), + action, error_desc); +} + +bool PeerConnection::PushdownLocalTransportDescription( + const SessionDescription* sdesc, + cricket::ContentAction action, + std::string* err) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + + if (!sdesc) { + return false; + } + + for (const TransportInfo& tinfo : sdesc->transport_infos()) { + if (!transport_controller_->SetLocalTransportDescription( + tinfo.content_name, tinfo.description, action, err)) { + return false; + } + } + + return true; +} + +bool PeerConnection::PushdownRemoteTransportDescription( + const SessionDescription* sdesc, + cricket::ContentAction action, + std::string* err) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + + if (!sdesc) { + return false; + } + + for (const TransportInfo& tinfo : sdesc->transport_infos()) { + if (!transport_controller_->SetRemoteTransportDescription( + tinfo.content_name, tinfo.description, action, err)) { + return false; + } + } + + return true; +} + +bool PeerConnection::GetTransportDescription( + const SessionDescription* description, + const std::string& content_name, + cricket::TransportDescription* tdesc) { + if (!description || !tdesc) { + return false; + } + const TransportInfo* transport_info = + description->GetTransportInfoByName(content_name); + if (!transport_info) { + return false; + } + *tdesc = transport_info->description; + return true; +} + +bool PeerConnection::EnableBundle(const cricket::ContentGroup& bundle) { + const std::string* first_content_name = bundle.FirstContentName(); + if (!first_content_name) { + LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; + return false; + } + const std::string& transport_name = *first_content_name; + + auto maybe_set_transport = [this, bundle, + transport_name](cricket::BaseChannel* ch) { + if (!ch || !bundle.HasContentName(ch->content_name())) { + return true; + } + + std::string old_transport_name = ch->transport_name(); + if (old_transport_name == transport_name) { + LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name() + << " on " << transport_name << "."; + return true; + } + + cricket::DtlsTransportInternal* rtp_dtls_transport = + transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + bool need_rtcp = (ch->rtcp_dtls_transport() != nullptr); + cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; + if (need_rtcp) { + rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + + ch->SetTransports(rtp_dtls_transport, rtcp_dtls_transport); + LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on " + << transport_name << "."; + transport_controller_->DestroyDtlsTransport( + old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + // If the channel needs rtcp, it means that the channel used to have a + // rtcp transport which needs to be deleted now. + if (need_rtcp) { + transport_controller_->DestroyDtlsTransport( + old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + return true; + }; + + if (!maybe_set_transport(voice_channel()) || + !maybe_set_transport(video_channel()) || + !maybe_set_transport(rtp_data_channel())) { + return false; + } + // For SCTP, transport creation/deletion happens here instead of in the + // object itself. + if (sctp_transport_) { + RTC_DCHECK(sctp_transport_name_); + RTC_DCHECK(sctp_content_name_); + if (transport_name != *sctp_transport_name_ && + bundle.HasContentName(*sctp_content_name_)) { + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&PeerConnection::ChangeSctpTransport_n, this, + transport_name)); + } + } + + return true; +} + +bool PeerConnection::ProcessIceMessage(const IceCandidateInterface* candidate) { + if (!remote_description()) { + LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added " + << "without any remote session description."; + return false; + } + + if (!candidate) { + LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL."; + return false; + } + + bool valid = false; + bool ready = ReadyToUseRemoteCandidate(candidate, NULL, &valid); + if (!valid) { + return false; + } + + // Add this candidate to the remote session description. + if (!mutable_remote_description()->AddCandidate(candidate)) { + LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used."; + return false; + } + + if (ready) { + return UseCandidate(candidate); + } else { + LOG(LS_INFO) << "ProcessIceMessage: Not ready to use candidate."; + return true; + } +} + +bool PeerConnection::RemoveRemoteIceCandidates( + const std::vector& candidates) { + if (!remote_description()) { + LOG(LS_ERROR) << "RemoveRemoteIceCandidates: ICE candidates can't be " + << "removed without any remote session description."; + return false; + } + + if (candidates.empty()) { + LOG(LS_ERROR) << "RemoveRemoteIceCandidates: candidates are empty."; + return false; + } + + size_t number_removed = + mutable_remote_description()->RemoveCandidates(candidates); + if (number_removed != candidates.size()) { + LOG(LS_ERROR) << "RemoveRemoteIceCandidates: Failed to remove candidates. " + << "Requested " << candidates.size() << " but only " + << number_removed << " are removed."; + } + + // Remove the candidates from the transport controller. + std::string error; + bool res = transport_controller_->RemoveRemoteCandidates(candidates, &error); + if (!res && !error.empty()) { + LOG(LS_ERROR) << "Error when removing remote candidates: " << error; + } + return true; +} + +cricket::IceConfig PeerConnection::ParseIceConfig( + const PeerConnectionInterface::RTCConfiguration& config) const { + cricket::ContinualGatheringPolicy gathering_policy; + // TODO(honghaiz): Add the third continual gathering policy in + // PeerConnectionInterface and map it to GATHER_CONTINUALLY_AND_RECOVER. + switch (config.continual_gathering_policy) { + case PeerConnectionInterface::GATHER_ONCE: + gathering_policy = cricket::GATHER_ONCE; + break; + case PeerConnectionInterface::GATHER_CONTINUALLY: + gathering_policy = cricket::GATHER_CONTINUALLY; + break; + default: + RTC_NOTREACHED(); + gathering_policy = cricket::GATHER_ONCE; + } + cricket::IceConfig ice_config; + ice_config.receiving_timeout = config.ice_connection_receiving_timeout; + ice_config.prioritize_most_likely_candidate_pairs = + config.prioritize_most_likely_ice_candidate_pairs; + ice_config.backup_connection_ping_interval = + config.ice_backup_candidate_pair_ping_interval; + ice_config.continual_gathering_policy = gathering_policy; + ice_config.presume_writable_when_fully_relayed = + config.presume_writable_when_fully_relayed; + ice_config.ice_check_min_interval = config.ice_check_min_interval; + ice_config.regather_all_networks_interval_range = + config.ice_regather_interval_range; + return ice_config; +} + +void PeerConnection::SetIceConfig(const cricket::IceConfig& config) { + transport_controller_->SetIceConfig(config); +} + +void PeerConnection::MaybeStartGathering() { + transport_controller_->MaybeStartGathering(); +} + +bool PeerConnection::GetLocalTrackIdBySsrc(uint32_t ssrc, + std::string* track_id) { + if (!local_description()) { + return false; + } + return webrtc::GetTrackIdBySsrc(local_description()->description(), ssrc, + track_id); +} + +bool PeerConnection::GetRemoteTrackIdBySsrc(uint32_t ssrc, + std::string* track_id) { + if (!remote_description()) { + return false; + } + return webrtc::GetTrackIdBySsrc(remote_description()->description(), ssrc, + track_id); +} + +bool PeerConnection::SendData(const cricket::SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + cricket::SendDataResult* result) { + if (!rtp_data_channel_ && !sctp_transport_) { + LOG(LS_ERROR) << "SendData called when rtp_data_channel_ " + << "and sctp_transport_ are NULL."; + return false; + } + return rtp_data_channel_ + ? rtp_data_channel_->SendData(params, payload, result) + : network_thread()->Invoke( + RTC_FROM_HERE, + Bind(&cricket::SctpTransportInternal::SendData, + sctp_transport_.get(), params, payload, result)); +} + +bool PeerConnection::ConnectDataChannel(DataChannel* webrtc_data_channel) { + if (!rtp_data_channel_ && !sctp_transport_) { + // Don't log an error here, because DataChannels are expected to call + // ConnectDataChannel in this state. It's the only way to initially tell + // whether or not the underlying transport is ready. + return false; + } + if (rtp_data_channel_) { + rtp_data_channel_->SignalReadyToSendData.connect( + webrtc_data_channel, &DataChannel::OnChannelReady); + rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel, + &DataChannel::OnDataReceived); + } else { + SignalSctpReadyToSendData.connect(webrtc_data_channel, + &DataChannel::OnChannelReady); + SignalSctpDataReceived.connect(webrtc_data_channel, + &DataChannel::OnDataReceived); + SignalSctpStreamClosedRemotely.connect( + webrtc_data_channel, &DataChannel::OnStreamClosedRemotely); + } + return true; +} + +void PeerConnection::DisconnectDataChannel(DataChannel* webrtc_data_channel) { + if (!rtp_data_channel_ && !sctp_transport_) { + LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and " + "sctp_transport_ are NULL."; + return; + } + if (rtp_data_channel_) { + rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel); + rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel); + } else { + SignalSctpReadyToSendData.disconnect(webrtc_data_channel); + SignalSctpDataReceived.disconnect(webrtc_data_channel); + SignalSctpStreamClosedRemotely.disconnect(webrtc_data_channel); + } +} + +void PeerConnection::AddSctpDataStream(int sid) { + if (!sctp_transport_) { + LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL."; + return; + } + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream, + sctp_transport_.get(), sid)); +} + +void PeerConnection::RemoveSctpDataStream(int sid) { + if (!sctp_transport_) { + LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is " + << "NULL."; + return; + } + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream, + sctp_transport_.get(), sid)); +} + +bool PeerConnection::ReadyToSendData() const { + return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) || + sctp_ready_to_send_data_; +} + +std::unique_ptr PeerConnection::GetSessionStats_s() { + RTC_DCHECK(signaling_thread()->IsCurrent()); + ChannelNamePairs channel_name_pairs; + if (voice_channel()) { + channel_name_pairs.voice = rtc::Optional(ChannelNamePair( + voice_channel()->content_name(), voice_channel()->transport_name())); + } + if (video_channel()) { + channel_name_pairs.video = rtc::Optional(ChannelNamePair( + video_channel()->content_name(), video_channel()->transport_name())); + } + if (rtp_data_channel()) { + channel_name_pairs.data = rtc::Optional( + ChannelNamePair(rtp_data_channel()->content_name(), + rtp_data_channel()->transport_name())); + } + if (sctp_transport_) { + RTC_DCHECK(sctp_content_name_); + RTC_DCHECK(sctp_transport_name_); + channel_name_pairs.data = rtc::Optional( + ChannelNamePair(*sctp_content_name_, *sctp_transport_name_)); + } + return GetSessionStats(channel_name_pairs); +} + +std::unique_ptr PeerConnection::GetSessionStats( + const ChannelNamePairs& channel_name_pairs) { + if (network_thread()->IsCurrent()) { + return GetSessionStats_n(channel_name_pairs); + } + return network_thread()->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&PeerConnection::GetSessionStats_n, this, channel_name_pairs)); +} + +bool PeerConnection::GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + return transport_controller_->GetLocalCertificate(transport_name, + certificate); +} + +std::unique_ptr PeerConnection::GetRemoteSSLCertificate( + const std::string& transport_name) { + return transport_controller_->GetRemoteSSLCertificate(transport_name); +} + +cricket::DataChannelType PeerConnection::data_channel_type() const { + return data_channel_type_; +} + +bool PeerConnection::IceRestartPending(const std::string& content_name) const { + return pending_ice_restarts_.find(content_name) != + pending_ice_restarts_.end(); +} + +void PeerConnection::SetNeedsIceRestartFlag() { + transport_controller_->SetNeedsIceRestartFlag(); +} + +bool PeerConnection::NeedsIceRestart(const std::string& content_name) const { + return transport_controller_->NeedsIceRestart(content_name); +} + +void PeerConnection::OnCertificateReady( + const rtc::scoped_refptr& certificate) { + transport_controller_->SetLocalCertificate(certificate); +} + +void PeerConnection::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) { + SetError(ERROR_TRANSPORT, + rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); +} + +void PeerConnection::OnTransportControllerConnectionState( + cricket::IceConnectionState state) { + switch (state) { + case cricket::kIceConnectionConnecting: + // If the current state is Connected or Completed, then there were + // writable channels but now there are not, so the next state must + // be Disconnected. + // kIceConnectionConnecting is currently used as the default, + // un-connected state by the TransportController, so its only use is + // detecting disconnections. + if (ice_connection_state_ == + PeerConnectionInterface::kIceConnectionConnected || + ice_connection_state_ == + PeerConnectionInterface::kIceConnectionCompleted) { + SetIceConnectionState( + PeerConnectionInterface::kIceConnectionDisconnected); + } + break; + case cricket::kIceConnectionFailed: + SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed); + break; + case cricket::kIceConnectionConnected: + LOG(LS_INFO) << "Changing to ICE connected state because " + << "all transports are writable."; + SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); + break; + case cricket::kIceConnectionCompleted: + LOG(LS_INFO) << "Changing to ICE completed state because " + << "all transports are complete."; + if (ice_connection_state_ != + PeerConnectionInterface::kIceConnectionConnected) { + // If jumping directly from "checking" to "connected", + // signal "connected" first. + SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); + } + SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); + if (metrics_observer()) { + ReportTransportStats(); + } + break; + default: + RTC_NOTREACHED(); + } +} + +void PeerConnection::OnTransportControllerCandidatesGathered( + const std::string& transport_name, + const cricket::Candidates& candidates) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + int sdp_mline_index; + if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { + LOG(LS_ERROR) << "OnTransportControllerCandidatesGathered: content name " + << transport_name << " not found"; + return; + } + + for (cricket::Candidates::const_iterator citer = candidates.begin(); + citer != candidates.end(); ++citer) { + // Use transport_name as the candidate media id. + std::unique_ptr candidate( + new JsepIceCandidate(transport_name, sdp_mline_index, *citer)); + if (local_description()) { + mutable_local_description()->AddCandidate(candidate.get()); + } + OnIceCandidate(std::move(candidate)); + } +} + +void PeerConnection::OnTransportControllerCandidatesRemoved( + const std::vector& candidates) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + // Sanity check. + for (const cricket::Candidate& candidate : candidates) { + if (candidate.transport_name().empty()) { + LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " + << "empty content name in candidate " + << candidate.ToString(); + return; + } + } + + if (local_description()) { + mutable_local_description()->RemoveCandidates(candidates); + } + OnIceCandidatesRemoved(candidates); +} + +void PeerConnection::OnTransportControllerDtlsHandshakeError( + rtc::SSLHandshakeError error) { + if (metrics_observer()) { + metrics_observer()->IncrementEnumCounter( + webrtc::kEnumCounterDtlsHandshakeError, static_cast(error), + static_cast(rtc::SSLHandshakeError::MAX_VALUE)); + } +} + +// Enabling voice and video (and RTP data) channels. +void PeerConnection::EnableChannels() { + for (cricket::VoiceChannel* voice_channel : voice_channels_) { + if (!voice_channel->enabled()) { + voice_channel->Enable(true); + } + } + + for (cricket::VideoChannel* video_channel : video_channels_) { + if (!video_channel->enabled()) { + video_channel->Enable(true); + } + } + + if (rtp_data_channel_ && !rtp_data_channel_->enabled()) + rtp_data_channel_->Enable(true); +} + +// Returns the media index for a local ice candidate given the content name. +bool PeerConnection::GetLocalCandidateMediaIndex( + const std::string& content_name, + int* sdp_mline_index) { + if (!local_description() || !sdp_mline_index) { + return false; + } + + bool content_found = false; + const ContentInfos& contents = local_description()->description()->contents(); + for (size_t index = 0; index < contents.size(); ++index) { + if (contents[index].name == content_name) { + *sdp_mline_index = static_cast(index); + content_found = true; + break; + } + } + return content_found; +} + +bool PeerConnection::UseCandidatesInSessionDescription( + const SessionDescriptionInterface* remote_desc) { + if (!remote_desc) { + return true; + } + bool ret = true; + + for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) { + const IceCandidateCollection* candidates = remote_desc->candidates(m); + for (size_t n = 0; n < candidates->count(); ++n) { + const IceCandidateInterface* candidate = candidates->at(n); + bool valid = false; + if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) { + if (valid) { + LOG(LS_INFO) << "UseCandidatesInSessionDescription: Not ready to use " + << "candidate."; + } + continue; + } + ret = UseCandidate(candidate); + if (!ret) { + break; + } + } + } + return ret; +} + +bool PeerConnection::UseCandidate(const IceCandidateInterface* candidate) { + size_t mediacontent_index = static_cast(candidate->sdp_mline_index()); + size_t remote_content_size = + remote_description()->description()->contents().size(); + if (mediacontent_index >= remote_content_size) { + LOG(LS_ERROR) << "UseCandidate: Invalid candidate media index."; + return false; + } + + cricket::ContentInfo content = + remote_description()->description()->contents()[mediacontent_index]; + std::vector candidates; + candidates.push_back(candidate->candidate()); + // Invoking BaseSession method to handle remote candidates. + std::string error; + if (transport_controller_->AddRemoteCandidates(content.name, candidates, + &error)) { + // Candidates successfully submitted for checking. + if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew || + ice_connection_state_ == + PeerConnectionInterface::kIceConnectionDisconnected) { + // If state is New, then the session has just gotten its first remote ICE + // candidates, so go to Checking. + // If state is Disconnected, the session is re-using old candidates or + // receiving additional ones, so go to Checking. + // If state is Connected, stay Connected. + // TODO(bemasc): If state is Connected, and the new candidates are for a + // newly added transport, then the state actually _should_ move to + // checking. Add a way to distinguish that case. + SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); + } + // TODO(bemasc): If state is Completed, go back to Connected. + } else { + if (!error.empty()) { + LOG(LS_WARNING) << error; + } + } + return true; +} + +void PeerConnection::RemoveUnusedChannels(const SessionDescription* desc) { + // TODO(steveanton): Add support for multiple audio/video channels. + // Destroy video channel first since it may have a pointer to the + // voice channel. + const cricket::ContentInfo* video_info = cricket::GetFirstVideoContent(desc); + if ((!video_info || video_info->rejected) && video_channel()) { + RemoveAndDestroyVideoChannel(video_channel()); + } + + const cricket::ContentInfo* voice_info = cricket::GetFirstAudioContent(desc); + if ((!voice_info || voice_info->rejected) && voice_channel()) { + RemoveAndDestroyVoiceChannel(voice_channel()); + } + + const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc); + if (!data_info || data_info->rejected) { + if (rtp_data_channel_) { + DestroyDataChannel(); + } + if (sctp_transport_) { + OnDataChannelDestroyed(); + network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&PeerConnection::DestroySctpTransport_n, this)); + } + } +} + +// Returns the name of the transport channel when BUNDLE is enabled, or nullptr +// if the channel is not part of any bundle. +const std::string* PeerConnection::GetBundleTransportName( + const cricket::ContentInfo* content, + const cricket::ContentGroup* bundle) { + if (!bundle) { + return nullptr; + } + const std::string* first_content_name = bundle->FirstContentName(); + if (!first_content_name) { + LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; + return nullptr; + } + if (!bundle->HasContentName(content->name)) { + LOG(LS_WARNING) << content->name << " is not part of any bundle group"; + return nullptr; + } + LOG(LS_INFO) << "Bundling " << content->name << " on " << *first_content_name; + return first_content_name; +} + +bool PeerConnection::CreateChannels(const SessionDescription* desc) { + // TODO(steveanton): Add support for multiple audio/video channels. + const cricket::ContentGroup* bundle_group = nullptr; + if (configuration_.bundle_policy == + PeerConnectionInterface::kBundlePolicyMaxBundle) { + bundle_group = desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + if (!bundle_group) { + LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified"; + return false; + } + } + // Creating the media channels and transport proxies. + const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc); + if (voice && !voice->rejected && !voice_channel()) { + if (!CreateVoiceChannel(voice, + GetBundleTransportName(voice, bundle_group))) { + LOG(LS_ERROR) << "Failed to create voice channel."; + return false; + } + } + + const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc); + if (video && !video->rejected && !video_channel()) { + if (!CreateVideoChannel(video, + GetBundleTransportName(video, bundle_group))) { + LOG(LS_ERROR) << "Failed to create video channel."; + return false; + } + } + + const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc); + if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected && + !rtp_data_channel_ && !sctp_transport_) { + if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) { + LOG(LS_ERROR) << "Failed to create data channel."; + return false; + } + } + + return true; +} + +bool PeerConnection::CreateVoiceChannel(const cricket::ContentInfo* content, + const std::string* bundle_transport) { + // TODO(steveanton): Check to see if it's safe to create multiple voice + // channels. + RTC_DCHECK(voice_channels_.empty()); + + std::string transport_name = + bundle_transport ? *bundle_transport : content->name; + + cricket::DtlsTransportInternal* rtp_dtls_transport = + transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; + if (configuration_.rtcp_mux_policy != + PeerConnectionInterface::kRtcpMuxPolicyRequire) { + rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + + cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel( + call_.get(), configuration_.media_config, rtp_dtls_transport, + rtcp_dtls_transport, transport_controller_->signaling_thread(), + content->name, SrtpRequired(), audio_options_); + if (!voice_channel) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + if (rtcp_dtls_transport) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + return false; + } + + voice_channels_.push_back(voice_channel); + + voice_channel->SignalRtcpMuxFullyActive.connect( + this, &PeerConnection::DestroyRtcpTransport_n); + voice_channel->SignalDtlsSrtpSetupFailure.connect( + this, &PeerConnection::OnDtlsSrtpSetupFailure); + + // TODO(steveanton): This should signal which voice channel was created since + // we can have multiple. + OnVoiceChannelCreated(); + voice_channel->SignalSentPacket.connect(this, + &PeerConnection::OnSentPacket_w); + return true; +} + +bool PeerConnection::CreateVideoChannel(const cricket::ContentInfo* content, + const std::string* bundle_transport) { + // TODO(steveanton): Check to see if it's safe to create multiple video + // channels. + RTC_DCHECK(video_channels_.empty()); + + std::string transport_name = + bundle_transport ? *bundle_transport : content->name; + + cricket::DtlsTransportInternal* rtp_dtls_transport = + transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; + if (configuration_.rtcp_mux_policy != + PeerConnectionInterface::kRtcpMuxPolicyRequire) { + rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + + cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel( + call_.get(), configuration_.media_config, rtp_dtls_transport, + rtcp_dtls_transport, transport_controller_->signaling_thread(), + content->name, SrtpRequired(), video_options_); + + if (!video_channel) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + if (rtcp_dtls_transport) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + return false; + } + + video_channels_.push_back(video_channel); + + video_channel->SignalRtcpMuxFullyActive.connect( + this, &PeerConnection::DestroyRtcpTransport_n); + video_channel->SignalDtlsSrtpSetupFailure.connect( + this, &PeerConnection::OnDtlsSrtpSetupFailure); + + // TODO(steveanton): This should signal which video channel was created since + // we can have multiple. + OnVideoChannelCreated(); + video_channel->SignalSentPacket.connect(this, + &PeerConnection::OnSentPacket_w); + return true; +} + +bool PeerConnection::CreateDataChannel(const cricket::ContentInfo* content, + const std::string* bundle_transport) { + const std::string transport_name = + bundle_transport ? *bundle_transport : content->name; + bool sctp = (data_channel_type_ == cricket::DCT_SCTP); + if (sctp) { + if (!sctp_factory_) { + LOG(LS_ERROR) + << "Trying to create SCTP transport, but didn't compile with " + "SCTP support (HAVE_SCTP)"; + return false; + } + if (!network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&PeerConnection::CreateSctpTransport_n, + this, content->name, transport_name))) { + return false; + } + } else { + std::string transport_name = + bundle_transport ? *bundle_transport : content->name; + cricket::DtlsTransportInternal* rtp_dtls_transport = + transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; + if (configuration_.rtcp_mux_policy != + PeerConnectionInterface::kRtcpMuxPolicyRequire) { + rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + + rtp_data_channel_ = channel_manager()->CreateRtpDataChannel( + configuration_.media_config, rtp_dtls_transport, rtcp_dtls_transport, + transport_controller_->signaling_thread(), content->name, + SrtpRequired()); + + if (!rtp_data_channel_) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + if (rtcp_dtls_transport) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } + return false; + } + + rtp_data_channel_->SignalRtcpMuxFullyActive.connect( + this, &PeerConnection::DestroyRtcpTransport_n); + rtp_data_channel_->SignalDtlsSrtpSetupFailure.connect( + this, &PeerConnection::OnDtlsSrtpSetupFailure); + rtp_data_channel_->SignalSentPacket.connect( + this, &PeerConnection::OnSentPacket_w); + } + + OnDataChannelCreated(); + + return true; +} + +Call::Stats PeerConnection::GetCallStats() { + if (!worker_thread()->IsCurrent()) { + return worker_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&PeerConnection::GetCallStats, this)); + } + if (call_) { + return call_->GetStats(); + } else { + return Call::Stats(); + } +} + +std::unique_ptr PeerConnection::GetSessionStats_n( + const ChannelNamePairs& channel_name_pairs) { + RTC_DCHECK(network_thread()->IsCurrent()); + std::unique_ptr session_stats(new SessionStats()); + for (const auto channel_name_pair : + {&channel_name_pairs.voice, &channel_name_pairs.video, + &channel_name_pairs.data}) { + if (*channel_name_pair) { + cricket::TransportStats transport_stats; + if (!transport_controller_->GetStats((*channel_name_pair)->transport_name, + &transport_stats)) { + return nullptr; + } + session_stats->proxy_to_transport[(*channel_name_pair)->content_name] = + (*channel_name_pair)->transport_name; + session_stats->transport_stats[(*channel_name_pair)->transport_name] = + std::move(transport_stats); + } + } + return session_stats; +} + +bool PeerConnection::CreateSctpTransport_n(const std::string& content_name, + const std::string& transport_name) { + RTC_DCHECK(network_thread()->IsCurrent()); + RTC_DCHECK(sctp_factory_); + cricket::DtlsTransportInternal* tc = + transport_controller_->CreateDtlsTransport_n( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + sctp_transport_ = sctp_factory_->CreateSctpTransport(tc); + RTC_DCHECK(sctp_transport_); + sctp_invoker_.reset(new rtc::AsyncInvoker()); + sctp_transport_->SignalReadyToSendData.connect( + this, &PeerConnection::OnSctpTransportReadyToSendData_n); + sctp_transport_->SignalDataReceived.connect( + this, &PeerConnection::OnSctpTransportDataReceived_n); + sctp_transport_->SignalStreamClosedRemotely.connect( + this, &PeerConnection::OnSctpStreamClosedRemotely_n); + sctp_transport_name_ = rtc::Optional(transport_name); + sctp_content_name_ = rtc::Optional(content_name); + return true; +} + +void PeerConnection::ChangeSctpTransport_n(const std::string& transport_name) { + RTC_DCHECK(network_thread()->IsCurrent()); + RTC_DCHECK(sctp_transport_); + RTC_DCHECK(sctp_transport_name_); + std::string old_sctp_transport_name = *sctp_transport_name_; + sctp_transport_name_ = rtc::Optional(transport_name); + cricket::DtlsTransportInternal* tc = + transport_controller_->CreateDtlsTransport_n( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + sctp_transport_->SetTransportChannel(tc); + transport_controller_->DestroyDtlsTransport_n( + old_sctp_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); +} + +void PeerConnection::DestroySctpTransport_n() { + RTC_DCHECK(network_thread()->IsCurrent()); + sctp_transport_.reset(nullptr); + sctp_content_name_.reset(); + sctp_transport_name_.reset(); + sctp_invoker_.reset(nullptr); + sctp_ready_to_send_data_ = false; +} + +void PeerConnection::OnSctpTransportReadyToSendData_n() { + RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); + RTC_DCHECK(network_thread()->IsCurrent()); + // Note: Cannot use rtc::Bind here because it will grab a reference to + // PeerConnection and potentially cause PeerConnection to live longer than + // expected. It is safe not to grab a reference since the sctp_invoker_ will + // be destroyed before PeerConnection is destroyed, and at that point all + // pending tasks will be cleared. + sctp_invoker_->AsyncInvoke(RTC_FROM_HERE, signaling_thread(), [this] { + OnSctpTransportReadyToSendData_s(true); + }); +} + +void PeerConnection::OnSctpTransportReadyToSendData_s(bool ready) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + sctp_ready_to_send_data_ = ready; + SignalSctpReadyToSendData(ready); +} + +void PeerConnection::OnSctpTransportDataReceived_n( + const cricket::ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& payload) { + RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); + RTC_DCHECK(network_thread()->IsCurrent()); + // Note: Cannot use rtc::Bind here because it will grab a reference to + // PeerConnection and potentially cause PeerConnection to live longer than + // expected. It is safe not to grab a reference since the sctp_invoker_ will + // be destroyed before PeerConnection is destroyed, and at that point all + // pending tasks will be cleared. + sctp_invoker_->AsyncInvoke( + RTC_FROM_HERE, signaling_thread(), [this, params, payload] { + OnSctpTransportDataReceived_s(params, payload); + }); +} + +void PeerConnection::OnSctpTransportDataReceived_s( + const cricket::ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& payload) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + if (params.type == cricket::DMT_CONTROL && IsOpenMessage(payload)) { + // Received OPEN message; parse and signal that a new data channel should + // be created. + std::string label; + InternalDataChannelInit config; + config.id = params.ssrc; + if (!ParseDataChannelOpenMessage(payload, &label, &config)) { + LOG(LS_WARNING) << "Failed to parse the OPEN message for sid " + << params.ssrc; + return; + } + config.open_handshake_role = InternalDataChannelInit::kAcker; + OnDataChannelOpenMessage(label, config); + } else { + // Otherwise just forward the signal. + SignalSctpDataReceived(params, payload); + } +} + +void PeerConnection::OnSctpStreamClosedRemotely_n(int sid) { + RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); + RTC_DCHECK(network_thread()->IsCurrent()); + sctp_invoker_->AsyncInvoke( + RTC_FROM_HERE, signaling_thread(), + rtc::Bind(&sigslot::signal1::operator(), + &SignalSctpStreamClosedRemotely, sid)); +} + +// Returns false if bundle is enabled and rtcp_mux is disabled. +bool PeerConnection::ValidateBundleSettings(const SessionDescription* desc) { + bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE); + if (!bundle_enabled) + return true; + + const cricket::ContentGroup* bundle_group = + desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + RTC_DCHECK(bundle_group != NULL); + + const cricket::ContentInfos& contents = desc->contents(); + for (cricket::ContentInfos::const_iterator citer = contents.begin(); + citer != contents.end(); ++citer) { + const cricket::ContentInfo* content = (&*citer); + RTC_DCHECK(content != NULL); + if (bundle_group->HasContentName(content->name) && !content->rejected && + content->type == cricket::NS_JINGLE_RTP) { + if (!HasRtcpMuxEnabled(content)) + return false; + } + } + // RTCP-MUX is enabled in all the contents. + return true; +} + +bool PeerConnection::HasRtcpMuxEnabled(const cricket::ContentInfo* content) { + const cricket::MediaContentDescription* description = + static_cast(content->description); + return description->rtcp_mux(); +} + +bool PeerConnection::ValidateSessionDescription( + const SessionDescriptionInterface* sdesc, + cricket::ContentSource source, + std::string* err_desc) { + std::string type; + if (error() != ERROR_NONE) { + return BadSdp(source, type, GetSessionErrorMsg(), err_desc); + } + + if (!sdesc || !sdesc->description()) { + return BadSdp(source, type, kInvalidSdp, err_desc); + } + + type = sdesc->type(); + Action action = GetAction(sdesc->type()); + if (source == cricket::CS_LOCAL) { + if (!ExpectSetLocalDescription(action)) + return BadLocalSdp(type, BadStateErrMsg(signaling_state()), err_desc); + } else { + if (!ExpectSetRemoteDescription(action)) + return BadRemoteSdp(type, BadStateErrMsg(signaling_state()), err_desc); + } + + // Verify crypto settings. + std::string crypto_error; + if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || + dtls_enabled_) && + !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) { + return BadSdp(source, type, crypto_error, err_desc); + } + + // Verify ice-ufrag and ice-pwd. + if (!VerifyIceUfragPwdPresent(sdesc->description())) { + return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc); + } + + if (!ValidateBundleSettings(sdesc->description())) { + return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc); + } + + // TODO(skvlad): When the local rtcp-mux policy is Require, reject any + // m-lines that do not rtcp-mux enabled. + + // Verify m-lines in Answer when compared against Offer. + if (action == kAnswer || action == kPrAnswer) { + const cricket::SessionDescription* offer_desc = + (source == cricket::CS_LOCAL) ? remote_description()->description() + : local_description()->description(); + if (!MediaSectionsHaveSameCount(offer_desc, sdesc->description()) || + !MediaSectionsInSameOrder(offer_desc, sdesc->description())) { + return BadAnswerSdp(source, kMlineMismatchInAnswer, err_desc); + } + } else { + const cricket::SessionDescription* current_desc = nullptr; + if (source == cricket::CS_LOCAL && local_description()) { + current_desc = local_description()->description(); + } else if (source == cricket::CS_REMOTE && remote_description()) { + current_desc = remote_description()->description(); + } + // The re-offers should respect the order of m= sections in current + // description. See RFC3264 Section 8 paragraph 4 for more details. + if (current_desc && + !MediaSectionsInSameOrder(current_desc, sdesc->description())) { + return BadOfferSdp(source, kMlineMismatchInSubsequentOffer, err_desc); + } + } + + return true; +} + +bool PeerConnection::ExpectSetLocalDescription(Action action) { + PeerConnectionInterface::SignalingState state = signaling_state(); + if (action == kOffer) { + return (state == PeerConnectionInterface::kStable) || + (state == PeerConnectionInterface::kHaveLocalOffer); + } else { // Answer or PrAnswer + return (state == PeerConnectionInterface::kHaveRemoteOffer) || + (state == PeerConnectionInterface::kHaveLocalPrAnswer); + } +} + +bool PeerConnection::ExpectSetRemoteDescription(Action action) { + PeerConnectionInterface::SignalingState state = signaling_state(); + if (action == kOffer) { + return (state == PeerConnectionInterface::kStable) || + (state == PeerConnectionInterface::kHaveRemoteOffer); + } else { // Answer or PrAnswer. + return (state == PeerConnectionInterface::kHaveLocalOffer) || + (state == PeerConnectionInterface::kHaveRemotePrAnswer); + } +} + +std::string PeerConnection::GetSessionErrorMsg() { + std::ostringstream desc; + desc << kSessionError << GetErrorCodeString(error()) << ". "; + desc << kSessionErrorDesc << error_desc() << "."; + return desc.str(); +} + +// We need to check the local/remote description for the Transport instead of +// the session, because a new Transport added during renegotiation may have +// them unset while the session has them set from the previous negotiation. +// Not doing so may trigger the auto generation of transport description and +// mess up DTLS identity information, ICE credential, etc. +bool PeerConnection::ReadyToUseRemoteCandidate( + const IceCandidateInterface* candidate, + const SessionDescriptionInterface* remote_desc, + bool* valid) { + *valid = true; + + const SessionDescriptionInterface* current_remote_desc = + remote_desc ? remote_desc : remote_description(); + + if (!current_remote_desc) { + return false; + } + + size_t mediacontent_index = static_cast(candidate->sdp_mline_index()); + size_t remote_content_size = + current_remote_desc->description()->contents().size(); + if (mediacontent_index >= remote_content_size) { + LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate media index " + << mediacontent_index; + + *valid = false; + return false; + } + + cricket::ContentInfo content = + current_remote_desc->description()->contents()[mediacontent_index]; + + const std::string transport_name = GetTransportName(content.name); + if (transport_name.empty()) { + return false; + } + return transport_controller_->ReadyForRemoteCandidates(transport_name); +} + +bool PeerConnection::SrtpRequired() const { + return dtls_enabled_ || + webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED; +} + +void PeerConnection::OnTransportControllerGatheringState( + cricket::IceGatheringState state) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + if (state == cricket::kIceGatheringGathering) { + OnIceGatheringChange(PeerConnectionInterface::kIceGatheringGathering); + } else if (state == cricket::kIceGatheringComplete) { + OnIceGatheringChange(PeerConnectionInterface::kIceGatheringComplete); + } +} + +void PeerConnection::ReportTransportStats() { + // Use a set so we don't report the same stats twice if two channels share + // a transport. + std::set transport_names; + if (voice_channel()) { + transport_names.insert(voice_channel()->transport_name()); + } + if (video_channel()) { + transport_names.insert(video_channel()->transport_name()); + } + if (rtp_data_channel()) { + transport_names.insert(rtp_data_channel()->transport_name()); + } + if (sctp_transport_name_) { + transport_names.insert(*sctp_transport_name_); + } + for (const auto& name : transport_names) { + cricket::TransportStats stats; + if (transport_controller_->GetStats(name, &stats)) { + ReportBestConnectionState(stats); + ReportNegotiatedCiphers(stats); + } + } +} +// Walk through the ConnectionInfos to gather best connection usage +// for IPv4 and IPv6. +void PeerConnection::ReportBestConnectionState( + const cricket::TransportStats& stats) { + RTC_DCHECK(metrics_observer()); + for (cricket::TransportChannelStatsList::const_iterator it = + stats.channel_stats.begin(); + it != stats.channel_stats.end(); ++it) { + for (cricket::ConnectionInfos::const_iterator it_info = + it->connection_infos.begin(); + it_info != it->connection_infos.end(); ++it_info) { + if (!it_info->best_connection) { + continue; + } + + PeerConnectionEnumCounterType type = kPeerConnectionEnumCounterMax; + const cricket::Candidate& local = it_info->local_candidate; + const cricket::Candidate& remote = it_info->remote_candidate; + + // Increment the counter for IceCandidatePairType. + if (local.protocol() == cricket::TCP_PROTOCOL_NAME || + (local.type() == RELAY_PORT_TYPE && + local.relay_protocol() == cricket::TCP_PROTOCOL_NAME)) { + type = kEnumCounterIceCandidatePairTypeTcp; + } else if (local.protocol() == cricket::UDP_PROTOCOL_NAME) { + type = kEnumCounterIceCandidatePairTypeUdp; + } else { + RTC_CHECK(0); + } + metrics_observer()->IncrementEnumCounter( + type, GetIceCandidatePairCounter(local, remote), + kIceCandidatePairMax); + + // Increment the counter for IP type. + if (local.address().family() == AF_INET) { + metrics_observer()->IncrementEnumCounter( + kEnumCounterAddressFamily, kBestConnections_IPv4, + kPeerConnectionAddressFamilyCounter_Max); + + } else if (local.address().family() == AF_INET6) { + metrics_observer()->IncrementEnumCounter( + kEnumCounterAddressFamily, kBestConnections_IPv6, + kPeerConnectionAddressFamilyCounter_Max); + } else { + RTC_CHECK(0); + } + + return; + } + } +} + +void PeerConnection::ReportNegotiatedCiphers( + const cricket::TransportStats& stats) { + RTC_DCHECK(metrics_observer()); + if (!dtls_enabled_ || stats.channel_stats.empty()) { + return; + } + + int srtp_crypto_suite = stats.channel_stats[0].srtp_crypto_suite; + int ssl_cipher_suite = stats.channel_stats[0].ssl_cipher_suite; + if (srtp_crypto_suite == rtc::SRTP_INVALID_CRYPTO_SUITE && + ssl_cipher_suite == rtc::TLS_NULL_WITH_NULL_NULL) { + return; + } + + PeerConnectionEnumCounterType srtp_counter_type; + PeerConnectionEnumCounterType ssl_counter_type; + if (stats.transport_name == cricket::CN_AUDIO) { + srtp_counter_type = kEnumCounterAudioSrtpCipher; + ssl_counter_type = kEnumCounterAudioSslCipher; + } else if (stats.transport_name == cricket::CN_VIDEO) { + srtp_counter_type = kEnumCounterVideoSrtpCipher; + ssl_counter_type = kEnumCounterVideoSslCipher; + } else if (stats.transport_name == cricket::CN_DATA) { + srtp_counter_type = kEnumCounterDataSrtpCipher; + ssl_counter_type = kEnumCounterDataSslCipher; + } else { + RTC_NOTREACHED(); + return; + } + + if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE) { + metrics_observer()->IncrementSparseEnumCounter(srtp_counter_type, + srtp_crypto_suite); + } + if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL) { + metrics_observer()->IncrementSparseEnumCounter(ssl_counter_type, + ssl_cipher_suite); + } +} + +void PeerConnection::OnSentPacket_w(const rtc::SentPacket& sent_packet) { + RTC_DCHECK(worker_thread()->IsCurrent()); + RTC_DCHECK(call_); + call_->OnSentPacket(sent_packet); +} + +const std::string PeerConnection::GetTransportName( + const std::string& content_name) { + cricket::BaseChannel* channel = GetChannel(content_name); + if (!channel) { + if (sctp_transport_) { + RTC_DCHECK(sctp_content_name_); + RTC_DCHECK(sctp_transport_name_); + if (content_name == *sctp_content_name_) { + return *sctp_transport_name_; + } + } + // Return an empty string if failed to retrieve the transport name. + return ""; + } + return channel->transport_name(); +} + +void PeerConnection::DestroyRtcpTransport_n(const std::string& transport_name) { + RTC_DCHECK(network_thread()->IsCurrent()); + transport_controller_->DestroyDtlsTransport_n( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); +} + +void PeerConnection::RemoveAndDestroyVideoChannel( + cricket::VideoChannel* video_channel) { + auto it = + std::find(video_channels_.begin(), video_channels_.end(), video_channel); + RTC_DCHECK(it != video_channels_.end()); + if (it == video_channels_.end()) { + return; + } + video_channels_.erase(it); + DestroyVideoChannel(video_channel); +} + +void PeerConnection::DestroyVideoChannel(cricket::VideoChannel* video_channel) { + // TODO(steveanton): This should take an identifier for the video channel + // since we now support more than one. + OnVideoChannelDestroyed(); + RTC_DCHECK(video_channel->rtp_dtls_transport()); + const std::string transport_name = + video_channel->rtp_dtls_transport()->transport_name(); + const bool need_to_delete_rtcp = + (video_channel->rtcp_dtls_transport() != nullptr); + // The above need to be cached before destroying the video channel so that we + // do not access uninitialized memory. + channel_manager()->DestroyVideoChannel(video_channel); + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + if (need_to_delete_rtcp) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } +} + +void PeerConnection::RemoveAndDestroyVoiceChannel( + cricket::VoiceChannel* voice_channel) { + auto it = + std::find(voice_channels_.begin(), voice_channels_.end(), voice_channel); + RTC_DCHECK(it != voice_channels_.end()); + if (it == voice_channels_.end()) { + return; + } + voice_channels_.erase(it); + DestroyVoiceChannel(voice_channel); +} + +void PeerConnection::DestroyVoiceChannel(cricket::VoiceChannel* voice_channel) { + // TODO(steveanton): This should take an identifier for the voice channel + // since we now support more than one. + OnVoiceChannelDestroyed(); + RTC_DCHECK(voice_channel->rtp_dtls_transport()); + const std::string transport_name = + voice_channel->rtp_dtls_transport()->transport_name(); + const bool need_to_delete_rtcp = + (voice_channel->rtcp_dtls_transport() != nullptr); + // The above need to be cached before destroying the video channel so that we + // do not access uninitialized memory. + channel_manager()->DestroyVoiceChannel(voice_channel); + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + if (need_to_delete_rtcp) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } +} + +void PeerConnection::DestroyDataChannel() { + OnDataChannelDestroyed(); + RTC_DCHECK(rtp_data_channel_->rtp_dtls_transport()); + std::string transport_name; + transport_name = rtp_data_channel_->rtp_dtls_transport()->transport_name(); + bool need_to_delete_rtcp = + (rtp_data_channel_->rtcp_dtls_transport() != nullptr); + channel_manager()->DestroyRtpDataChannel(rtp_data_channel_); + rtp_data_channel_ = nullptr; + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + if (need_to_delete_rtcp) { + transport_controller_->DestroyDtlsTransport( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } +} + } // namespace webrtc diff --git a/pc/peerconnection.h b/pc/peerconnection.h index 2afb17c3ba..0c7962d7a5 100644 --- a/pc/peerconnection.h +++ b/pc/peerconnection.h @@ -11,9 +11,10 @@ #ifndef PC_PEERCONNECTION_H_ #define PC_PEERCONNECTION_H_ -#include #include #include +#include +#include #include #include "api/peerconnectioninterface.h" @@ -25,7 +26,7 @@ #include "pc/rtpsender.h" #include "pc/statscollector.h" #include "pc/streamcollection.h" -#include "pc/webrtcsession.h" +#include "pc/webrtcsessiondescriptionfactory.h" namespace webrtc { @@ -33,19 +34,47 @@ class MediaStreamObserver; class VideoRtpReceiver; class RtcEventLog; -// TODO(steveanton): Remove once WebRtcSession is merged into PeerConnection. -std::string GetSignalingStateString( - PeerConnectionInterface::SignalingState state); +// Statistics for all the transports of the session. +// TODO(pthatcher): Think of a better name for this. We already have +// a TransportStats in transport.h. Perhaps TransportsStats? +struct SessionStats { + std::map proxy_to_transport; + std::map transport_stats; +}; -// PeerConnection implements the PeerConnectionInterface interface. -// It uses WebRtcSession to implement the PeerConnection functionality. +struct ChannelNamePair { + ChannelNamePair(const std::string& content_name, + const std::string& transport_name) + : content_name(content_name), transport_name(transport_name) {} + std::string content_name; + std::string transport_name; +}; + +struct ChannelNamePairs { + rtc::Optional voice; + rtc::Optional video; + rtc::Optional data; +}; + +// PeerConnection is the implementation of the PeerConnection object as defined +// by the PeerConnectionInterface API surface. +// The class currently is solely responsible for the following: +// - Managing the session state machine (signaling state). +// - Creating and initializing lower-level objects, like PortAllocator and +// BaseChannels. +// - Owning and managing the life cycle of the RtpSender/RtpReceiver and track +// objects. +// - Tracking the current and pending local/remote session descriptions. +// The class currently is jointly responsible for the following: +// - Parsing and interpreting SDP. +// - Generating offers and answers based on the current state. +// - The ICE state machine. +// - Generating stats. class PeerConnection : public PeerConnectionInterface, + public DataChannelProviderInterface, public rtc::MessageHandler, public sigslot::has_slots<> { public: - // TODO(steveanton): Remove once WebRtcSession is merged into PeerConnection. - friend class WebRtcSession; - explicit PeerConnection(PeerConnectionFactory* factory, std::unique_ptr event_log, std::unique_ptr call); @@ -66,11 +95,6 @@ class PeerConnection : public PeerConnectionInterface, std::vector streams) override; bool RemoveTrack(RtpSenderInterface* sender) override; - // TODO(steveanton): Remove this once all clients have switched to using the - // PeerConnection shims for WebRtcSession methods instead of the methods - // directly via this getter. - virtual WebRtcSession* session() { return session_; } - // Gets the DTLS SSL certificate associated with the audio transport on the // remote side. This will become populated once the DTLS connection with the // peer has been completed, as indicated by the ICE connection state @@ -167,74 +191,87 @@ class PeerConnection : public PeerConnectionInterface, return sctp_data_channels_; } - // TODO(steveanton): These methods are temporarily added to facilitate work - // towards merging WebRtcSession into PeerConnection. To make this easier, we - // want only PeerConnection to interact with WebRtcSession so they can be - // merged easily. A few outside classes still access WebRtcSession methods - // directly, so these have been added to PeerConnection to remove the - // dependency from WebRtcSession. - rtc::Thread* network_thread() const { return factory_->network_thread(); } rtc::Thread* worker_thread() const { return factory_->worker_thread(); } rtc::Thread* signaling_thread() const { return factory_->signaling_thread(); } - virtual const std::string& session_id() const { - return session_->session_id(); - } - virtual bool session_created() const { return session_ != nullptr; } - virtual bool initial_offerer() const { return session_->initial_offerer(); } - virtual std::unique_ptr GetSessionStats_s() { - return session_->GetSessionStats_s(); - } + + // The SDP session ID as defined by RFC 3264. + virtual const std::string& session_id() const { return session_id_; } + + // Returns true if we were the initial offerer. + bool initial_offerer() const { return initial_offerer_ && *initial_offerer_; } + + // Returns stats for all channels of all transports. + // This avoids exposing the internal structures used to track them. + // The parameterless version creates |ChannelNamePairs| from |voice_channel|, + // |video_channel| and |voice_channel| if available - this requires it to be + // called on the signaling thread - and invokes the other |GetStats|. The + // other |GetStats| can be invoked on any thread; if not invoked on the + // network thread a thread hop will happen. + std::unique_ptr GetSessionStats_s(); virtual std::unique_ptr GetSessionStats( - const ChannelNamePairs& channel_name_pairs) { - return session_->GetSessionStats(channel_name_pairs); - } + const ChannelNamePairs& channel_name_pairs); + + // virtual so it can be mocked in unit tests virtual bool GetLocalCertificate( const std::string& transport_name, - rtc::scoped_refptr* certificate) { - return session_->GetLocalCertificate(transport_name, certificate); - } + rtc::scoped_refptr* certificate); virtual std::unique_ptr GetRemoteSSLCertificate( - const std::string& transport_name) { - return session_->GetRemoteSSLCertificate(transport_name); - } - virtual Call::Stats GetCallStats() { return session_->GetCallStats(); } + const std::string& transport_name); + + virtual Call::Stats GetCallStats(); + + // Exposed for stats collecting. + // TODO(steveanton): Switch callers to use the plural form and remove these. virtual cricket::VoiceChannel* voice_channel() { - return session_->voice_channel(); + if (voice_channels_.empty()) { + return nullptr; + } else { + return voice_channels_[0]; + } } virtual cricket::VideoChannel* video_channel() { - return session_->video_channel(); - } - virtual cricket::RtpDataChannel* rtp_data_channel() { - return session_->rtp_data_channel(); - } - virtual rtc::Optional sctp_content_name() const { - return session_->sctp_content_name(); - } - virtual rtc::Optional sctp_transport_name() const { - return session_->sctp_transport_name(); - } - virtual bool GetLocalTrackIdBySsrc(uint32_t ssrc, std::string* track_id) { - return session_->GetLocalTrackIdBySsrc(ssrc, track_id); - } - virtual bool GetRemoteTrackIdBySsrc(uint32_t ssrc, std::string* track_id) { - return session_->GetRemoteTrackIdBySsrc(ssrc, track_id); - } - bool IceRestartPending(const std::string& content_name) const { - return session_->IceRestartPending(content_name); - } - bool NeedsIceRestart(const std::string& content_name) const { - return session_->NeedsIceRestart(content_name); - } - bool GetSslRole(const std::string& content_name, rtc::SSLRole* role) { - return session_->GetSslRole(content_name, role); + if (video_channels_.empty()) { + return nullptr; + } else { + return video_channels_[0]; + } } - // This is needed for stats tests to inject a MockWebRtcSession. Once - // WebRtcSession has been merged in, this will no longer be needed. - void set_session_for_testing(WebRtcSession* session) { - session_ = session; + // Only valid when using deprecated RTP data channels. + virtual cricket::RtpDataChannel* rtp_data_channel() { + return rtp_data_channel_; } + virtual rtc::Optional sctp_content_name() const { + return sctp_content_name_; + } + virtual rtc::Optional sctp_transport_name() const { + return sctp_transport_name_; + } + + // Get the id used as a media stream track's "id" field from ssrc. + virtual bool GetLocalTrackIdBySsrc(uint32_t ssrc, std::string* track_id); + virtual bool GetRemoteTrackIdBySsrc(uint32_t ssrc, std::string* track_id); + + // Returns true if there was an ICE restart initiated by the remote offer. + bool IceRestartPending(const std::string& content_name) const; + + // Returns true if the ICE restart flag above was set, and no ICE restart has + // occurred yet for this transport (by applying a local description with + // changed ufrag/password). If the transport has been deleted as a result of + // bundling, returns false. + bool NeedsIceRestart(const std::string& content_name) const; + + // Get SSL role for an arbitrary m= section (handles bundling correctly). + // TODO(deadbeef): This is only used internally by the session description + // factory, it shouldn't really be public). + bool GetSslRole(const std::string& content_name, rtc::SSLRole* role); + + enum Error { + ERROR_NONE = 0, // no error + ERROR_CONTENT = 1, // channel errors in SetLocalContent/SetRemoteContent + ERROR_TRANSPORT = 2, // transport error of some kind + }; protected: ~PeerConnection() override; @@ -477,6 +514,251 @@ class PeerConnection : public PeerConnectionInterface, cricket::ChannelManager* channel_manager() const; MetricsObserverInterface* metrics_observer() const; + // Indicates the type of SessionDescription in a call to SetLocalDescription + // and SetRemoteDescription. + enum Action { + kOffer, + kPrAnswer, + kAnswer, + }; + + // Returns the last error in the session. See the enum above for details. + Error error() const { return error_; } + const std::string& error_desc() const { return error_desc_; } + + virtual std::vector voice_channels() const { + return voice_channels_; + } + virtual std::vector video_channels() const { + return video_channels_; + } + + cricket::BaseChannel* GetChannel(const std::string& content_name); + + // Get current SSL role used by SCTP's underlying transport. + bool GetSctpSslRole(rtc::SSLRole* role); + + void CreateOffer( + CreateSessionDescriptionObserver* observer, + const PeerConnectionInterface::RTCOfferAnswerOptions& options, + const cricket::MediaSessionOptions& session_options); + void CreateAnswer(CreateSessionDescriptionObserver* observer, + const cricket::MediaSessionOptions& session_options); + bool SetLocalDescription(std::unique_ptr desc, + std::string* err_desc); + bool SetRemoteDescription(std::unique_ptr desc, + std::string* err_desc); + + bool ProcessIceMessage(const IceCandidateInterface* ice_candidate); + + bool RemoveRemoteIceCandidates( + const std::vector& candidates); + + cricket::IceConfig ParseIceConfig( + const PeerConnectionInterface::RTCConfiguration& config) const; + + void SetIceConfig(const cricket::IceConfig& ice_config); + + // Start gathering candidates for any new transports, or transports doing an + // ICE restart. + void MaybeStartGathering(); + + // Implements DataChannelProviderInterface. + bool SendData(const cricket::SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + cricket::SendDataResult* result) override; + bool ConnectDataChannel(DataChannel* webrtc_data_channel) override; + void DisconnectDataChannel(DataChannel* webrtc_data_channel) override; + void AddSctpDataStream(int sid) override; + void RemoveSctpDataStream(int sid) override; + bool ReadyToSendData() const override; + + cricket::DataChannelType data_channel_type() const; + + // Set the "needs-ice-restart" flag as described in JSEP. After the flag is + // set, offers should generate new ufrags/passwords until an ICE restart + // occurs. + void SetNeedsIceRestartFlag(); + + // Called when an RTCCertificate is generated or retrieved by + // WebRTCSessionDescriptionFactory. Should happen before setLocalDescription. + void OnCertificateReady( + const rtc::scoped_refptr& certificate); + void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp); + + cricket::TransportController* transport_controller() const { + return transport_controller_.get(); + } + + // Return all managed, non-null channels. + std::vector Channels() const; + + // Non-const versions of local_description()/remote_description(), for use + // internally. + SessionDescriptionInterface* mutable_local_description() { + return pending_local_description_ ? pending_local_description_.get() + : current_local_description_.get(); + } + SessionDescriptionInterface* mutable_remote_description() { + return pending_remote_description_ ? pending_remote_description_.get() + : current_remote_description_.get(); + } + + // Updates the error state, signaling if necessary. + void SetError(Error error, const std::string& error_desc); + + bool UpdateSessionState(Action action, + cricket::ContentSource source, + std::string* err_desc); + Action GetAction(const std::string& type); + // Push the media parts of the local or remote session description + // down to all of the channels. + bool PushdownMediaDescription(cricket::ContentAction action, + cricket::ContentSource source, + std::string* error_desc); + bool PushdownSctpParameters_n(cricket::ContentSource source); + + bool PushdownTransportDescription(cricket::ContentSource source, + cricket::ContentAction action, + std::string* error_desc); + + // Helper methods to push local and remote transport descriptions. + bool PushdownLocalTransportDescription( + const cricket::SessionDescription* sdesc, + cricket::ContentAction action, + std::string* error_desc); + bool PushdownRemoteTransportDescription( + const cricket::SessionDescription* sdesc, + cricket::ContentAction action, + std::string* error_desc); + + // Returns true and the TransportInfo of the given |content_name| + // from |description|. Returns false if it's not available. + static bool GetTransportDescription( + const cricket::SessionDescription* description, + const std::string& content_name, + cricket::TransportDescription* info); + + // Returns the name of the transport channel when BUNDLE is enabled, or + // nullptr if the channel is not part of any bundle. + const std::string* GetBundleTransportName( + const cricket::ContentInfo* content, + const cricket::ContentGroup* bundle); + + // Cause all the BaseChannels in the bundle group to have the same + // transport channel. + bool EnableBundle(const cricket::ContentGroup& bundle); + + // Enables media channels to allow sending of media. + void EnableChannels(); + // Returns the media index for a local ice candidate given the content name. + // Returns false if the local session description does not have a media + // content called |content_name|. + bool GetLocalCandidateMediaIndex(const std::string& content_name, + int* sdp_mline_index); + // Uses all remote candidates in |remote_desc| in this session. + bool UseCandidatesInSessionDescription( + const SessionDescriptionInterface* remote_desc); + // Uses |candidate| in this session. + bool UseCandidate(const IceCandidateInterface* candidate); + // Deletes the corresponding channel of contents that don't exist in |desc|. + // |desc| can be null. This means that all channels are deleted. + void RemoveUnusedChannels(const cricket::SessionDescription* desc); + + // Allocates media channels based on the |desc|. If |desc| doesn't have + // the BUNDLE option, this method will disable BUNDLE in PortAllocator. + // This method will also delete any existing media channels before creating. + bool CreateChannels(const cricket::SessionDescription* desc); + + // Helper methods to create media channels. + bool CreateVoiceChannel(const cricket::ContentInfo* content, + const std::string* bundle_transport); + bool CreateVideoChannel(const cricket::ContentInfo* content, + const std::string* bundle_transport); + bool CreateDataChannel(const cricket::ContentInfo* content, + const std::string* bundle_transport); + + std::unique_ptr GetSessionStats_n( + const ChannelNamePairs& channel_name_pairs); + + bool CreateSctpTransport_n(const std::string& content_name, + const std::string& transport_name); + // For bundling. + void ChangeSctpTransport_n(const std::string& transport_name); + void DestroySctpTransport_n(); + // SctpTransport signal handlers. Needed to marshal signals from the network + // to signaling thread. + void OnSctpTransportReadyToSendData_n(); + // This may be called with "false" if the direction of the m= section causes + // us to tear down the SCTP connection. + void OnSctpTransportReadyToSendData_s(bool ready); + void OnSctpTransportDataReceived_n(const cricket::ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& payload); + // Beyond just firing the signal to the signaling thread, listens to SCTP + // CONTROL messages on unused SIDs and processes them as OPEN messages. + void OnSctpTransportDataReceived_s(const cricket::ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& payload); + void OnSctpStreamClosedRemotely_n(int sid); + + bool ValidateBundleSettings(const cricket::SessionDescription* desc); + bool HasRtcpMuxEnabled(const cricket::ContentInfo* content); + // Below methods are helper methods which verifies SDP. + bool ValidateSessionDescription(const SessionDescriptionInterface* sdesc, + cricket::ContentSource source, + std::string* err_desc); + + // Check if a call to SetLocalDescription is acceptable with |action|. + bool ExpectSetLocalDescription(Action action); + // Check if a call to SetRemoteDescription is acceptable with |action|. + bool ExpectSetRemoteDescription(Action action); + // Verifies a=setup attribute as per RFC 5763. + bool ValidateDtlsSetupAttribute(const cricket::SessionDescription* desc, + Action action); + + // Returns true if we are ready to push down the remote candidate. + // |remote_desc| is the new remote description, or NULL if the current remote + // description should be used. Output |valid| is true if the candidate media + // index is valid. + bool ReadyToUseRemoteCandidate(const IceCandidateInterface* candidate, + const SessionDescriptionInterface* remote_desc, + bool* valid); + + // Returns true if SRTP (either using DTLS-SRTP or SDES) is required by + // this session. + bool SrtpRequired() const; + + // TransportController signal handlers. + void OnTransportControllerConnectionState(cricket::IceConnectionState state); + void OnTransportControllerGatheringState(cricket::IceGatheringState state); + void OnTransportControllerCandidatesGathered( + const std::string& transport_name, + const std::vector& candidates); + void OnTransportControllerCandidatesRemoved( + const std::vector& candidates); + void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error); + + std::string GetSessionErrorMsg(); + + // Invoked when TransportController connection completion is signaled. + // Reports stats for all transports in use. + void ReportTransportStats(); + + // Gather the usage of IPv4/IPv6 as best connection. + void ReportBestConnectionState(const cricket::TransportStats& stats); + + void ReportNegotiatedCiphers(const cricket::TransportStats& stats); + + void OnSentPacket_w(const rtc::SentPacket& sent_packet); + + const std::string GetTransportName(const std::string& content_name); + + void DestroyRtcpTransport_n(const std::string& transport_name); + void RemoveAndDestroyVideoChannel(cricket::VideoChannel* video_channel); + void DestroyVideoChannel(cricket::VideoChannel* video_channel); + void RemoveAndDestroyVoiceChannel(cricket::VoiceChannel* voice_channel); + void DestroyVoiceChannel(cricket::VoiceChannel* voice_channel); + void DestroyDataChannel(); + // Storing the factory as a scoped reference pointer ensures that the memory // in the PeerConnectionFactoryImpl remains available as long as the // PeerConnection is running. It is passed to PeerConnection as a raw pointer. @@ -523,8 +805,6 @@ class PeerConnection : public PeerConnectionInterface, bool remote_peer_supports_msid_ = false; std::unique_ptr call_; - WebRtcSession* session_ = nullptr; - std::unique_ptr owned_session_; std::unique_ptr stats_; // A pointer is passed to senders_ rtc::scoped_refptr stats_collector_; @@ -533,6 +813,72 @@ class PeerConnection : public PeerConnectionInterface, std::vector< rtc::scoped_refptr>> receivers_; + + Error error_ = ERROR_NONE; + std::string error_desc_; + + std::string session_id_; + rtc::Optional initial_offerer_; + + std::unique_ptr transport_controller_; + std::unique_ptr sctp_factory_; + // TODO(steveanton): voice_channels_ and video_channels_ used to be a single + // VoiceChannel/VideoChannel respectively but are being changed to support + // multiple m= lines in unified plan. But until more work is done, these can + // only have 0 or 1 channel each. + // These channels are owned by ChannelManager. + std::vector voice_channels_; + std::vector video_channels_; + // |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_| + // when using SCTP. + cricket::RtpDataChannel* rtp_data_channel_ = nullptr; + + std::unique_ptr sctp_transport_; + // |sctp_transport_name_| keeps track of what DTLS transport the SCTP + // transport is using (which can change due to bundling). + rtc::Optional sctp_transport_name_; + // |sctp_content_name_| is the content name (MID) in SDP. + rtc::Optional sctp_content_name_; + // Value cached on signaling thread. Only updated when SctpReadyToSendData + // fires on the signaling thread. + bool sctp_ready_to_send_data_ = false; + // Same as signals provided by SctpTransport, but these are guaranteed to + // fire on the signaling thread, whereas SctpTransport fires on the networking + // thread. + // |sctp_invoker_| is used so that any signals queued on the signaling thread + // from the network thread are immediately discarded if the SctpTransport is + // destroyed (due to m= section being rejected). + // TODO(deadbeef): Use a proxy object to ensure that method calls/signals + // are marshalled to the right thread. Could almost use proxy.h for this, + // but it doesn't have a mechanism for marshalling sigslot::signals + std::unique_ptr sctp_invoker_; + sigslot::signal1 SignalSctpReadyToSendData; + sigslot::signal2 + SignalSctpDataReceived; + sigslot::signal1 SignalSctpStreamClosedRemotely; + + std::unique_ptr current_local_description_; + std::unique_ptr pending_local_description_; + std::unique_ptr current_remote_description_; + std::unique_ptr pending_remote_description_; + bool dtls_enabled_ = false; + // Specifies which kind of data channel is allowed. This is controlled + // by the chrome command-line flag and constraints: + // 1. If chrome command-line switch 'enable-sctp-data-channels' is enabled, + // constraint kEnableDtlsSrtp is true, and constaint kEnableRtpDataChannels is + // not set or false, SCTP is allowed (DCT_SCTP); + // 2. If constraint kEnableRtpDataChannels is true, RTP is allowed (DCT_RTP); + // 3. If both 1&2 are false, data channel is not allowed (DCT_NONE). + cricket::DataChannelType data_channel_type_ = cricket::DCT_NONE; + // List of content names for which the remote side triggered an ICE restart. + std::set pending_ice_restarts_; + + std::unique_ptr webrtc_session_desc_factory_; + + // Member variables for caching global options. + cricket::AudioOptions audio_options_; + cricket::VideoOptions video_options_; }; } // namespace webrtc diff --git a/pc/rtcstatscollector_unittest.cc b/pc/rtcstatscollector_unittest.cc index 44a462a13d..50cc6033b1 100644 --- a/pc/rtcstatscollector_unittest.cc +++ b/pc/rtcstatscollector_unittest.cc @@ -31,7 +31,6 @@ #include "pc/mediastreamtrack.h" #include "pc/test/mock_datachannel.h" #include "pc/test/mock_peerconnection.h" -#include "pc/test/mock_webrtcsession.h" #include "pc/test/rtcstatsobtainer.h" #include "rtc_base/checks.h" #include "rtc_base/fakeclock.h" @@ -281,9 +280,7 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { media_engine_(new cricket::FakeMediaEngine()), pc_factory_(new FakePeerConnectionFactory( std::unique_ptr(media_engine_))), - pc_(pc_factory_), - session_(&pc_) { - pc_.set_session_for_testing(&session_); + pc_(pc_factory_) { // Default return values for mocks. EXPECT_CALL(pc_, local_streams()).WillRepeatedly(Return(nullptr)); EXPECT_CALL(pc_, remote_streams()).WillRepeatedly(Return(nullptr)); @@ -293,12 +290,11 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { std::vector>())); EXPECT_CALL(pc_, sctp_data_channels()).WillRepeatedly( ReturnRef(data_channels_)); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); - EXPECT_CALL(session_, GetSessionStats(_)).WillRepeatedly(ReturnNull()); - EXPECT_CALL(session_, GetLocalCertificate(_, _)).WillRepeatedly( - Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, GetSessionStats(_)).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); } @@ -307,7 +303,6 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { rtc::Thread* network_thread() { return network_thread_; } rtc::Thread* signaling_thread() { return signaling_thread_; } cricket::FakeMediaEngine* media_engine() { return media_engine_; } - MockWebRtcSession& session() { return session_; } MockPeerConnection& pc() { return pc_; } std::vector>& data_channels() { return data_channels_; @@ -464,7 +459,7 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { worker_thread_, network_thread_, nullptr, media_engine_, voice_media_channel, "VoiceContentName", kDefaultRtcpMuxRequired, kDefaultSrtpRequired)); - EXPECT_CALL(session_, voice_channel()) + EXPECT_CALL(pc_, voice_channel()) .WillRepeatedly(Return(voice_channel_.get())); EXPECT_CALL(*voice_media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(*voice_media_info_), Return(true))); @@ -473,7 +468,7 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { video_channel_.reset(new cricket::VideoChannel( worker_thread_, network_thread_, nullptr, video_media_channel, "VideoContentName", kDefaultRtcpMuxRequired, kDefaultSrtpRequired)); - EXPECT_CALL(session_, video_channel()) + EXPECT_CALL(pc_, video_channel()) .WillRepeatedly(Return(video_channel_.get())); EXPECT_CALL(*video_media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(*video_media_info_), Return(true))); @@ -489,7 +484,6 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { cricket::FakeMediaEngine* media_engine_; rtc::scoped_refptr pc_factory_; MockPeerConnection pc_; - MockWebRtcSession session_; std::vector> data_channels_; std::unique_ptr voice_channel_; @@ -712,24 +706,26 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsSingle) { std::vector({ "(remote) single certificate" })); // Mock the session to return the local and remote certificates. - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([](const ChannelNamePairs&) { std::unique_ptr stats(new SessionStats()); stats->transport_stats["transport"].transport_name = "transport"; return stats; })); - EXPECT_CALL(test_->session(), GetLocalCertificate(_, _)).WillRepeatedly( - Invoke([&local_certinfo](const std::string& transport_name, - rtc::scoped_refptr* certificate) { - if (transport_name == "transport") { - *certificate = local_certinfo->certificate; - return true; - } - return false; - })); - EXPECT_CALL(test_->session(), - GetRemoteSSLCertificate_ReturnsRawPointer(_)).WillRepeatedly(Invoke( - [&remote_certinfo](const std::string& transport_name) { + EXPECT_CALL(test_->pc(), GetLocalCertificate(_, _)) + .WillRepeatedly( + Invoke([&local_certinfo]( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + if (transport_name == "transport") { + *certificate = local_certinfo->certificate; + return true; + } + return false; + })); + EXPECT_CALL(test_->pc(), GetRemoteSSLCertificate_ReturnsRawPointer(_)) + .WillRepeatedly(Invoke([&remote_certinfo]( + const std::string& transport_name) { if (transport_name == "transport") return remote_certinfo->certificate->ssl_certificate().GetReference(); return static_cast(nullptr); @@ -803,13 +799,13 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) { session_stats.transport_stats["TransportName"].transport_name = "TransportName"; - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); - EXPECT_CALL(test_->session(), voice_channel()) + EXPECT_CALL(test_->pc(), voice_channel()) .WillRepeatedly(Return(&voice_channel)); - EXPECT_CALL(test_->session(), video_channel()) + EXPECT_CALL(test_->pc(), video_channel()) .WillRepeatedly(Return(&video_channel)); rtc::scoped_refptr report = GetStatsReport(); @@ -883,31 +879,31 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsMultiple) { video_remote_certinfo->ders); // Mock the session to return the local and remote certificates. - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([](const ChannelNamePairs&) { std::unique_ptr stats(new SessionStats()); stats->transport_stats["audio"].transport_name = "audio"; stats->transport_stats["video"].transport_name = "video"; return stats; })); - EXPECT_CALL(test_->session(), GetLocalCertificate(_, _)).WillRepeatedly( - Invoke([&audio_local_certinfo, &video_local_certinfo]( - const std::string& transport_name, - rtc::scoped_refptr* certificate) { - if (transport_name == "audio") { - *certificate = audio_local_certinfo->certificate; - return true; - } - if (transport_name == "video") { - *certificate = video_local_certinfo->certificate; - return true; - } - return false; - })); - EXPECT_CALL(test_->session(), - GetRemoteSSLCertificate_ReturnsRawPointer(_)).WillRepeatedly(Invoke( - [&audio_remote_certinfo, &video_remote_certinfo]( - const std::string& transport_name) { + EXPECT_CALL(test_->pc(), GetLocalCertificate(_, _)) + .WillRepeatedly( + Invoke([&audio_local_certinfo, &video_local_certinfo]( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + if (transport_name == "audio") { + *certificate = audio_local_certinfo->certificate; + return true; + } + if (transport_name == "video") { + *certificate = video_local_certinfo->certificate; + return true; + } + return false; + })); + EXPECT_CALL(test_->pc(), GetRemoteSSLCertificate_ReturnsRawPointer(_)) + .WillRepeatedly(Invoke([&audio_remote_certinfo, &video_remote_certinfo]( + const std::string& transport_name) { if (transport_name == "audio") { return audio_remote_certinfo->certificate->ssl_certificate() .GetReference(); @@ -943,24 +939,26 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsChain) { CreateFakeCertificateAndInfoFromDers(remote_ders); // Mock the session to return the local and remote certificates. - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([](const ChannelNamePairs&) { std::unique_ptr stats(new SessionStats()); stats->transport_stats["transport"].transport_name = "transport"; return stats; })); - EXPECT_CALL(test_->session(), GetLocalCertificate(_, _)).WillRepeatedly( - Invoke([&local_certinfo](const std::string& transport_name, - rtc::scoped_refptr* certificate) { - if (transport_name == "transport") { - *certificate = local_certinfo->certificate; - return true; - } - return false; - })); - EXPECT_CALL(test_->session(), - GetRemoteSSLCertificate_ReturnsRawPointer(_)).WillRepeatedly(Invoke( - [&remote_certinfo](const std::string& transport_name) { + EXPECT_CALL(test_->pc(), GetLocalCertificate(_, _)) + .WillRepeatedly( + Invoke([&local_certinfo]( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + if (transport_name == "transport") { + *certificate = local_certinfo->certificate; + return true; + } + return false; + })); + EXPECT_CALL(test_->pc(), GetRemoteSSLCertificate_ReturnsRawPointer(_)) + .WillRepeatedly(Invoke([&remote_certinfo]( + const std::string& transport_name) { if (transport_name == "transport") return remote_certinfo->certificate->ssl_certificate().GetReference(); return static_cast(nullptr); @@ -1176,7 +1174,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { b_transport_channel_stats); // Mock the session to return the desired candidates. - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); @@ -1252,7 +1250,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { transport_channel_stats); // Mock the session to return the desired candidates. - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); @@ -1262,7 +1260,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { cricket::VideoMediaInfo video_media_info; EXPECT_CALL(*video_media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(video_media_info), Return(true))); - EXPECT_CALL(test_->session(), video_channel()) + EXPECT_CALL(test_->pc(), video_channel()) .WillRepeatedly(Return(&video_channel)); rtc::scoped_refptr report = GetStatsReport(); @@ -1357,8 +1355,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { call_stats.send_bandwidth_bps = kSendBandwidth; const int kRecvBandwidth = 999; call_stats.recv_bandwidth_bps = kRecvBandwidth; - EXPECT_CALL(test_->session(), GetCallStats()) - .WillRepeatedly(Return(call_stats)); + EXPECT_CALL(test_->pc(), GetCallStats()).WillRepeatedly(Return(call_stats)); EXPECT_CALL(*video_media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(video_media_info), Return(true))); collector_->ClearCachedStatsReport(); @@ -1851,11 +1848,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { session_stats.transport_stats["TransportName"].channel_stats.push_back( channel_stats); - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); - EXPECT_CALL(test_->session(), voice_channel()) + EXPECT_CALL(test_->pc(), voice_channel()) .WillRepeatedly(Return(&voice_channel)); rtc::scoped_refptr report = GetStatsReport(); @@ -1934,11 +1931,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { session_stats.transport_stats["TransportName"].channel_stats.push_back( channel_stats); - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); - EXPECT_CALL(test_->session(), video_channel()) + EXPECT_CALL(test_->pc(), video_channel()) .WillRepeatedly(Return(&video_channel)); rtc::scoped_refptr report = GetStatsReport(); @@ -2027,11 +2024,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { session_stats.transport_stats["TransportName"].channel_stats.push_back( channel_stats); - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); - EXPECT_CALL(test_->session(), voice_channel()) + EXPECT_CALL(test_->pc(), voice_channel()) .WillRepeatedly(Return(&voice_channel)); rtc::scoped_refptr report = GetStatsReport(); @@ -2109,11 +2106,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { session_stats.transport_stats["TransportName"].channel_stats.push_back( channel_stats); - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); - EXPECT_CALL(test_->session(), video_channel()) + EXPECT_CALL(test_->pc(), video_channel()) .WillRepeatedly(Return(&video_channel)); rtc::scoped_refptr report = GetStatsReport(); @@ -2190,7 +2187,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { // Mock the session to return the desired candidates. - EXPECT_CALL(test_->session(), GetSessionStats(_)) + EXPECT_CALL(test_->pc(), GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats)); })); @@ -2277,18 +2274,20 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { std::unique_ptr remote_certinfo = CreateFakeCertificateAndInfoFromDers( std::vector({ "(remote) local", "(remote) chain" })); - EXPECT_CALL(test_->session(), GetLocalCertificate(_, _)).WillRepeatedly( - Invoke([&local_certinfo](const std::string& transport_name, - rtc::scoped_refptr* certificate) { - if (transport_name == "transport") { - *certificate = local_certinfo->certificate; - return true; - } - return false; - })); - EXPECT_CALL(test_->session(), - GetRemoteSSLCertificate_ReturnsRawPointer(_)).WillRepeatedly(Invoke( - [&remote_certinfo](const std::string& transport_name) { + EXPECT_CALL(test_->pc(), GetLocalCertificate(_, _)) + .WillRepeatedly( + Invoke([&local_certinfo]( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + if (transport_name == "transport") { + *certificate = local_certinfo->certificate; + return true; + } + return false; + })); + EXPECT_CALL(test_->pc(), GetRemoteSSLCertificate_ReturnsRawPointer(_)) + .WillRepeatedly(Invoke([&remote_certinfo]( + const std::string& transport_name) { if (transport_name == "transport") return remote_certinfo->certificate->ssl_certificate().GetReference(); return static_cast(nullptr); diff --git a/pc/statscollector.cc b/pc/statscollector.cc index 15fe173082..194581663d 100644 --- a/pc/statscollector.cc +++ b/pc/statscollector.cc @@ -557,23 +557,19 @@ StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) { } stats_gathering_started_ = time_now; - // TODO(pthatcher): Merge PeerConnection and WebRtcSession so there is no - // pc_->session(). - if (pc_->session_created()) { - // TODO(tommi): All of these hop over to the worker thread to fetch - // information. We could use an AsyncInvoker to run all of these and post - // the information back to the signaling thread where we can create and - // update stats reports. That would also clean up the threading story a bit - // since we'd be creating/updating the stats report objects consistently on - // the same thread (this class has no locks right now). - ExtractSessionInfo(); - ExtractBweInfo(); - ExtractVoiceInfo(); - ExtractVideoInfo(level); - ExtractSenderInfo(); - ExtractDataInfo(); - UpdateTrackReports(); - } + // TODO(tommi): All of these hop over to the worker thread to fetch + // information. We could use an AsyncInvoker to run all of these and post + // the information back to the signaling thread where we can create and + // update stats reports. That would also clean up the threading story a bit + // since we'd be creating/updating the stats report objects consistently on + // the same thread (this class has no locks right now). + ExtractSessionInfo(); + ExtractBweInfo(); + ExtractVoiceInfo(); + ExtractVideoInfo(level); + ExtractSenderInfo(); + ExtractDataInfo(); + UpdateTrackReports(); } StatsReport* StatsCollector::PrepareReport( diff --git a/pc/statscollector_unittest.cc b/pc/statscollector_unittest.cc index 76e9006d1b..7448a12d55 100644 --- a/pc/statscollector_unittest.cc +++ b/pc/statscollector_unittest.cc @@ -27,7 +27,6 @@ #include "pc/test/fakedatachannelprovider.h" #include "pc/test/fakevideotracksource.h" #include "pc/test/mock_peerconnection.h" -#include "pc/test/mock_webrtcsession.h" #include "pc/videotrack.h" #include "rtc_base/base64.h" #include "rtc_base/fakesslidentity.h" @@ -570,14 +569,12 @@ class StatsCollectorTest : public testing::Test { media_engine_(new cricket::FakeMediaEngine()), pc_factory_(new FakePeerConnectionFactory( std::unique_ptr(media_engine_))), - pc_(pc_factory_), - session_(&pc_) { - pc_.set_session_for_testing(&session_); + pc_(pc_factory_) { // By default, we ignore session GetStats calls. - EXPECT_CALL(session_, GetSessionStats(_)).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, GetSessionStats(_)).WillRepeatedly(ReturnNull()); // Add default returns for mock classes. - EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(pc_, sctp_data_channels()) .WillRepeatedly(ReturnRef(data_channels_)); EXPECT_CALL(pc_, GetSenders()).WillRepeatedly(Return( @@ -610,7 +607,7 @@ class StatsCollectorTest : public testing::Test { webrtc::FakeVideoTrackSource::Create(), rtc::Thread::Current()); stream_->AddTrack(track_); - EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(kLocalTrackId), Return(true))); } @@ -621,7 +618,7 @@ class StatsCollectorTest : public testing::Test { webrtc::FakeVideoTrackSource::Create(), rtc::Thread::Current()); stream_->AddTrack(track_); - EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true))); } @@ -633,7 +630,7 @@ class StatsCollectorTest : public testing::Test { audio_track_ = new rtc::RefCountedObject( kLocalTrackId); stream_->AddTrack(audio_track_); - EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) .WillOnce(DoAll(SetArgPointee<1>(kLocalTrackId), Return(true))); } @@ -645,7 +642,7 @@ class StatsCollectorTest : public testing::Test { audio_track_ = new rtc::RefCountedObject( kRemoteTrackId); stream_->AddTrack(audio_track_); - EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) .WillOnce(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true))); } @@ -682,7 +679,7 @@ class StatsCollectorTest : public testing::Test { // Instruct the session to return stats containing the transport channel. InitSessionStats(vc_name); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -694,9 +691,8 @@ class StatsCollectorTest : public testing::Test { if (voice_receiver_info) stats_read->receivers.push_back(*voice_receiver_info); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly( - Return(voice_channel)); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(Return(voice_channel)); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(*stats_read), Return(true))); @@ -778,13 +774,12 @@ class StatsCollectorTest : public testing::Test { new rtc::FakeSSLIdentity(local_cert)))); // Configure MockWebRtcSession - EXPECT_CALL(session_, - GetLocalCertificate(transport_stats.transport_name, _)) + EXPECT_CALL(pc_, GetLocalCertificate(transport_stats.transport_name, _)) .WillOnce(DoAll(SetArgPointee<1>(local_certificate), Return(true))); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer( - transport_stats.transport_name)) + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer( + transport_stats.transport_name)) .WillOnce(Return(remote_cert.release())); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillOnce(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats)); @@ -845,7 +840,6 @@ class StatsCollectorTest : public testing::Test { cricket::FakeMediaEngine* media_engine_; rtc::scoped_refptr pc_factory_; MockPeerConnection pc_; - MockWebRtcSession session_; FakeDataChannelProvider data_channel_provider_; SessionStats session_stats_; rtc::scoped_refptr stream_; @@ -919,15 +913,14 @@ TEST_F(StatsCollectorTest, ExtractDataInfo) { TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -952,8 +945,8 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -968,15 +961,14 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { TEST_F(StatsCollectorTest, AudioBandwidthEstimationInfoIsReported) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); const char kAudioChannelName[] = "audio"; InitSessionStats(kAudioChannelName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr(new SessionStats(session_stats_)); })); @@ -1009,9 +1001,9 @@ TEST_F(StatsCollectorTest, AudioBandwidthEstimationInfoIsReported) { call_stats.send_bandwidth_bps = kSendBandwidth; call_stats.recv_bandwidth_bps = kRecvBandwidth; call_stats.pacer_delay_ms = kPacerDelay; - EXPECT_CALL(session_, GetCallStats()).WillRepeatedly(Return(call_stats)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, GetCallStats()).WillRepeatedly(Return(call_stats)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -1035,15 +1027,14 @@ TEST_F(StatsCollectorTest, AudioBandwidthEstimationInfoIsReported) { TEST_F(StatsCollectorTest, VideoBandwidthEstimationInfoIsReported) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1077,9 +1068,9 @@ TEST_F(StatsCollectorTest, VideoBandwidthEstimationInfoIsReported) { call_stats.send_bandwidth_bps = kSendBandwidth; call_stats.recv_bandwidth_bps = kRecvBandwidth; call_stats.pacer_delay_ms = kPacerDelay; - EXPECT_CALL(session_, GetCallStats()).WillRepeatedly(Return(call_stats)); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, GetCallStats()).WillRepeatedly(Return(call_stats)); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -1160,14 +1151,13 @@ TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) { TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1190,8 +1180,8 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -1235,9 +1225,8 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); @@ -1259,14 +1248,14 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), Return(true))); InitSessionStats(kVcName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1323,9 +1312,8 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); @@ -1339,7 +1327,7 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { // Instruct the session to return stats containing the transport channel. InitSessionStats(kVcName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1356,8 +1344,8 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { video_sender_info.remote_stats.push_back(remote_ssrc_stats); stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -1377,14 +1365,13 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1407,8 +1394,8 @@ TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) { video_receiver_info.packets_concealed = kNumOfPacketsConcealed; stats_read.receivers.push_back(video_receiver_info); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -1572,9 +1559,8 @@ TEST_F(StatsCollectorTest, ChainlessCertificateReportsCreated) { TEST_F(StatsCollectorTest, NoTransport) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); StatsReports reports; // returned values. @@ -1592,7 +1578,7 @@ TEST_F(StatsCollectorTest, NoTransport) { transport_stats; // Configure MockWebRtcSession - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats)); @@ -1631,9 +1617,8 @@ TEST_F(StatsCollectorTest, NoTransport) { TEST_F(StatsCollectorTest, NoCertificates) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); StatsReports reports; // returned values. @@ -1651,7 +1636,7 @@ TEST_F(StatsCollectorTest, NoCertificates) { transport_stats; // Configure MockWebRtcSession - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats)); @@ -1696,9 +1681,8 @@ TEST_F(StatsCollectorTest, UnsupportedDigestIgnored) { TEST_F(StatsCollectorTest, FilterOutNegativeInitialValues) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); @@ -1715,7 +1699,7 @@ TEST_F(StatsCollectorTest, FilterOutNegativeInitialValues) { rtc::scoped_refptr local_track( new rtc::RefCountedObject(kLocalTrackId)); stream_->AddTrack(local_track); - EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) .WillOnce(DoAll(SetArgPointee<1>(kLocalTrackId), Return(true))); stats.AddStream(stream_); stats.AddLocalAudioTrack(local_track.get(), kSsrcOfTrack); @@ -1725,14 +1709,14 @@ TEST_F(StatsCollectorTest, FilterOutNegativeInitialValues) { webrtc::MediaStream::Create("remotestreamlabel")); rtc::scoped_refptr remote_track( new rtc::RefCountedObject(kRemoteTrackId)); - EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) .WillOnce(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true))); remote_stream->AddTrack(remote_track); stats.AddStream(remote_stream); // Instruct the session to return stats containing the transport channel. InitSessionStats(kVcName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1760,8 +1744,8 @@ TEST_F(StatsCollectorTest, FilterOutNegativeInitialValues) { stats_read.senders.push_back(voice_sender_info); stats_read.receivers.push_back(voice_receiver_info); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -1806,9 +1790,8 @@ TEST_F(StatsCollectorTest, FilterOutNegativeInitialValues) { TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); @@ -1842,9 +1825,8 @@ TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) { TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); @@ -1872,9 +1854,8 @@ TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) { TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); @@ -1889,7 +1870,7 @@ TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { // Instruct the session to return stats containing the transport channel. InitSessionStats(kVcName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1903,8 +1884,8 @@ TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { cricket::VoiceMediaInfo stats_read; stats_read.senders.push_back(voice_sender_info); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -1936,9 +1917,8 @@ TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); @@ -1958,14 +1938,14 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { webrtc::MediaStream::Create("remotestreamlabel")); rtc::scoped_refptr remote_track( new rtc::RefCountedObject(kRemoteTrackId)); - EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) .WillOnce(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true))); remote_stream->AddTrack(remote_track); stats.AddStream(remote_stream); // Instruct the session to return stats containing the transport channel. InitSessionStats(kVcName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -1986,8 +1966,8 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { stats_read.senders.push_back(voice_sender_info); stats_read.receivers.push_back(voice_receiver_info); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), Return(true))); @@ -2026,9 +2006,8 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); @@ -2061,7 +2040,7 @@ TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) { static const std::string kNewTrackId = "new_track_id"; rtc::scoped_refptr new_audio_track( new rtc::RefCountedObject(kNewTrackId)); - EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + EXPECT_CALL(pc_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) .WillOnce(DoAll(SetArgPointee<1>(kNewTrackId), Return(true))); stream_->AddTrack(new_audio_track); @@ -2080,15 +2059,14 @@ TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) { TEST_F(StatsCollectorTest, VerifyVideoSendSsrcStats) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -2111,8 +2089,8 @@ TEST_F(StatsCollectorTest, VerifyVideoSendSsrcStats) { video_sender_info.qp_sum = rtc::Optional(11); stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); @@ -2128,15 +2106,14 @@ TEST_F(StatsCollectorTest, VerifyVideoSendSsrcStats) { TEST_F(StatsCollectorTest, VerifyVideoReceiveSsrcStats) { StatsCollectorForTest stats(&pc_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) + EXPECT_CALL(pc_, GetLocalCertificate(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(pc_, GetRemoteSSLCertificate_ReturnsRawPointer(_)) .WillRepeatedly(Return(nullptr)); const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); - EXPECT_CALL(session_, GetSessionStats(_)) + EXPECT_CALL(pc_, GetSessionStats(_)) .WillRepeatedly(Invoke([this](const ChannelNamePairs&) { return std::unique_ptr( new SessionStats(session_stats_)); @@ -2159,8 +2136,8 @@ TEST_F(StatsCollectorTest, VerifyVideoReceiveSsrcStats) { video_receiver_info.qp_sum = rtc::Optional(11); stats_read.receivers.push_back(video_receiver_info); - EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(pc_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(pc_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); diff --git a/pc/test/mock_peerconnection.h b/pc/test/mock_peerconnection.h index 032adb9d0a..444ee2e618 100644 --- a/pc/test/mock_peerconnection.h +++ b/pc/test/mock_peerconnection.h @@ -12,6 +12,7 @@ #define PC_TEST_MOCK_PEERCONNECTION_H_ #include +#include #include #include @@ -27,7 +28,7 @@ namespace webrtc { class FakePeerConnectionFactory : public rtc::RefCountedObject { public: - FakePeerConnectionFactory( + explicit FakePeerConnectionFactory( std::unique_ptr media_engine) : rtc::RefCountedObject( rtc::Thread::Current(), @@ -41,6 +42,10 @@ class FakePeerConnectionFactory class MockPeerConnection : public rtc::RefCountedObject { public: + // TODO(nisse): Valid overrides commented out, because the gmock + // methods don't use any override declarations, and we want to avoid + // warnings from -Winconsistent-missing-override. See + // http://crbug.com/428099. explicit MockPeerConnection(PeerConnectionFactory* factory) : rtc::RefCountedObject( factory, @@ -56,6 +61,27 @@ class MockPeerConnection std::vector>()); MOCK_CONST_METHOD0(sctp_data_channels, const std::vector>&()); + MOCK_METHOD0(voice_channel, cricket::VoiceChannel*()); + MOCK_METHOD0(video_channel, cricket::VideoChannel*()); + // Libjingle uses "local" for a outgoing track, and "remote" for a incoming + // track. + MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32_t, std::string*)); + MOCK_METHOD2(GetRemoteTrackIdBySsrc, bool(uint32_t, std::string*)); + MOCK_METHOD0(GetCallStats, Call::Stats()); + MOCK_METHOD1(GetSessionStats, + std::unique_ptr(const ChannelNamePairs&)); + MOCK_METHOD2(GetLocalCertificate, + bool(const std::string& transport_name, + rtc::scoped_refptr* certificate)); + + // Workaround for gmock's inability to cope with move-only return values. + std::unique_ptr GetRemoteSSLCertificate( + const std::string& transport_name) /* override */ { + return std::unique_ptr( + GetRemoteSSLCertificate_ReturnsRawPointer(transport_name)); + } + MOCK_METHOD1(GetRemoteSSLCertificate_ReturnsRawPointer, + rtc::SSLCertificate*(const std::string& transport_name)); }; } // namespace webrtc diff --git a/pc/test/mock_webrtcsession.h b/pc/test/mock_webrtcsession.h deleted file mode 100644 index e9654647cf..0000000000 --- a/pc/test/mock_webrtcsession.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef PC_TEST_MOCK_WEBRTCSESSION_H_ -#define PC_TEST_MOCK_WEBRTCSESSION_H_ - -#include -#include - -#include "media/sctp/sctptransportinternal.h" -#include "pc/test/mock_peerconnection.h" -#include "pc/webrtcsession.h" -#include "test/gmock.h" - -namespace webrtc { - -class MockWebRtcSession : public webrtc::WebRtcSession { - public: - // TODO(nisse): Valid overrides commented out, because the gmock - // methods don't use any override declarations, and we want to avoid - // warnings from -Winconsistent-missing-override. See - // http://crbug.com/428099. - explicit MockWebRtcSession(PeerConnection* pc) - : WebRtcSession( - pc, - std::unique_ptr( - new cricket::TransportController( - rtc::Thread::Current(), - rtc::Thread::Current(), - nullptr, - /*redetermine_role_on_ice_restart=*/true, - rtc::CryptoOptions())), - std::unique_ptr()) {} - MOCK_METHOD0(voice_channel, cricket::VoiceChannel*()); - MOCK_METHOD0(video_channel, cricket::VideoChannel*()); - // Libjingle uses "local" for a outgoing track, and "remote" for a incoming - // track. - MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32_t, std::string*)); - MOCK_METHOD2(GetRemoteTrackIdBySsrc, bool(uint32_t, std::string*)); - MOCK_METHOD0(GetCallStats, Call::Stats()); - MOCK_METHOD1(GetSessionStats, - std::unique_ptr(const ChannelNamePairs&)); - MOCK_METHOD2(GetLocalCertificate, - bool(const std::string& transport_name, - rtc::scoped_refptr* certificate)); - - // Workaround for gmock's inability to cope with move-only return values. - std::unique_ptr GetRemoteSSLCertificate( - const std::string& transport_name) /* override */ { - return std::unique_ptr( - GetRemoteSSLCertificate_ReturnsRawPointer(transport_name)); - } - MOCK_METHOD1(GetRemoteSSLCertificate_ReturnsRawPointer, - rtc::SSLCertificate*(const std::string& transport_name)); -}; - -} // namespace webrtc - -#endif // PC_TEST_MOCK_WEBRTCSESSION_H_ diff --git a/pc/webrtcsession.cc b/pc/webrtcsession.cc deleted file mode 100644 index cbed1bc146..0000000000 --- a/pc/webrtcsession.cc +++ /dev/null @@ -1,2388 +0,0 @@ -/* - * Copyright 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "pc/webrtcsession.h" - -#include - -#include -#include -#include -#include - -#include "api/call/audio_sink.h" -#include "api/jsepicecandidate.h" -#include "api/jsepsessiondescription.h" -#include "api/peerconnectioninterface.h" -#include "call/call.h" -#include "media/base/mediaconstants.h" -#include "media/sctp/sctptransportinternal.h" -#include "p2p/base/portallocator.h" -#include "pc/channel.h" -#include "pc/channelmanager.h" -#include "pc/mediasession.h" -#include "pc/peerconnection.h" -#include "pc/sctputils.h" -#include "pc/webrtcsessiondescriptionfactory.h" -#include "rtc_base/basictypes.h" -#include "rtc_base/bind.h" -#include "rtc_base/checks.h" -#include "rtc_base/helpers.h" -#include "rtc_base/logging.h" -#include "rtc_base/stringencode.h" -#include "rtc_base/stringutils.h" - -using cricket::ContentInfo; -using cricket::ContentInfos; -using cricket::MediaContentDescription; -using cricket::SessionDescription; -using cricket::TransportInfo; - -using cricket::LOCAL_PORT_TYPE; -using cricket::STUN_PORT_TYPE; -using cricket::RELAY_PORT_TYPE; -using cricket::PRFLX_PORT_TYPE; - -namespace webrtc { - -// Error messages -const char kBundleWithoutRtcpMux[] = - "rtcp-mux must be enabled when BUNDLE " - "is enabled."; -const char kCreateChannelFailed[] = "Failed to create channels."; -const char kInvalidCandidates[] = "Description contains invalid candidates."; -const char kInvalidSdp[] = "Invalid session description."; -const char kMlineMismatchInAnswer[] = - "The order of m-lines in answer doesn't match order in offer. Rejecting " - "answer."; -const char kMlineMismatchInSubsequentOffer[] = - "The order of m-lines in subsequent offer doesn't match order from " - "previous offer/answer."; -const char kPushDownTDFailed[] = - "Failed to push down transport description:"; -const char kSdpWithoutDtlsFingerprint[] = - "Called with SDP without DTLS fingerprint."; -const char kSdpWithoutSdesCrypto[] = - "Called with SDP without SDES crypto."; -const char kSdpWithoutIceUfragPwd[] = - "Called with SDP without ice-ufrag and ice-pwd."; -const char kSessionError[] = "Session error code: "; -const char kSessionErrorDesc[] = "Session error description: "; -const char kDtlsSrtpSetupFailureRtp[] = - "Couldn't set up DTLS-SRTP on RTP channel."; -const char kDtlsSrtpSetupFailureRtcp[] = - "Couldn't set up DTLS-SRTP on RTCP channel."; -const char kEnableBundleFailed[] = "Failed to enable BUNDLE."; - -IceCandidatePairType GetIceCandidatePairCounter( - const cricket::Candidate& local, - const cricket::Candidate& remote) { - const auto& l = local.type(); - const auto& r = remote.type(); - const auto& host = LOCAL_PORT_TYPE; - const auto& srflx = STUN_PORT_TYPE; - const auto& relay = RELAY_PORT_TYPE; - const auto& prflx = PRFLX_PORT_TYPE; - if (l == host && r == host) { - bool local_private = IPIsPrivate(local.address().ipaddr()); - bool remote_private = IPIsPrivate(remote.address().ipaddr()); - if (local_private) { - if (remote_private) { - return kIceCandidatePairHostPrivateHostPrivate; - } else { - return kIceCandidatePairHostPrivateHostPublic; - } - } else { - if (remote_private) { - return kIceCandidatePairHostPublicHostPrivate; - } else { - return kIceCandidatePairHostPublicHostPublic; - } - } - } - if (l == host && r == srflx) - return kIceCandidatePairHostSrflx; - if (l == host && r == relay) - return kIceCandidatePairHostRelay; - if (l == host && r == prflx) - return kIceCandidatePairHostPrflx; - if (l == srflx && r == host) - return kIceCandidatePairSrflxHost; - if (l == srflx && r == srflx) - return kIceCandidatePairSrflxSrflx; - if (l == srflx && r == relay) - return kIceCandidatePairSrflxRelay; - if (l == srflx && r == prflx) - return kIceCandidatePairSrflxPrflx; - if (l == relay && r == host) - return kIceCandidatePairRelayHost; - if (l == relay && r == srflx) - return kIceCandidatePairRelaySrflx; - if (l == relay && r == relay) - return kIceCandidatePairRelayRelay; - if (l == relay && r == prflx) - return kIceCandidatePairRelayPrflx; - if (l == prflx && r == host) - return kIceCandidatePairPrflxHost; - if (l == prflx && r == srflx) - return kIceCandidatePairPrflxSrflx; - if (l == prflx && r == relay) - return kIceCandidatePairPrflxRelay; - return kIceCandidatePairMax; -} - -// Verify that the order of media sections in |new_desc| matches -// |existing_desc|. The number of m= sections in |new_desc| should be no less -// than |existing_desc|. -static bool MediaSectionsInSameOrder(const SessionDescription* existing_desc, - const SessionDescription* new_desc) { - if (!existing_desc || !new_desc) { - return false; - } - - if (existing_desc->contents().size() > new_desc->contents().size()) { - return false; - } - - for (size_t i = 0; i < existing_desc->contents().size(); ++i) { - if (new_desc->contents()[i].name != existing_desc->contents()[i].name) { - return false; - } - const MediaContentDescription* new_desc_mdesc = - static_cast( - new_desc->contents()[i].description); - const MediaContentDescription* existing_desc_mdesc = - static_cast( - existing_desc->contents()[i].description); - if (new_desc_mdesc->type() != existing_desc_mdesc->type()) { - return false; - } - } - return true; -} - -static bool MediaSectionsHaveSameCount(const SessionDescription* desc1, - const SessionDescription* desc2) { - if (!desc1 || !desc2) { - return false; - } - return desc1->contents().size() == desc2->contents().size(); -} - -// Checks that each non-rejected content has SDES crypto keys or a DTLS -// fingerprint, unless it's in a BUNDLE group, in which case only the -// BUNDLE-tag section (first media section/description in the BUNDLE group) -// needs a ufrag and pwd. Mismatches, such as replying with a DTLS fingerprint -// to SDES keys, will be caught in JsepTransport negotiation, and backstopped -// by Channel's |srtp_required| check. -static bool VerifyCrypto(const SessionDescription* desc, - bool dtls_enabled, - std::string* error) { - const cricket::ContentGroup* bundle = - desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - const ContentInfos& contents = desc->contents(); - for (size_t index = 0; index < contents.size(); ++index) { - const ContentInfo* cinfo = &contents[index]; - if (cinfo->rejected) { - continue; - } - if (bundle && bundle->HasContentName(cinfo->name) && - cinfo->name != *(bundle->FirstContentName())) { - // This isn't the first media section in the BUNDLE group, so it's not - // required to have crypto attributes, since only the crypto attributes - // from the first section actually get used. - continue; - } - - // If the content isn't rejected or bundled into another m= section, crypto - // must be present. - const MediaContentDescription* media = - static_cast(cinfo->description); - const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); - if (!media || !tinfo) { - // Something is not right. - LOG(LS_ERROR) << kInvalidSdp; - *error = kInvalidSdp; - return false; - } - if (dtls_enabled) { - if (!tinfo->description.identity_fingerprint) { - LOG(LS_WARNING) << - "Session description must have DTLS fingerprint if DTLS enabled."; - *error = kSdpWithoutDtlsFingerprint; - return false; - } - } else { - if (media->cryptos().empty()) { - LOG(LS_WARNING) << - "Session description must have SDES when DTLS disabled."; - *error = kSdpWithoutSdesCrypto; - return false; - } - } - } - - return true; -} - -// Checks that each non-rejected content has ice-ufrag and ice-pwd set, unless -// it's in a BUNDLE group, in which case only the BUNDLE-tag section (first -// media section/description in the BUNDLE group) needs a ufrag and pwd. -static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) { - const cricket::ContentGroup* bundle = - desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - const ContentInfos& contents = desc->contents(); - for (size_t index = 0; index < contents.size(); ++index) { - const ContentInfo* cinfo = &contents[index]; - if (cinfo->rejected) { - continue; - } - if (bundle && bundle->HasContentName(cinfo->name) && - cinfo->name != *(bundle->FirstContentName())) { - // This isn't the first media section in the BUNDLE group, so it's not - // required to have ufrag/password, since only the ufrag/password from - // the first section actually get used. - continue; - } - - // If the content isn't rejected or bundled into another m= section, - // ice-ufrag and ice-pwd must be present. - const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); - if (!tinfo) { - // Something is not right. - LOG(LS_ERROR) << kInvalidSdp; - return false; - } - if (tinfo->description.ice_ufrag.empty() || - tinfo->description.ice_pwd.empty()) { - LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; - return false; - } - } - return true; -} - -static bool GetTrackIdBySsrc(const SessionDescription* session_description, - uint32_t ssrc, - std::string* track_id) { - RTC_DCHECK(track_id != NULL); - - const cricket::ContentInfo* audio_info = - cricket::GetFirstAudioContent(session_description); - if (audio_info) { - const cricket::MediaContentDescription* audio_content = - static_cast( - audio_info->description); - - const auto* found = - cricket::GetStreamBySsrc(audio_content->streams(), ssrc); - if (found) { - *track_id = found->id; - return true; - } - } - - const cricket::ContentInfo* video_info = - cricket::GetFirstVideoContent(session_description); - if (video_info) { - const cricket::MediaContentDescription* video_content = - static_cast( - video_info->description); - - const auto* found = - cricket::GetStreamBySsrc(video_content->streams(), ssrc); - if (found) { - *track_id = found->id; - return true; - } - } - return false; -} - -// Get the SCTP port out of a SessionDescription. -// Return -1 if not found. -static int GetSctpPort(const SessionDescription* session_description) { - const ContentInfo* content_info = GetFirstDataContent(session_description); - RTC_DCHECK(content_info); - if (!content_info) { - return -1; - } - const cricket::DataContentDescription* data = - static_cast( - (content_info->description)); - std::string value; - cricket::DataCodec match_pattern(cricket::kGoogleSctpDataCodecPlType, - cricket::kGoogleSctpDataCodecName); - for (const cricket::DataCodec& codec : data->codecs()) { - if (!codec.Matches(match_pattern)) { - continue; - } - if (codec.GetParam(cricket::kCodecParamPort, &value)) { - return rtc::FromString(value); - } - } - return -1; -} - -static bool BadSdp(const std::string& source, - const std::string& type, - const std::string& reason, - std::string* err_desc) { - std::ostringstream desc; - desc << "Failed to set " << source; - if (!type.empty()) { - desc << " " << type; - } - desc << " sdp: " << reason; - - if (err_desc) { - *err_desc = desc.str(); - } - LOG(LS_ERROR) << desc.str(); - return false; -} - -static bool BadSdp(cricket::ContentSource source, - const std::string& type, - const std::string& reason, - std::string* err_desc) { - if (source == cricket::CS_LOCAL) { - return BadSdp("local", type, reason, err_desc); - } else { - return BadSdp("remote", type, reason, err_desc); - } -} - -static bool BadLocalSdp(const std::string& type, - const std::string& reason, - std::string* err_desc) { - return BadSdp(cricket::CS_LOCAL, type, reason, err_desc); -} - -static bool BadRemoteSdp(const std::string& type, - const std::string& reason, - std::string* err_desc) { - return BadSdp(cricket::CS_REMOTE, type, reason, err_desc); -} - -static bool BadOfferSdp(cricket::ContentSource source, - const std::string& reason, - std::string* err_desc) { - return BadSdp(source, SessionDescriptionInterface::kOffer, reason, err_desc); -} - -static bool BadPranswerSdp(cricket::ContentSource source, - const std::string& reason, - std::string* err_desc) { - return BadSdp(source, SessionDescriptionInterface::kPrAnswer, - reason, err_desc); -} - -static bool BadAnswerSdp(cricket::ContentSource source, - const std::string& reason, - std::string* err_desc) { - return BadSdp(source, SessionDescriptionInterface::kAnswer, reason, err_desc); -} - -static std::string BadStateErrMsg( - PeerConnectionInterface::SignalingState state) { - std::ostringstream desc; - desc << "Called in wrong state: " << GetSignalingStateString(state); - return desc.str(); -} - -#define GET_STRING_OF_ERROR_CODE(err) \ - case webrtc::WebRtcSession::err: \ - result = #err; \ - break; - -static std::string GetErrorCodeString(webrtc::WebRtcSession::Error err) { - std::string result; - switch (err) { - GET_STRING_OF_ERROR_CODE(ERROR_NONE) - GET_STRING_OF_ERROR_CODE(ERROR_CONTENT) - GET_STRING_OF_ERROR_CODE(ERROR_TRANSPORT) - default: - RTC_NOTREACHED(); - break; - } - return result; -} - -static std::string MakeErrorString(const std::string& error, - const std::string& desc) { - std::ostringstream ret; - ret << error << " " << desc; - return ret.str(); -} - -static std::string MakeTdErrorString(const std::string& desc) { - return MakeErrorString(kPushDownTDFailed, desc); -} - -// Returns true if |new_desc| requests an ICE restart (i.e., new ufrag/pwd). -bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc, - const SessionDescriptionInterface* new_desc, - const std::string& content_name) { - if (!old_desc) { - return false; - } - const SessionDescription* new_sd = new_desc->description(); - const SessionDescription* old_sd = old_desc->description(); - const ContentInfo* cinfo = new_sd->GetContentByName(content_name); - if (!cinfo || cinfo->rejected) { - return false; - } - // If the content isn't rejected, check if ufrag and password has changed. - const cricket::TransportDescription* new_transport_desc = - new_sd->GetTransportDescriptionByName(content_name); - const cricket::TransportDescription* old_transport_desc = - old_sd->GetTransportDescriptionByName(content_name); - if (!new_transport_desc || !old_transport_desc) { - // No transport description exists. This is not an ICE restart. - return false; - } - if (cricket::IceCredentialsChanged( - old_transport_desc->ice_ufrag, old_transport_desc->ice_pwd, - new_transport_desc->ice_ufrag, new_transport_desc->ice_pwd)) { - LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name - << "."; - return true; - } - return false; -} - -WebRtcSession::WebRtcSession( - PeerConnection* pc, - std::unique_ptr transport_controller, - std::unique_ptr sctp_factory) - : pc_(pc), - // RFC 3264: The numeric value of the session id and version in the - // o line MUST be representable with a "64 bit signed integer". - // Due to this constraint session id |session_id| is max limited to - // LLONG_MAX. - session_id_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)), - transport_controller_(std::move(transport_controller)), - sctp_factory_(std::move(sctp_factory)), - dtls_enabled_(false), - data_channel_type_(cricket::DCT_NONE) { - transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED); - transport_controller_->SignalConnectionState.connect( - this, &WebRtcSession::OnTransportControllerConnectionState); - transport_controller_->SignalGatheringState.connect( - this, &WebRtcSession::OnTransportControllerGatheringState); - transport_controller_->SignalCandidatesGathered.connect( - this, &WebRtcSession::OnTransportControllerCandidatesGathered); - transport_controller_->SignalCandidatesRemoved.connect( - this, &WebRtcSession::OnTransportControllerCandidatesRemoved); - transport_controller_->SignalDtlsHandshakeError.connect( - this, &WebRtcSession::OnTransportControllerDtlsHandshakeError); -} - -WebRtcSession::~WebRtcSession() { - RTC_DCHECK(signaling_thread()->IsCurrent()); - // Destroy video channels first since they may have a pointer to a voice - // channel. - for (auto* channel : video_channels_) { - DestroyVideoChannel(channel); - } - for (auto* channel : voice_channels_) { - DestroyVoiceChannel(channel); - } - if (rtp_data_channel_) { - DestroyDataChannel(); - } - if (sctp_transport_) { - pc_->OnDataChannelDestroyed(); - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); - } - - LOG(LS_INFO) << "Session: " << session_id() << " is destroyed."; -} - -void WebRtcSession::Initialize( - const PeerConnectionFactoryInterface::Options& options, - std::unique_ptr cert_generator, - const PeerConnectionInterface::RTCConfiguration& rtc_configuration, - PeerConnection* pc) { - transport_controller_->SetSslMaxProtocolVersion(options.ssl_max_version); - - // Obtain a certificate from RTCConfiguration if any were provided (optional). - rtc::scoped_refptr certificate; - if (!rtc_configuration.certificates.empty()) { - // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of - // just picking the first one. The decision should be made based on the DTLS - // handshake. The DTLS negotiations need to know about all certificates. - certificate = rtc_configuration.certificates[0]; - } - - SetIceConfig(ParseIceConfig(rtc_configuration)); - - if (options.disable_encryption) { - dtls_enabled_ = false; - } else { - // Enable DTLS by default if we have an identity store or a certificate. - dtls_enabled_ = (cert_generator || certificate); - // |rtc_configuration| can override the default |dtls_enabled_| value. - if (rtc_configuration.enable_dtls_srtp) { - dtls_enabled_ = *(rtc_configuration.enable_dtls_srtp); - } - } - - // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. - // It takes precendence over the disable_sctp_data_channels - // PeerConnectionFactoryInterface::Options. - if (rtc_configuration.enable_rtp_data_channel) { - data_channel_type_ = cricket::DCT_RTP; - } else { - // DTLS has to be enabled to use SCTP. - if (!options.disable_sctp_data_channels && dtls_enabled_) { - data_channel_type_ = cricket::DCT_SCTP; - } - } - - video_options_.screencast_min_bitrate_kbps = - rtc_configuration.screencast_min_bitrate; - audio_options_.combined_audio_video_bwe = - rtc_configuration.combined_audio_video_bwe; - - audio_options_.audio_jitter_buffer_max_packets = - rtc::Optional(rtc_configuration.audio_jitter_buffer_max_packets); - - audio_options_.audio_jitter_buffer_fast_accelerate = rtc::Optional( - rtc_configuration.audio_jitter_buffer_fast_accelerate); - - // Whether the certificate generator/certificate is null or not determines - // what WebRtcSessionDescriptionFactory will do, so make sure that we give it - // the right instructions by clearing the variables if needed. - if (!dtls_enabled_) { - cert_generator.reset(); - certificate = nullptr; - } else if (certificate) { - // Favor generated certificate over the certificate generator. - cert_generator.reset(); - } - - webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( - signaling_thread(), pc_->channel_manager(), pc, session_id(), - std::move(cert_generator), certificate)); - webrtc_session_desc_factory_->SignalCertificateReady.connect( - this, &WebRtcSession::OnCertificateReady); - - if (options.disable_encryption) { - webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); - } - - webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( - options.crypto_options.enable_encrypted_rtp_header_extensions); -} - -void WebRtcSession::Close() { - pc_->ChangeSignalingState(PeerConnectionInterface::kClosed); - RemoveUnusedChannels(nullptr); - RTC_DCHECK(voice_channels_.empty()); - RTC_DCHECK(video_channels_.empty()); - RTC_DCHECK(!rtp_data_channel_); - RTC_DCHECK(!sctp_transport_); -} - -cricket::BaseChannel* WebRtcSession::GetChannel( - const std::string& content_name) { - if (voice_channel() && voice_channel()->content_name() == content_name) { - return voice_channel(); - } - if (video_channel() && video_channel()->content_name() == content_name) { - return video_channel(); - } - if (rtp_data_channel() && - rtp_data_channel()->content_name() == content_name) { - return rtp_data_channel(); - } - return nullptr; -} - -rtc::Thread* WebRtcSession::network_thread() const { - return pc_->network_thread(); -} -rtc::Thread* WebRtcSession::worker_thread() const { - return pc_->worker_thread(); -} -rtc::Thread* WebRtcSession::signaling_thread() const { - return pc_->signaling_thread(); -} - -bool WebRtcSession::GetSctpSslRole(rtc::SSLRole* role) { - if (!local_description() || !remote_description()) { - LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " - << "SSL Role of the SCTP transport."; - return false; - } - if (!sctp_transport_) { - LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the " - << "SSL Role of the SCTP transport."; - return false; - } - - return transport_controller_->GetSslRole(*sctp_transport_name_, role); -} - -bool WebRtcSession::GetSslRole(const std::string& content_name, - rtc::SSLRole* role) { - if (!local_description() || !remote_description()) { - LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " - << "SSL Role of the session."; - return false; - } - - return transport_controller_->GetSslRole(GetTransportName(content_name), - role); -} - -void WebRtcSession::CreateOffer( - CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - const cricket::MediaSessionOptions& session_options) { - webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); -} - -void WebRtcSession::CreateAnswer( - CreateSessionDescriptionObserver* observer, - const cricket::MediaSessionOptions& session_options) { - webrtc_session_desc_factory_->CreateAnswer(observer, session_options); -} - -bool WebRtcSession::SetLocalDescription( - std::unique_ptr desc, - std::string* err_desc) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - - // Validate SDP. - if (!ValidateSessionDescription(desc.get(), cricket::CS_LOCAL, err_desc)) { - return false; - } - - // Update the initial_offerer flag if this session is the initial_offerer. - Action action = GetAction(desc->type()); - if (!initial_offerer_.has_value()) { - initial_offerer_.emplace(action == kOffer); - if (*initial_offerer_) { - transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); - } else { - transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED); - } - } - - if (action == kAnswer) { - current_local_description_ = std::move(desc); - pending_local_description_ = nullptr; - current_remote_description_ = std::move(pending_remote_description_); - } else { - pending_local_description_ = std::move(desc); - } - - // Transport and Media channels will be created only when offer is set. - if (action == kOffer && !CreateChannels(local_description()->description())) { - // TODO(mallinath) - Handle CreateChannel failure, as new local description - // is applied. Restore back to old description. - return BadLocalSdp(local_description()->type(), kCreateChannelFailed, - err_desc); - } - - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(local_description()->description()); - - if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) { - return false; - } - if (remote_description()) { - // Now that we have a local description, we can push down remote candidates. - UseCandidatesInSessionDescription(remote_description()); - } - - pending_ice_restarts_.clear(); - if (error() != ERROR_NONE) { - return BadLocalSdp(local_description()->type(), GetSessionErrorMsg(), - err_desc); - } - return true; -} - -bool WebRtcSession::SetRemoteDescription( - std::unique_ptr desc, - std::string* err_desc) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - - // Validate SDP. - if (!ValidateSessionDescription(desc.get(), cricket::CS_REMOTE, err_desc)) { - return false; - } - - // Hold this pointer so candidates can be copied to it later in the method. - SessionDescriptionInterface* desc_ptr = desc.get(); - - const SessionDescriptionInterface* old_remote_description = - remote_description(); - // Grab ownership of the description being replaced for the remainder of this - // method, since it's used below as |old_remote_description|. - std::unique_ptr replaced_remote_description; - Action action = GetAction(desc->type()); - if (action == kAnswer) { - replaced_remote_description = pending_remote_description_ - ? std::move(pending_remote_description_) - : std::move(current_remote_description_); - current_remote_description_ = std::move(desc); - pending_remote_description_ = nullptr; - current_local_description_ = std::move(pending_local_description_); - } else { - replaced_remote_description = std::move(pending_remote_description_); - pending_remote_description_ = std::move(desc); - } - - // Transport and Media channels will be created only when offer is set. - if (action == kOffer && - !CreateChannels(remote_description()->description())) { - // TODO(mallinath) - Handle CreateChannel failure, as new local description - // is applied. Restore back to old description. - return BadRemoteSdp(remote_description()->type(), kCreateChannelFailed, - err_desc); - } - - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(remote_description()->description()); - - // NOTE: Candidates allocation will be initiated only when SetLocalDescription - // is called. - if (!UpdateSessionState(action, cricket::CS_REMOTE, err_desc)) { - return false; - } - - if (local_description() && - !UseCandidatesInSessionDescription(remote_description())) { - return BadRemoteSdp(remote_description()->type(), kInvalidCandidates, - err_desc); - } - - if (old_remote_description) { - for (const cricket::ContentInfo& content : - old_remote_description->description()->contents()) { - // Check if this new SessionDescription contains new ICE ufrag and - // password that indicates the remote peer requests an ICE restart. - // TODO(deadbeef): When we start storing both the current and pending - // remote description, this should reset pending_ice_restarts and compare - // against the current description. - if (CheckForRemoteIceRestart(old_remote_description, remote_description(), - content.name)) { - if (action == kOffer) { - pending_ice_restarts_.insert(content.name); - } - } else { - // We retain all received candidates only if ICE is not restarted. - // When ICE is restarted, all previous candidates belong to an old - // generation and should not be kept. - // TODO(deadbeef): This goes against the W3C spec which says the remote - // description should only contain candidates from the last set remote - // description plus any candidates added since then. We should remove - // this once we're sure it won't break anything. - WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( - old_remote_description, content.name, desc_ptr); - } - } - } - - if (error() != ERROR_NONE) { - return BadRemoteSdp(remote_description()->type(), GetSessionErrorMsg(), - err_desc); - } - - // Set the the ICE connection state to connecting since the connection may - // become writable with peer reflexive candidates before any remote candidate - // is signaled. - // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix - // is to have a new signal the indicates a change in checking state from the - // transport and expose a new checking() member from transport that can be - // read to determine the current checking state. The existing SignalConnecting - // actually means "gathering candidates", so cannot be be used here. - if (remote_description()->type() != SessionDescriptionInterface::kOffer && - pc_->ice_connection_state() == - PeerConnectionInterface::kIceConnectionNew) { - pc_->SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); - } - return true; -} - -// TODO(steveanton): Eventually it'd be nice to store the channels as a single -// vector of BaseChannel pointers instead of separate voice and video channel -// vectors. At that point, this will become a simple getter. -std::vector WebRtcSession::Channels() const { - std::vector channels; - channels.insert(channels.end(), voice_channels_.begin(), - voice_channels_.end()); - channels.insert(channels.end(), video_channels_.begin(), - video_channels_.end()); - if (rtp_data_channel_) { - channels.push_back(rtp_data_channel_); - } - return channels; -} - -void WebRtcSession::SetError(Error error, const std::string& error_desc) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - if (error != error_) { - error_ = error; - error_desc_ = error_desc; - } -} - -bool WebRtcSession::UpdateSessionState( - Action action, cricket::ContentSource source, - std::string* err_desc) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - - // If there's already a pending error then no state transition should happen. - // But all call-sites should be verifying this before calling us! - RTC_DCHECK(error() == ERROR_NONE); - std::string td_err; - if (action == kOffer) { - if (!PushdownTransportDescription(source, cricket::CA_OFFER, &td_err)) { - return BadOfferSdp(source, MakeTdErrorString(td_err), err_desc); - } - pc_->ChangeSignalingState(source == cricket::CS_LOCAL - ? PeerConnectionInterface::kHaveLocalOffer - : PeerConnectionInterface::kHaveRemoteOffer); - if (!PushdownMediaDescription(cricket::CA_OFFER, source, err_desc)) { - SetError(ERROR_CONTENT, *err_desc); - } - if (error() != ERROR_NONE) { - return BadOfferSdp(source, GetSessionErrorMsg(), err_desc); - } - } else if (action == kPrAnswer) { - if (!PushdownTransportDescription(source, cricket::CA_PRANSWER, &td_err)) { - return BadPranswerSdp(source, MakeTdErrorString(td_err), err_desc); - } - EnableChannels(); - pc_->ChangeSignalingState( - source == cricket::CS_LOCAL - ? PeerConnectionInterface::kHaveLocalPrAnswer - : PeerConnectionInterface::kHaveRemotePrAnswer); - if (!PushdownMediaDescription(cricket::CA_PRANSWER, source, err_desc)) { - SetError(ERROR_CONTENT, *err_desc); - } - if (error() != ERROR_NONE) { - return BadPranswerSdp(source, GetSessionErrorMsg(), err_desc); - } - } else if (action == kAnswer) { - const cricket::ContentGroup* local_bundle = - local_description()->description()->GetGroupByName( - cricket::GROUP_TYPE_BUNDLE); - const cricket::ContentGroup* remote_bundle = - remote_description()->description()->GetGroupByName( - cricket::GROUP_TYPE_BUNDLE); - if (local_bundle && remote_bundle) { - // The answerer decides the transport to bundle on. - const cricket::ContentGroup* answer_bundle = - (source == cricket::CS_LOCAL ? local_bundle : remote_bundle); - if (!EnableBundle(*answer_bundle)) { - LOG(LS_WARNING) << "Failed to enable BUNDLE."; - return BadAnswerSdp(source, kEnableBundleFailed, err_desc); - } - } - // Only push down the transport description after enabling BUNDLE; we don't - // want to push down a description on a transport about to be destroyed. - if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) { - return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc); - } - EnableChannels(); - pc_->ChangeSignalingState(PeerConnectionInterface::kStable); - if (!PushdownMediaDescription(cricket::CA_ANSWER, source, err_desc)) { - SetError(ERROR_CONTENT, *err_desc); - } - if (error() != ERROR_NONE) { - return BadAnswerSdp(source, GetSessionErrorMsg(), err_desc); - } - } - return true; -} - -WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) { - if (type == SessionDescriptionInterface::kOffer) { - return WebRtcSession::kOffer; - } else if (type == SessionDescriptionInterface::kPrAnswer) { - return WebRtcSession::kPrAnswer; - } else if (type == SessionDescriptionInterface::kAnswer) { - return WebRtcSession::kAnswer; - } - RTC_NOTREACHED() << "unknown action type"; - return WebRtcSession::kOffer; -} - -bool WebRtcSession::PushdownMediaDescription( - cricket::ContentAction action, - cricket::ContentSource source, - std::string* err) { - const SessionDescription* sdesc = - (source == cricket::CS_LOCAL ? local_description() : remote_description()) - ->description(); - RTC_DCHECK(sdesc); - bool all_success = true; - for (auto* channel : Channels()) { - // TODO(steveanton): Add support for multiple channels of the same type. - const ContentInfo* content_info = - cricket::GetFirstMediaContent(sdesc->contents(), channel->media_type()); - if (!content_info) { - continue; - } - const MediaContentDescription* content_desc = - static_cast(content_info->description); - if (content_desc && !content_info->rejected) { - bool success = (source == cricket::CS_LOCAL) - ? channel->SetLocalContent(content_desc, action, err) - : channel->SetRemoteContent(content_desc, action, err); - if (!success) { - all_success = false; - break; - } - } - } - // Need complete offer/answer with an SCTP m= section before starting SCTP, - // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19 - if (sctp_transport_ && local_description() && remote_description() && - cricket::GetFirstDataContent(local_description()->description()) && - cricket::GetFirstDataContent(remote_description()->description())) { - all_success &= network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&WebRtcSession::PushdownSctpParameters_n, this, source)); - } - return all_success; -} - -bool WebRtcSession::PushdownSctpParameters_n(cricket::ContentSource source) { - RTC_DCHECK(network_thread()->IsCurrent()); - RTC_DCHECK(local_description()); - RTC_DCHECK(remote_description()); - // Apply the SCTP port (which is hidden inside a DataCodec structure...) - // When we support "max-message-size", that would also be pushed down here. - return sctp_transport_->Start( - GetSctpPort(local_description()->description()), - GetSctpPort(remote_description()->description())); -} - -bool WebRtcSession::PushdownTransportDescription(cricket::ContentSource source, - cricket::ContentAction action, - std::string* error_desc) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - - if (source == cricket::CS_LOCAL) { - return PushdownLocalTransportDescription(local_description()->description(), - action, error_desc); - } - return PushdownRemoteTransportDescription(remote_description()->description(), - action, error_desc); -} - -bool WebRtcSession::PushdownLocalTransportDescription( - const SessionDescription* sdesc, - cricket::ContentAction action, - std::string* err) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - - if (!sdesc) { - return false; - } - - for (const TransportInfo& tinfo : sdesc->transport_infos()) { - if (!transport_controller_->SetLocalTransportDescription( - tinfo.content_name, tinfo.description, action, err)) { - return false; - } - } - - return true; -} - -bool WebRtcSession::PushdownRemoteTransportDescription( - const SessionDescription* sdesc, - cricket::ContentAction action, - std::string* err) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - - if (!sdesc) { - return false; - } - - for (const TransportInfo& tinfo : sdesc->transport_infos()) { - if (!transport_controller_->SetRemoteTransportDescription( - tinfo.content_name, tinfo.description, action, err)) { - return false; - } - } - - return true; -} - -bool WebRtcSession::GetTransportDescription( - const SessionDescription* description, - const std::string& content_name, - cricket::TransportDescription* tdesc) { - if (!description || !tdesc) { - return false; - } - const TransportInfo* transport_info = - description->GetTransportInfoByName(content_name); - if (!transport_info) { - return false; - } - *tdesc = transport_info->description; - return true; -} - -bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { - const std::string* first_content_name = bundle.FirstContentName(); - if (!first_content_name) { - LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; - return false; - } - const std::string& transport_name = *first_content_name; - - auto maybe_set_transport = [this, bundle, - transport_name](cricket::BaseChannel* ch) { - if (!ch || !bundle.HasContentName(ch->content_name())) { - return true; - } - - std::string old_transport_name = ch->transport_name(); - if (old_transport_name == transport_name) { - LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name() - << " on " << transport_name << "."; - return true; - } - - cricket::DtlsTransportInternal* rtp_dtls_transport = - transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - bool need_rtcp = (ch->rtcp_dtls_transport() != nullptr); - cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; - if (need_rtcp) { - rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - - ch->SetTransports(rtp_dtls_transport, rtcp_dtls_transport); - LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on " - << transport_name << "."; - transport_controller_->DestroyDtlsTransport( - old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - // If the channel needs rtcp, it means that the channel used to have a - // rtcp transport which needs to be deleted now. - if (need_rtcp) { - transport_controller_->DestroyDtlsTransport( - old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - return true; - }; - - if (!maybe_set_transport(voice_channel()) || - !maybe_set_transport(video_channel()) || - !maybe_set_transport(rtp_data_channel())) { - return false; - } - // For SCTP, transport creation/deletion happens here instead of in the - // object itself. - if (sctp_transport_) { - RTC_DCHECK(sctp_transport_name_); - RTC_DCHECK(sctp_content_name_); - if (transport_name != *sctp_transport_name_ && - bundle.HasContentName(*sctp_content_name_)) { - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this, - transport_name)); - } - } - - return true; -} - -bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) { - if (!remote_description()) { - LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added " - << "without any remote session description."; - return false; - } - - if (!candidate) { - LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL."; - return false; - } - - bool valid = false; - bool ready = ReadyToUseRemoteCandidate(candidate, NULL, &valid); - if (!valid) { - return false; - } - - // Add this candidate to the remote session description. - if (!mutable_remote_description()->AddCandidate(candidate)) { - LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used."; - return false; - } - - if (ready) { - return UseCandidate(candidate); - } else { - LOG(LS_INFO) << "ProcessIceMessage: Not ready to use candidate."; - return true; - } -} - -bool WebRtcSession::RemoveRemoteIceCandidates( - const std::vector& candidates) { - if (!remote_description()) { - LOG(LS_ERROR) << "RemoveRemoteIceCandidates: ICE candidates can't be " - << "removed without any remote session description."; - return false; - } - - if (candidates.empty()) { - LOG(LS_ERROR) << "RemoveRemoteIceCandidates: candidates are empty."; - return false; - } - - size_t number_removed = - mutable_remote_description()->RemoveCandidates(candidates); - if (number_removed != candidates.size()) { - LOG(LS_ERROR) << "RemoveRemoteIceCandidates: Failed to remove candidates. " - << "Requested " << candidates.size() << " but only " - << number_removed << " are removed."; - } - - // Remove the candidates from the transport controller. - std::string error; - bool res = transport_controller_->RemoveRemoteCandidates(candidates, &error); - if (!res && !error.empty()) { - LOG(LS_ERROR) << "Error when removing remote candidates: " << error; - } - return true; -} - -cricket::IceConfig WebRtcSession::ParseIceConfig( - const PeerConnectionInterface::RTCConfiguration& config) const { - cricket::ContinualGatheringPolicy gathering_policy; - // TODO(honghaiz): Add the third continual gathering policy in - // PeerConnectionInterface and map it to GATHER_CONTINUALLY_AND_RECOVER. - switch (config.continual_gathering_policy) { - case PeerConnectionInterface::GATHER_ONCE: - gathering_policy = cricket::GATHER_ONCE; - break; - case PeerConnectionInterface::GATHER_CONTINUALLY: - gathering_policy = cricket::GATHER_CONTINUALLY; - break; - default: - RTC_NOTREACHED(); - gathering_policy = cricket::GATHER_ONCE; - } - cricket::IceConfig ice_config; - ice_config.receiving_timeout = config.ice_connection_receiving_timeout; - ice_config.prioritize_most_likely_candidate_pairs = - config.prioritize_most_likely_ice_candidate_pairs; - ice_config.backup_connection_ping_interval = - config.ice_backup_candidate_pair_ping_interval; - ice_config.continual_gathering_policy = gathering_policy; - ice_config.presume_writable_when_fully_relayed = - config.presume_writable_when_fully_relayed; - ice_config.ice_check_min_interval = config.ice_check_min_interval; - ice_config.regather_all_networks_interval_range = - config.ice_regather_interval_range; - return ice_config; -} - -void WebRtcSession::SetIceConfig(const cricket::IceConfig& config) { - transport_controller_->SetIceConfig(config); -} - -void WebRtcSession::MaybeStartGathering() { - transport_controller_->MaybeStartGathering(); -} - -bool WebRtcSession::GetLocalTrackIdBySsrc(uint32_t ssrc, - std::string* track_id) { - if (!local_description()) { - return false; - } - return webrtc::GetTrackIdBySsrc(local_description()->description(), ssrc, - track_id); -} - -bool WebRtcSession::GetRemoteTrackIdBySsrc(uint32_t ssrc, - std::string* track_id) { - if (!remote_description()) { - return false; - } - return webrtc::GetTrackIdBySsrc(remote_description()->description(), ssrc, - track_id); -} - -bool WebRtcSession::SendData(const cricket::SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - cricket::SendDataResult* result) { - if (!rtp_data_channel_ && !sctp_transport_) { - LOG(LS_ERROR) << "SendData called when rtp_data_channel_ " - << "and sctp_transport_ are NULL."; - return false; - } - return rtp_data_channel_ - ? rtp_data_channel_->SendData(params, payload, result) - : network_thread()->Invoke( - RTC_FROM_HERE, - Bind(&cricket::SctpTransportInternal::SendData, - sctp_transport_.get(), params, payload, result)); -} - -bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) { - if (!rtp_data_channel_ && !sctp_transport_) { - // Don't log an error here, because DataChannels are expected to call - // ConnectDataChannel in this state. It's the only way to initially tell - // whether or not the underlying transport is ready. - return false; - } - if (rtp_data_channel_) { - rtp_data_channel_->SignalReadyToSendData.connect( - webrtc_data_channel, &DataChannel::OnChannelReady); - rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel, - &DataChannel::OnDataReceived); - } else { - SignalSctpReadyToSendData.connect(webrtc_data_channel, - &DataChannel::OnChannelReady); - SignalSctpDataReceived.connect(webrtc_data_channel, - &DataChannel::OnDataReceived); - SignalSctpStreamClosedRemotely.connect( - webrtc_data_channel, &DataChannel::OnStreamClosedRemotely); - } - return true; -} - -void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) { - if (!rtp_data_channel_ && !sctp_transport_) { - LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and " - "sctp_transport_ are NULL."; - return; - } - if (rtp_data_channel_) { - rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel); - rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel); - } else { - SignalSctpReadyToSendData.disconnect(webrtc_data_channel); - SignalSctpDataReceived.disconnect(webrtc_data_channel); - SignalSctpStreamClosedRemotely.disconnect(webrtc_data_channel); - } -} - -void WebRtcSession::AddSctpDataStream(int sid) { - if (!sctp_transport_) { - LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL."; - return; - } - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream, - sctp_transport_.get(), sid)); -} - -void WebRtcSession::RemoveSctpDataStream(int sid) { - if (!sctp_transport_) { - LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is " - << "NULL."; - return; - } - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream, - sctp_transport_.get(), sid)); -} - -bool WebRtcSession::ReadyToSendData() const { - return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) || - sctp_ready_to_send_data_; -} - -std::unique_ptr WebRtcSession::GetSessionStats_s() { - RTC_DCHECK(signaling_thread()->IsCurrent()); - ChannelNamePairs channel_name_pairs; - if (voice_channel()) { - channel_name_pairs.voice = rtc::Optional(ChannelNamePair( - voice_channel()->content_name(), voice_channel()->transport_name())); - } - if (video_channel()) { - channel_name_pairs.video = rtc::Optional(ChannelNamePair( - video_channel()->content_name(), video_channel()->transport_name())); - } - if (rtp_data_channel()) { - channel_name_pairs.data = rtc::Optional( - ChannelNamePair(rtp_data_channel()->content_name(), - rtp_data_channel()->transport_name())); - } - if (sctp_transport_) { - RTC_DCHECK(sctp_content_name_); - RTC_DCHECK(sctp_transport_name_); - channel_name_pairs.data = rtc::Optional( - ChannelNamePair(*sctp_content_name_, *sctp_transport_name_)); - } - return GetSessionStats(channel_name_pairs); -} - -std::unique_ptr WebRtcSession::GetSessionStats( - const ChannelNamePairs& channel_name_pairs) { - if (network_thread()->IsCurrent()) { - return GetSessionStats_n(channel_name_pairs); - } - return network_thread()->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&WebRtcSession::GetSessionStats_n, this, channel_name_pairs)); -} - -bool WebRtcSession::GetLocalCertificate( - const std::string& transport_name, - rtc::scoped_refptr* certificate) { - return transport_controller_->GetLocalCertificate(transport_name, - certificate); -} - -std::unique_ptr WebRtcSession::GetRemoteSSLCertificate( - const std::string& transport_name) { - return transport_controller_->GetRemoteSSLCertificate(transport_name); -} - -cricket::DataChannelType WebRtcSession::data_channel_type() const { - return data_channel_type_; -} - -bool WebRtcSession::IceRestartPending(const std::string& content_name) const { - return pending_ice_restarts_.find(content_name) != - pending_ice_restarts_.end(); -} - -void WebRtcSession::SetNeedsIceRestartFlag() { - transport_controller_->SetNeedsIceRestartFlag(); -} - -bool WebRtcSession::NeedsIceRestart(const std::string& content_name) const { - return transport_controller_->NeedsIceRestart(content_name); -} - -void WebRtcSession::OnCertificateReady( - const rtc::scoped_refptr& certificate) { - transport_controller_->SetLocalCertificate(certificate); -} - -void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) { - SetError(ERROR_TRANSPORT, - rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); -} - -void WebRtcSession::OnTransportControllerConnectionState( - cricket::IceConnectionState state) { - switch (state) { - case cricket::kIceConnectionConnecting: - // If the current state is Connected or Completed, then there were - // writable channels but now there are not, so the next state must - // be Disconnected. - // kIceConnectionConnecting is currently used as the default, - // un-connected state by the TransportController, so its only use is - // detecting disconnections. - if (pc_->ice_connection_state_ == - PeerConnectionInterface::kIceConnectionConnected || - pc_->ice_connection_state_ == - PeerConnectionInterface::kIceConnectionCompleted) { - pc_->SetIceConnectionState( - PeerConnectionInterface::kIceConnectionDisconnected); - } - break; - case cricket::kIceConnectionFailed: - pc_->SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed); - break; - case cricket::kIceConnectionConnected: - LOG(LS_INFO) << "Changing to ICE connected state because " - << "all transports are writable."; - pc_->SetIceConnectionState( - PeerConnectionInterface::kIceConnectionConnected); - break; - case cricket::kIceConnectionCompleted: - LOG(LS_INFO) << "Changing to ICE completed state because " - << "all transports are complete."; - if (pc_->ice_connection_state_ != - PeerConnectionInterface::kIceConnectionConnected) { - // If jumping directly from "checking" to "connected", - // signal "connected" first. - pc_->SetIceConnectionState( - PeerConnectionInterface::kIceConnectionConnected); - } - pc_->SetIceConnectionState( - PeerConnectionInterface::kIceConnectionCompleted); - if (pc_->metrics_observer()) { - ReportTransportStats(); - } - break; - default: - RTC_NOTREACHED(); - } -} - -void WebRtcSession::OnTransportControllerCandidatesGathered( - const std::string& transport_name, - const cricket::Candidates& candidates) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - int sdp_mline_index; - if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { - LOG(LS_ERROR) << "OnTransportControllerCandidatesGathered: content name " - << transport_name << " not found"; - return; - } - - for (cricket::Candidates::const_iterator citer = candidates.begin(); - citer != candidates.end(); ++citer) { - // Use transport_name as the candidate media id. - std::unique_ptr candidate( - new JsepIceCandidate(transport_name, sdp_mline_index, *citer)); - if (local_description()) { - mutable_local_description()->AddCandidate(candidate.get()); - } - pc_->OnIceCandidate(std::move(candidate)); - } -} - -void WebRtcSession::OnTransportControllerCandidatesRemoved( - const std::vector& candidates) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - // Sanity check. - for (const cricket::Candidate& candidate : candidates) { - if (candidate.transport_name().empty()) { - LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " - << "empty content name in candidate " - << candidate.ToString(); - return; - } - } - - if (local_description()) { - mutable_local_description()->RemoveCandidates(candidates); - } - pc_->OnIceCandidatesRemoved(candidates); -} - -void WebRtcSession::OnTransportControllerDtlsHandshakeError( - rtc::SSLHandshakeError error) { - if (pc_->metrics_observer()) { - pc_->metrics_observer()->IncrementEnumCounter( - webrtc::kEnumCounterDtlsHandshakeError, static_cast(error), - static_cast(rtc::SSLHandshakeError::MAX_VALUE)); - } -} - -// Enabling voice and video (and RTP data) channels. -void WebRtcSession::EnableChannels() { - for (cricket::VoiceChannel* voice_channel : voice_channels_) { - if (!voice_channel->enabled()) { - voice_channel->Enable(true); - } - } - - for (cricket::VideoChannel* video_channel : video_channels_) { - if (!video_channel->enabled()) { - video_channel->Enable(true); - } - } - - if (rtp_data_channel_ && !rtp_data_channel_->enabled()) - rtp_data_channel_->Enable(true); -} - -// Returns the media index for a local ice candidate given the content name. -bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name, - int* sdp_mline_index) { - if (!local_description() || !sdp_mline_index) { - return false; - } - - bool content_found = false; - const ContentInfos& contents = local_description()->description()->contents(); - for (size_t index = 0; index < contents.size(); ++index) { - if (contents[index].name == content_name) { - *sdp_mline_index = static_cast(index); - content_found = true; - break; - } - } - return content_found; -} - -bool WebRtcSession::UseCandidatesInSessionDescription( - const SessionDescriptionInterface* remote_desc) { - if (!remote_desc) { - return true; - } - bool ret = true; - - for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) { - const IceCandidateCollection* candidates = remote_desc->candidates(m); - for (size_t n = 0; n < candidates->count(); ++n) { - const IceCandidateInterface* candidate = candidates->at(n); - bool valid = false; - if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) { - if (valid) { - LOG(LS_INFO) << "UseCandidatesInSessionDescription: Not ready to use " - << "candidate."; - } - continue; - } - ret = UseCandidate(candidate); - if (!ret) { - break; - } - } - } - return ret; -} - -bool WebRtcSession::UseCandidate(const IceCandidateInterface* candidate) { - size_t mediacontent_index = static_cast(candidate->sdp_mline_index()); - size_t remote_content_size = - remote_description()->description()->contents().size(); - if (mediacontent_index >= remote_content_size) { - LOG(LS_ERROR) << "UseCandidate: Invalid candidate media index."; - return false; - } - - cricket::ContentInfo content = - remote_description()->description()->contents()[mediacontent_index]; - std::vector candidates; - candidates.push_back(candidate->candidate()); - // Invoking BaseSession method to handle remote candidates. - std::string error; - if (transport_controller_->AddRemoteCandidates(content.name, candidates, - &error)) { - // Candidates successfully submitted for checking. - if (pc_->ice_connection_state_ == - PeerConnectionInterface::kIceConnectionNew || - pc_->ice_connection_state_ == - PeerConnectionInterface::kIceConnectionDisconnected) { - // If state is New, then the session has just gotten its first remote ICE - // candidates, so go to Checking. - // If state is Disconnected, the session is re-using old candidates or - // receiving additional ones, so go to Checking. - // If state is Connected, stay Connected. - // TODO(bemasc): If state is Connected, and the new candidates are for a - // newly added transport, then the state actually _should_ move to - // checking. Add a way to distinguish that case. - pc_->SetIceConnectionState( - PeerConnectionInterface::kIceConnectionChecking); - } - // TODO(bemasc): If state is Completed, go back to Connected. - } else { - if (!error.empty()) { - LOG(LS_WARNING) << error; - } - } - return true; -} - -void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { - // TODO(steveanton): Add support for multiple audio/video channels. - // Destroy video channel first since it may have a pointer to the - // voice channel. - const cricket::ContentInfo* video_info = cricket::GetFirstVideoContent(desc); - if ((!video_info || video_info->rejected) && video_channel()) { - RemoveAndDestroyVideoChannel(video_channel()); - } - - const cricket::ContentInfo* voice_info = cricket::GetFirstAudioContent(desc); - if ((!voice_info || voice_info->rejected) && voice_channel()) { - RemoveAndDestroyVoiceChannel(voice_channel()); - } - - const cricket::ContentInfo* data_info = - cricket::GetFirstDataContent(desc); - if (!data_info || data_info->rejected) { - if (rtp_data_channel_) { - DestroyDataChannel(); - } - if (sctp_transport_) { - pc_->OnDataChannelDestroyed(); - network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); - } - } -} - -// Returns the name of the transport channel when BUNDLE is enabled, or nullptr -// if the channel is not part of any bundle. -const std::string* WebRtcSession::GetBundleTransportName( - const cricket::ContentInfo* content, - const cricket::ContentGroup* bundle) { - if (!bundle) { - return nullptr; - } - const std::string* first_content_name = bundle->FirstContentName(); - if (!first_content_name) { - LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; - return nullptr; - } - if (!bundle->HasContentName(content->name)) { - LOG(LS_WARNING) << content->name << " is not part of any bundle group"; - return nullptr; - } - LOG(LS_INFO) << "Bundling " << content->name << " on " << *first_content_name; - return first_content_name; -} - -bool WebRtcSession::CreateChannels(const SessionDescription* desc) { - // TODO(steveanton): Add support for multiple audio/video channels. - const cricket::ContentGroup* bundle_group = nullptr; - if (pc_->configuration_.bundle_policy == - PeerConnectionInterface::kBundlePolicyMaxBundle) { - bundle_group = desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - if (!bundle_group) { - LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified"; - return false; - } - } - // Creating the media channels and transport proxies. - const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc); - if (voice && !voice->rejected && !voice_channel()) { - if (!CreateVoiceChannel(voice, - GetBundleTransportName(voice, bundle_group))) { - LOG(LS_ERROR) << "Failed to create voice channel."; - return false; - } - } - - const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc); - if (video && !video->rejected && !video_channel()) { - if (!CreateVideoChannel(video, - GetBundleTransportName(video, bundle_group))) { - LOG(LS_ERROR) << "Failed to create video channel."; - return false; - } - } - - const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc); - if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected && - !rtp_data_channel_ && !sctp_transport_) { - if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) { - LOG(LS_ERROR) << "Failed to create data channel."; - return false; - } - } - - return true; -} - -bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content, - const std::string* bundle_transport) { - // TODO(steveanton): Check to see if it's safe to create multiple voice - // channels. - RTC_DCHECK(voice_channels_.empty()); - - std::string transport_name = - bundle_transport ? *bundle_transport : content->name; - - cricket::DtlsTransportInternal* rtp_dtls_transport = - transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; - if (pc_->configuration_.rtcp_mux_policy != - PeerConnectionInterface::kRtcpMuxPolicyRequire) { - rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - - cricket::VoiceChannel* voice_channel = - pc_->channel_manager()->CreateVoiceChannel( - pc_->call_.get(), pc_->configuration_.media_config, - rtp_dtls_transport, rtcp_dtls_transport, - transport_controller_->signaling_thread(), content->name, - SrtpRequired(), audio_options_); - if (!voice_channel) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - if (rtcp_dtls_transport) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - return false; - } - - voice_channels_.push_back(voice_channel); - - voice_channel->SignalRtcpMuxFullyActive.connect( - this, &WebRtcSession::DestroyRtcpTransport_n); - voice_channel->SignalDtlsSrtpSetupFailure.connect( - this, &WebRtcSession::OnDtlsSrtpSetupFailure); - - // TODO(steveanton): This should signal which voice channel was created since - // we can have multiple. - pc_->OnVoiceChannelCreated(); - voice_channel->SignalSentPacket.connect(this, &WebRtcSession::OnSentPacket_w); - return true; -} - -bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content, - const std::string* bundle_transport) { - // TODO(steveanton): Check to see if it's safe to create multiple video - // channels. - RTC_DCHECK(video_channels_.empty()); - - std::string transport_name = - bundle_transport ? *bundle_transport : content->name; - - cricket::DtlsTransportInternal* rtp_dtls_transport = - transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; - if (pc_->configuration_.rtcp_mux_policy != - PeerConnectionInterface::kRtcpMuxPolicyRequire) { - rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - - cricket::VideoChannel* video_channel = - pc_->channel_manager()->CreateVideoChannel( - pc_->call_.get(), pc_->configuration_.media_config, - rtp_dtls_transport, rtcp_dtls_transport, - transport_controller_->signaling_thread(), content->name, - SrtpRequired(), video_options_); - - if (!video_channel) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - if (rtcp_dtls_transport) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - return false; - } - - video_channels_.push_back(video_channel); - - video_channel->SignalRtcpMuxFullyActive.connect( - this, &WebRtcSession::DestroyRtcpTransport_n); - video_channel->SignalDtlsSrtpSetupFailure.connect( - this, &WebRtcSession::OnDtlsSrtpSetupFailure); - - // TODO(steveanton): This should signal which video channel was created since - // we can have multiple. - pc_->OnVideoChannelCreated(); - video_channel->SignalSentPacket.connect(this, &WebRtcSession::OnSentPacket_w); - return true; -} - -bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content, - const std::string* bundle_transport) { - const std::string transport_name = - bundle_transport ? *bundle_transport : content->name; - bool sctp = (data_channel_type_ == cricket::DCT_SCTP); - if (sctp) { - if (!sctp_factory_) { - LOG(LS_ERROR) - << "Trying to create SCTP transport, but didn't compile with " - "SCTP support (HAVE_SCTP)"; - return false; - } - if (!network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&WebRtcSession::CreateSctpTransport_n, - this, content->name, transport_name))) { - return false; - } - } else { - std::string transport_name = - bundle_transport ? *bundle_transport : content->name; - cricket::DtlsTransportInternal* rtp_dtls_transport = - transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr; - if (pc_->configuration_.rtcp_mux_policy != - PeerConnectionInterface::kRtcpMuxPolicyRequire) { - rtcp_dtls_transport = transport_controller_->CreateDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - - rtp_data_channel_ = pc_->channel_manager()->CreateRtpDataChannel( - pc_->configuration_.media_config, rtp_dtls_transport, - rtcp_dtls_transport, transport_controller_->signaling_thread(), - content->name, SrtpRequired()); - - if (!rtp_data_channel_) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - if (rtcp_dtls_transport) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } - return false; - } - - rtp_data_channel_->SignalRtcpMuxFullyActive.connect( - this, &WebRtcSession::DestroyRtcpTransport_n); - rtp_data_channel_->SignalDtlsSrtpSetupFailure.connect( - this, &WebRtcSession::OnDtlsSrtpSetupFailure); - rtp_data_channel_->SignalSentPacket.connect(this, - &WebRtcSession::OnSentPacket_w); - } - - pc_->OnDataChannelCreated(); - - return true; -} - -Call::Stats WebRtcSession::GetCallStats() { - if (!worker_thread()->IsCurrent()) { - return worker_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&WebRtcSession::GetCallStats, this)); - } - if (pc_->call_) { - return pc_->call_->GetStats(); - } else { - return Call::Stats(); - } -} - -std::unique_ptr WebRtcSession::GetSessionStats_n( - const ChannelNamePairs& channel_name_pairs) { - RTC_DCHECK(network_thread()->IsCurrent()); - std::unique_ptr session_stats(new SessionStats()); - for (const auto channel_name_pair : { &channel_name_pairs.voice, - &channel_name_pairs.video, - &channel_name_pairs.data }) { - if (*channel_name_pair) { - cricket::TransportStats transport_stats; - if (!transport_controller_->GetStats((*channel_name_pair)->transport_name, - &transport_stats)) { - return nullptr; - } - session_stats->proxy_to_transport[(*channel_name_pair)->content_name] = - (*channel_name_pair)->transport_name; - session_stats->transport_stats[(*channel_name_pair)->transport_name] = - std::move(transport_stats); - } - } - return session_stats; -} - -bool WebRtcSession::CreateSctpTransport_n(const std::string& content_name, - const std::string& transport_name) { - RTC_DCHECK(network_thread()->IsCurrent()); - RTC_DCHECK(sctp_factory_); - cricket::DtlsTransportInternal* tc = - transport_controller_->CreateDtlsTransport_n( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - sctp_transport_ = sctp_factory_->CreateSctpTransport(tc); - RTC_DCHECK(sctp_transport_); - sctp_invoker_.reset(new rtc::AsyncInvoker()); - sctp_transport_->SignalReadyToSendData.connect( - this, &WebRtcSession::OnSctpTransportReadyToSendData_n); - sctp_transport_->SignalDataReceived.connect( - this, &WebRtcSession::OnSctpTransportDataReceived_n); - sctp_transport_->SignalStreamClosedRemotely.connect( - this, &WebRtcSession::OnSctpStreamClosedRemotely_n); - sctp_transport_name_ = rtc::Optional(transport_name); - sctp_content_name_ = rtc::Optional(content_name); - return true; -} - -void WebRtcSession::ChangeSctpTransport_n(const std::string& transport_name) { - RTC_DCHECK(network_thread()->IsCurrent()); - RTC_DCHECK(sctp_transport_); - RTC_DCHECK(sctp_transport_name_); - std::string old_sctp_transport_name = *sctp_transport_name_; - sctp_transport_name_ = rtc::Optional(transport_name); - cricket::DtlsTransportInternal* tc = - transport_controller_->CreateDtlsTransport_n( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - sctp_transport_->SetTransportChannel(tc); - transport_controller_->DestroyDtlsTransport_n( - old_sctp_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); -} - -void WebRtcSession::DestroySctpTransport_n() { - RTC_DCHECK(network_thread()->IsCurrent()); - sctp_transport_.reset(nullptr); - sctp_content_name_.reset(); - sctp_transport_name_.reset(); - sctp_invoker_.reset(nullptr); - sctp_ready_to_send_data_ = false; -} - -void WebRtcSession::OnSctpTransportReadyToSendData_n() { - RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); - RTC_DCHECK(network_thread()->IsCurrent()); - sctp_invoker_->AsyncInvoke( - RTC_FROM_HERE, signaling_thread(), - rtc::Bind(&WebRtcSession::OnSctpTransportReadyToSendData_s, this, true)); -} - -void WebRtcSession::OnSctpTransportReadyToSendData_s(bool ready) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - sctp_ready_to_send_data_ = ready; - SignalSctpReadyToSendData(ready); -} - -void WebRtcSession::OnSctpTransportDataReceived_n( - const cricket::ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& payload) { - RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); - RTC_DCHECK(network_thread()->IsCurrent()); - sctp_invoker_->AsyncInvoke( - RTC_FROM_HERE, signaling_thread(), - rtc::Bind(&WebRtcSession::OnSctpTransportDataReceived_s, this, params, - payload)); -} - -void WebRtcSession::OnSctpTransportDataReceived_s( - const cricket::ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& payload) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - if (params.type == cricket::DMT_CONTROL && IsOpenMessage(payload)) { - // Received OPEN message; parse and signal that a new data channel should - // be created. - std::string label; - InternalDataChannelInit config; - config.id = params.ssrc; - if (!ParseDataChannelOpenMessage(payload, &label, &config)) { - LOG(LS_WARNING) << "Failed to parse the OPEN message for sid " - << params.ssrc; - return; - } - config.open_handshake_role = InternalDataChannelInit::kAcker; - pc_->OnDataChannelOpenMessage(label, config); - } else { - // Otherwise just forward the signal. - SignalSctpDataReceived(params, payload); - } -} - -void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) { - RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); - RTC_DCHECK(network_thread()->IsCurrent()); - sctp_invoker_->AsyncInvoke( - RTC_FROM_HERE, signaling_thread(), - rtc::Bind(&sigslot::signal1::operator(), - &SignalSctpStreamClosedRemotely, sid)); -} - -// Returns false if bundle is enabled and rtcp_mux is disabled. -bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) { - bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE); - if (!bundle_enabled) - return true; - - const cricket::ContentGroup* bundle_group = - desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - RTC_DCHECK(bundle_group != NULL); - - const cricket::ContentInfos& contents = desc->contents(); - for (cricket::ContentInfos::const_iterator citer = contents.begin(); - citer != contents.end(); ++citer) { - const cricket::ContentInfo* content = (&*citer); - RTC_DCHECK(content != NULL); - if (bundle_group->HasContentName(content->name) && - !content->rejected && content->type == cricket::NS_JINGLE_RTP) { - if (!HasRtcpMuxEnabled(content)) - return false; - } - } - // RTCP-MUX is enabled in all the contents. - return true; -} - -bool WebRtcSession::HasRtcpMuxEnabled( - const cricket::ContentInfo* content) { - const cricket::MediaContentDescription* description = - static_cast(content->description); - return description->rtcp_mux(); -} - -bool WebRtcSession::ValidateSessionDescription( - const SessionDescriptionInterface* sdesc, - cricket::ContentSource source, std::string* err_desc) { - std::string type; - if (error() != ERROR_NONE) { - return BadSdp(source, type, GetSessionErrorMsg(), err_desc); - } - - if (!sdesc || !sdesc->description()) { - return BadSdp(source, type, kInvalidSdp, err_desc); - } - - type = sdesc->type(); - Action action = GetAction(sdesc->type()); - if (source == cricket::CS_LOCAL) { - if (!ExpectSetLocalDescription(action)) - return BadLocalSdp(type, BadStateErrMsg(pc_->signaling_state()), - err_desc); - } else { - if (!ExpectSetRemoteDescription(action)) - return BadRemoteSdp(type, BadStateErrMsg(pc_->signaling_state()), - err_desc); - } - - // Verify crypto settings. - std::string crypto_error; - if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || - dtls_enabled_) && - !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) { - return BadSdp(source, type, crypto_error, err_desc); - } - - // Verify ice-ufrag and ice-pwd. - if (!VerifyIceUfragPwdPresent(sdesc->description())) { - return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc); - } - - if (!ValidateBundleSettings(sdesc->description())) { - return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc); - } - - // TODO(skvlad): When the local rtcp-mux policy is Require, reject any - // m-lines that do not rtcp-mux enabled. - - // Verify m-lines in Answer when compared against Offer. - if (action == kAnswer || action == kPrAnswer) { - const cricket::SessionDescription* offer_desc = - (source == cricket::CS_LOCAL) ? remote_description()->description() - : local_description()->description(); - if (!MediaSectionsHaveSameCount(offer_desc, sdesc->description()) || - !MediaSectionsInSameOrder(offer_desc, sdesc->description())) { - return BadAnswerSdp(source, kMlineMismatchInAnswer, err_desc); - } - } else { - const cricket::SessionDescription* current_desc = nullptr; - if (source == cricket::CS_LOCAL && local_description()) { - current_desc = local_description()->description(); - } else if (source == cricket::CS_REMOTE && remote_description()) { - current_desc = remote_description()->description(); - } - // The re-offers should respect the order of m= sections in current - // description. See RFC3264 Section 8 paragraph 4 for more details. - if (current_desc && - !MediaSectionsInSameOrder(current_desc, sdesc->description())) { - return BadOfferSdp(source, kMlineMismatchInSubsequentOffer, err_desc); - } - } - - return true; -} - -bool WebRtcSession::ExpectSetLocalDescription(Action action) { - PeerConnectionInterface::SignalingState state = pc_->signaling_state(); - if (action == kOffer) { - return (state == PeerConnectionInterface::kStable) || - (state == PeerConnectionInterface::kHaveLocalOffer); - } else { // Answer or PrAnswer - return (state == PeerConnectionInterface::kHaveRemoteOffer) || - (state == PeerConnectionInterface::kHaveLocalPrAnswer); - } -} - -bool WebRtcSession::ExpectSetRemoteDescription(Action action) { - PeerConnectionInterface::SignalingState state = pc_->signaling_state(); - if (action == kOffer) { - return (state == PeerConnectionInterface::kStable) || - (state == PeerConnectionInterface::kHaveRemoteOffer); - } else { // Answer or PrAnswer. - return (state == PeerConnectionInterface::kHaveLocalOffer) || - (state == PeerConnectionInterface::kHaveRemotePrAnswer); - } -} - -std::string WebRtcSession::GetSessionErrorMsg() { - std::ostringstream desc; - desc << kSessionError << GetErrorCodeString(error()) << ". "; - desc << kSessionErrorDesc << error_desc() << "."; - return desc.str(); -} - -// We need to check the local/remote description for the Transport instead of -// the session, because a new Transport added during renegotiation may have -// them unset while the session has them set from the previous negotiation. -// Not doing so may trigger the auto generation of transport description and -// mess up DTLS identity information, ICE credential, etc. -bool WebRtcSession::ReadyToUseRemoteCandidate( - const IceCandidateInterface* candidate, - const SessionDescriptionInterface* remote_desc, - bool* valid) { - *valid = true; - - const SessionDescriptionInterface* current_remote_desc = - remote_desc ? remote_desc : remote_description(); - - if (!current_remote_desc) { - return false; - } - - size_t mediacontent_index = - static_cast(candidate->sdp_mline_index()); - size_t remote_content_size = - current_remote_desc->description()->contents().size(); - if (mediacontent_index >= remote_content_size) { - LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate media index " - << mediacontent_index; - - *valid = false; - return false; - } - - cricket::ContentInfo content = - current_remote_desc->description()->contents()[mediacontent_index]; - - const std::string transport_name = GetTransportName(content.name); - if (transport_name.empty()) { - return false; - } - return transport_controller_->ReadyForRemoteCandidates(transport_name); -} - -bool WebRtcSession::SrtpRequired() const { - return dtls_enabled_ || - webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED; -} - -void WebRtcSession::OnTransportControllerGatheringState( - cricket::IceGatheringState state) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - if (state == cricket::kIceGatheringGathering) { - pc_->OnIceGatheringChange(PeerConnectionInterface::kIceGatheringGathering); - } else if (state == cricket::kIceGatheringComplete) { - pc_->OnIceGatheringChange(PeerConnectionInterface::kIceGatheringComplete); - } -} - -void WebRtcSession::ReportTransportStats() { - // Use a set so we don't report the same stats twice if two channels share - // a transport. - std::set transport_names; - if (voice_channel()) { - transport_names.insert(voice_channel()->transport_name()); - } - if (video_channel()) { - transport_names.insert(video_channel()->transport_name()); - } - if (rtp_data_channel()) { - transport_names.insert(rtp_data_channel()->transport_name()); - } - if (sctp_transport_name_) { - transport_names.insert(*sctp_transport_name_); - } - for (const auto& name : transport_names) { - cricket::TransportStats stats; - if (transport_controller_->GetStats(name, &stats)) { - ReportBestConnectionState(stats); - ReportNegotiatedCiphers(stats); - } - } -} -// Walk through the ConnectionInfos to gather best connection usage -// for IPv4 and IPv6. -void WebRtcSession::ReportBestConnectionState( - const cricket::TransportStats& stats) { - RTC_DCHECK(pc_->metrics_observer()); - for (cricket::TransportChannelStatsList::const_iterator it = - stats.channel_stats.begin(); - it != stats.channel_stats.end(); ++it) { - for (cricket::ConnectionInfos::const_iterator it_info = - it->connection_infos.begin(); - it_info != it->connection_infos.end(); ++it_info) { - if (!it_info->best_connection) { - continue; - } - - PeerConnectionEnumCounterType type = kPeerConnectionEnumCounterMax; - const cricket::Candidate& local = it_info->local_candidate; - const cricket::Candidate& remote = it_info->remote_candidate; - - // Increment the counter for IceCandidatePairType. - if (local.protocol() == cricket::TCP_PROTOCOL_NAME || - (local.type() == RELAY_PORT_TYPE && - local.relay_protocol() == cricket::TCP_PROTOCOL_NAME)) { - type = kEnumCounterIceCandidatePairTypeTcp; - } else if (local.protocol() == cricket::UDP_PROTOCOL_NAME) { - type = kEnumCounterIceCandidatePairTypeUdp; - } else { - RTC_CHECK(0); - } - pc_->metrics_observer()->IncrementEnumCounter( - type, GetIceCandidatePairCounter(local, remote), - kIceCandidatePairMax); - - // Increment the counter for IP type. - if (local.address().family() == AF_INET) { - pc_->metrics_observer()->IncrementEnumCounter( - kEnumCounterAddressFamily, kBestConnections_IPv4, - kPeerConnectionAddressFamilyCounter_Max); - - } else if (local.address().family() == AF_INET6) { - pc_->metrics_observer()->IncrementEnumCounter( - kEnumCounterAddressFamily, kBestConnections_IPv6, - kPeerConnectionAddressFamilyCounter_Max); - } else { - RTC_CHECK(0); - } - - return; - } - } -} - -void WebRtcSession::ReportNegotiatedCiphers( - const cricket::TransportStats& stats) { - RTC_DCHECK(pc_->metrics_observer()); - if (!dtls_enabled_ || stats.channel_stats.empty()) { - return; - } - - int srtp_crypto_suite = stats.channel_stats[0].srtp_crypto_suite; - int ssl_cipher_suite = stats.channel_stats[0].ssl_cipher_suite; - if (srtp_crypto_suite == rtc::SRTP_INVALID_CRYPTO_SUITE && - ssl_cipher_suite == rtc::TLS_NULL_WITH_NULL_NULL) { - return; - } - - PeerConnectionEnumCounterType srtp_counter_type; - PeerConnectionEnumCounterType ssl_counter_type; - if (stats.transport_name == cricket::CN_AUDIO) { - srtp_counter_type = kEnumCounterAudioSrtpCipher; - ssl_counter_type = kEnumCounterAudioSslCipher; - } else if (stats.transport_name == cricket::CN_VIDEO) { - srtp_counter_type = kEnumCounterVideoSrtpCipher; - ssl_counter_type = kEnumCounterVideoSslCipher; - } else if (stats.transport_name == cricket::CN_DATA) { - srtp_counter_type = kEnumCounterDataSrtpCipher; - ssl_counter_type = kEnumCounterDataSslCipher; - } else { - RTC_NOTREACHED(); - return; - } - - if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE) { - pc_->metrics_observer()->IncrementSparseEnumCounter(srtp_counter_type, - srtp_crypto_suite); - } - if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL) { - pc_->metrics_observer()->IncrementSparseEnumCounter(ssl_counter_type, - ssl_cipher_suite); - } -} - -void WebRtcSession::OnSentPacket_w(const rtc::SentPacket& sent_packet) { - RTC_DCHECK(worker_thread()->IsCurrent()); - RTC_DCHECK(pc_->call_); - pc_->call_->OnSentPacket(sent_packet); -} - -const std::string WebRtcSession::GetTransportName( - const std::string& content_name) { - cricket::BaseChannel* channel = GetChannel(content_name); - if (!channel) { - if (sctp_transport_) { - RTC_DCHECK(sctp_content_name_); - RTC_DCHECK(sctp_transport_name_); - if (content_name == *sctp_content_name_) { - return *sctp_transport_name_; - } - } - // Return an empty string if failed to retrieve the transport name. - return ""; - } - return channel->transport_name(); -} - -void WebRtcSession::DestroyRtcpTransport_n(const std::string& transport_name) { - RTC_DCHECK(network_thread()->IsCurrent()); - transport_controller_->DestroyDtlsTransport_n( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); -} - -void WebRtcSession::RemoveAndDestroyVideoChannel( - cricket::VideoChannel* video_channel) { - auto it = - std::find(video_channels_.begin(), video_channels_.end(), video_channel); - RTC_DCHECK(it != video_channels_.end()); - if (it == video_channels_.end()) { - return; - } - video_channels_.erase(it); - DestroyVideoChannel(video_channel); -} - -void WebRtcSession::DestroyVideoChannel(cricket::VideoChannel* video_channel) { - // TODO(steveanton): This should take an identifier for the video channel - // since we now support more than one. - pc_->OnVideoChannelDestroyed(); - RTC_DCHECK(video_channel->rtp_dtls_transport()); - const std::string transport_name = - video_channel->rtp_dtls_transport()->transport_name(); - const bool need_to_delete_rtcp = - (video_channel->rtcp_dtls_transport() != nullptr); - // The above need to be cached before destroying the video channel so that we - // do not access uninitialized memory. - pc_->channel_manager()->DestroyVideoChannel(video_channel); - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - if (need_to_delete_rtcp) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } -} - -void WebRtcSession::RemoveAndDestroyVoiceChannel( - cricket::VoiceChannel* voice_channel) { - auto it = - std::find(voice_channels_.begin(), voice_channels_.end(), voice_channel); - RTC_DCHECK(it != voice_channels_.end()); - if (it == voice_channels_.end()) { - return; - } - voice_channels_.erase(it); - DestroyVoiceChannel(voice_channel); -} - -void WebRtcSession::DestroyVoiceChannel(cricket::VoiceChannel* voice_channel) { - // TODO(steveanton): This should take an identifier for the voice channel - // since we now support more than one. - pc_->OnVoiceChannelDestroyed(); - RTC_DCHECK(voice_channel->rtp_dtls_transport()); - const std::string transport_name = - voice_channel->rtp_dtls_transport()->transport_name(); - const bool need_to_delete_rtcp = - (voice_channel->rtcp_dtls_transport() != nullptr); - // The above need to be cached before destroying the video channel so that we - // do not access uninitialized memory. - pc_->channel_manager()->DestroyVoiceChannel(voice_channel); - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - if (need_to_delete_rtcp) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } -} - -void WebRtcSession::DestroyDataChannel() { - pc_->OnDataChannelDestroyed(); - RTC_DCHECK(rtp_data_channel_->rtp_dtls_transport()); - std::string transport_name; - transport_name = rtp_data_channel_->rtp_dtls_transport()->transport_name(); - bool need_to_delete_rtcp = - (rtp_data_channel_->rtcp_dtls_transport() != nullptr); - pc_->channel_manager()->DestroyRtpDataChannel(rtp_data_channel_); - rtp_data_channel_ = nullptr; - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); - if (need_to_delete_rtcp) { - transport_controller_->DestroyDtlsTransport( - transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP); - } -} -} // namespace webrtc diff --git a/pc/webrtcsession.h b/pc/webrtcsession.h deleted file mode 100644 index 00c4a5c9d1..0000000000 --- a/pc/webrtcsession.h +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef PC_WEBRTCSESSION_H_ -#define PC_WEBRTCSESSION_H_ - -#include -#include -#include -#include -#include - -#include "api/candidate.h" -#include "api/optional.h" -#include "api/peerconnectioninterface.h" -#include "api/statstypes.h" -#include "call/call.h" -#include "pc/datachannel.h" -#include "pc/mediasession.h" -#include "pc/transportcontroller.h" -#include "rtc_base/constructormagic.h" -#include "rtc_base/sigslot.h" -#include "rtc_base/sslidentity.h" -#include "rtc_base/thread.h" - -namespace cricket { - -class ChannelManager; -class RtpDataChannel; -class SctpTransportInternal; -class SctpTransportInternalFactory; -class StatsReport; -class VideoChannel; -class VoiceChannel; - -} // namespace cricket - -namespace webrtc { - -class IceRestartAnswerLatch; -class JsepIceCandidate; -class MediaStreamSignaling; -class PeerConnection; -class RtcEventLog; -class WebRtcSessionDescriptionFactory; - -// Statistics for all the transports of the session. -// TODO(pthatcher): Think of a better name for this. We already have -// a TransportStats in transport.h. Perhaps TransportsStats? -struct SessionStats { - std::map proxy_to_transport; - std::map transport_stats; -}; - -struct ChannelNamePair { - ChannelNamePair( - const std::string& content_name, const std::string& transport_name) - : content_name(content_name), transport_name(transport_name) {} - std::string content_name; - std::string transport_name; -}; - -struct ChannelNamePairs { - rtc::Optional voice; - rtc::Optional video; - rtc::Optional data; -}; - -// A WebRtcSession manages general session state. This includes negotiation -// of both the application-level and network-level protocols: the former -// defines what will be sent and the latter defines how it will be sent. Each -// network-level protocol is represented by a Transport object. Each Transport -// participates in the network-level negotiation. The individual streams of -// packets are represented by TransportChannels. The application-level protocol -// is represented by SessionDecription objects. -class WebRtcSession : - public DataChannelProviderInterface, - public sigslot::has_slots<> { - public: - enum Error { - ERROR_NONE = 0, // no error - ERROR_CONTENT = 1, // channel errors in SetLocalContent/SetRemoteContent - ERROR_TRANSPORT = 2, // transport error of some kind - }; - - // |sctp_factory| may be null, in which case SCTP is treated as unsupported. - WebRtcSession( - PeerConnection* pc, // TODO(steveanton): Temporary. - std::unique_ptr transport_controller, - std::unique_ptr sctp_factory); - virtual ~WebRtcSession(); - - // These are const to allow them to be called from const methods. - rtc::Thread* network_thread() const; - rtc::Thread* worker_thread() const; - rtc::Thread* signaling_thread() const; - - // The ID of this session. - const std::string& session_id() const { return session_id_; } - - void Initialize( - const PeerConnectionFactoryInterface::Options& options, - std::unique_ptr cert_generator, - const PeerConnectionInterface::RTCConfiguration& rtc_configuration, - PeerConnection* pc); - // Deletes the voice, video and data channel and changes the session state - // to STATE_CLOSED. - void Close(); - - // Returns true if we were the initial offerer. - bool initial_offerer() const { return initial_offerer_ && *initial_offerer_; } - - // Returns the last error in the session. See the enum above for details. - Error error() const { return error_; } - const std::string& error_desc() const { return error_desc_; } - - // Exposed for stats collecting. - // TODO(steveanton): Switch callers to use the plural form and remove these. - virtual cricket::VoiceChannel* voice_channel() { - if (voice_channels_.empty()) { - return nullptr; - } else { - return voice_channels_[0]; - } - } - virtual cricket::VideoChannel* video_channel() { - if (video_channels_.empty()) { - return nullptr; - } else { - return video_channels_[0]; - } - } - - virtual std::vector voice_channels() const { - return voice_channels_; - } - virtual std::vector video_channels() const { - return video_channels_; - } - - // Only valid when using deprecated RTP data channels. - virtual cricket::RtpDataChannel* rtp_data_channel() { - return rtp_data_channel_; - } - virtual rtc::Optional sctp_content_name() const { - return sctp_content_name_; - } - virtual rtc::Optional sctp_transport_name() const { - return sctp_transport_name_; - } - - cricket::BaseChannel* GetChannel(const std::string& content_name); - - // Get current SSL role used by SCTP's underlying transport. - bool GetSctpSslRole(rtc::SSLRole* role); - // Get SSL role for an arbitrary m= section (handles bundling correctly). - // TODO(deadbeef): This is only used internally by the session description - // factory, it shouldn't really be public). - bool GetSslRole(const std::string& content_name, rtc::SSLRole* role); - - void CreateOffer( - CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - const cricket::MediaSessionOptions& session_options); - void CreateAnswer(CreateSessionDescriptionObserver* observer, - const cricket::MediaSessionOptions& session_options); - bool SetLocalDescription(std::unique_ptr desc, - std::string* err_desc); - bool SetRemoteDescription(std::unique_ptr desc, - std::string* err_desc); - - bool ProcessIceMessage(const IceCandidateInterface* ice_candidate); - - bool RemoveRemoteIceCandidates( - const std::vector& candidates); - - cricket::IceConfig ParseIceConfig( - const PeerConnectionInterface::RTCConfiguration& config) const; - - void SetIceConfig(const cricket::IceConfig& ice_config); - - // Start gathering candidates for any new transports, or transports doing an - // ICE restart. - void MaybeStartGathering(); - - const SessionDescriptionInterface* local_description() const { - return pending_local_description_ ? pending_local_description_.get() - : current_local_description_.get(); - } - const SessionDescriptionInterface* remote_description() const { - return pending_remote_description_ ? pending_remote_description_.get() - : current_remote_description_.get(); - } - const SessionDescriptionInterface* current_local_description() const { - return current_local_description_.get(); - } - const SessionDescriptionInterface* current_remote_description() const { - return current_remote_description_.get(); - } - const SessionDescriptionInterface* pending_local_description() const { - return pending_local_description_.get(); - } - const SessionDescriptionInterface* pending_remote_description() const { - return pending_remote_description_.get(); - } - - // Get the id used as a media stream track's "id" field from ssrc. - virtual bool GetLocalTrackIdBySsrc(uint32_t ssrc, std::string* track_id); - virtual bool GetRemoteTrackIdBySsrc(uint32_t ssrc, std::string* track_id); - - // Implements DataChannelProviderInterface. - bool SendData(const cricket::SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - cricket::SendDataResult* result) override; - bool ConnectDataChannel(DataChannel* webrtc_data_channel) override; - void DisconnectDataChannel(DataChannel* webrtc_data_channel) override; - void AddSctpDataStream(int sid) override; - void RemoveSctpDataStream(int sid) override; - bool ReadyToSendData() const override; - - virtual Call::Stats GetCallStats(); - - // Returns stats for all channels of all transports. - // This avoids exposing the internal structures used to track them. - // The parameterless version creates |ChannelNamePairs| from |voice_channel|, - // |video_channel| and |voice_channel| if available - this requires it to be - // called on the signaling thread - and invokes the other |GetStats|. The - // other |GetStats| can be invoked on any thread; if not invoked on the - // network thread a thread hop will happen. - std::unique_ptr GetSessionStats_s(); - virtual std::unique_ptr GetSessionStats( - const ChannelNamePairs& channel_name_pairs); - - // virtual so it can be mocked in unit tests - virtual bool GetLocalCertificate( - const std::string& transport_name, - rtc::scoped_refptr* certificate); - - // Caller owns returned certificate - virtual std::unique_ptr GetRemoteSSLCertificate( - const std::string& transport_name); - - cricket::DataChannelType data_channel_type() const; - - // Returns true if there was an ICE restart initiated by the remote offer. - bool IceRestartPending(const std::string& content_name) const; - - // Set the "needs-ice-restart" flag as described in JSEP. After the flag is - // set, offers should generate new ufrags/passwords until an ICE restart - // occurs. - void SetNeedsIceRestartFlag(); - // Returns true if the ICE restart flag above was set, and no ICE restart has - // occurred yet for this transport (by applying a local description with - // changed ufrag/password). If the transport has been deleted as a result of - // bundling, returns false. - bool NeedsIceRestart(const std::string& content_name) const; - - // Called when an RTCCertificate is generated or retrieved by - // WebRTCSessionDescriptionFactory. Should happen before setLocalDescription. - void OnCertificateReady( - const rtc::scoped_refptr& certificate); - void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp); - - cricket::TransportController* transport_controller() const { - return transport_controller_.get(); - } - - private: - // Indicates the type of SessionDescription in a call to SetLocalDescription - // and SetRemoteDescription. - enum Action { - kOffer, - kPrAnswer, - kAnswer, - }; - - // TODO(steveanton): Remove this once WebRtcSession and PeerConnection are - // merged. This is so that we can eliminate signals from this class and - // directly call the method in PeerConnection they are connected to. - PeerConnection* pc_; - - // Return all managed, non-null channels. - std::vector Channels() const; - - // Non-const versions of local_description()/remote_description(), for use - // internally. - SessionDescriptionInterface* mutable_local_description() { - return pending_local_description_ ? pending_local_description_.get() - : current_local_description_.get(); - } - SessionDescriptionInterface* mutable_remote_description() { - return pending_remote_description_ ? pending_remote_description_.get() - : current_remote_description_.get(); - } - - // Updates the error state, signaling if necessary. - void SetError(Error error, const std::string& error_desc); - - bool UpdateSessionState(Action action, cricket::ContentSource source, - std::string* err_desc); - Action GetAction(const std::string& type); - // Push the media parts of the local or remote session description - // down to all of the channels. - bool PushdownMediaDescription(cricket::ContentAction action, - cricket::ContentSource source, - std::string* error_desc); - bool PushdownSctpParameters_n(cricket::ContentSource source); - - bool PushdownTransportDescription(cricket::ContentSource source, - cricket::ContentAction action, - std::string* error_desc); - - // Helper methods to push local and remote transport descriptions. - bool PushdownLocalTransportDescription( - const cricket::SessionDescription* sdesc, - cricket::ContentAction action, - std::string* error_desc); - bool PushdownRemoteTransportDescription( - const cricket::SessionDescription* sdesc, - cricket::ContentAction action, - std::string* error_desc); - - // Returns true and the TransportInfo of the given |content_name| - // from |description|. Returns false if it's not available. - static bool GetTransportDescription( - const cricket::SessionDescription* description, - const std::string& content_name, - cricket::TransportDescription* info); - - // Returns the name of the transport channel when BUNDLE is enabled, or - // nullptr if the channel is not part of any bundle. - const std::string* GetBundleTransportName( - const cricket::ContentInfo* content, - const cricket::ContentGroup* bundle); - - // Cause all the BaseChannels in the bundle group to have the same - // transport channel. - bool EnableBundle(const cricket::ContentGroup& bundle); - - // Enables media channels to allow sending of media. - void EnableChannels(); - // Returns the media index for a local ice candidate given the content name. - // Returns false if the local session description does not have a media - // content called |content_name|. - bool GetLocalCandidateMediaIndex(const std::string& content_name, - int* sdp_mline_index); - // Uses all remote candidates in |remote_desc| in this session. - bool UseCandidatesInSessionDescription( - const SessionDescriptionInterface* remote_desc); - // Uses |candidate| in this session. - bool UseCandidate(const IceCandidateInterface* candidate); - // Deletes the corresponding channel of contents that don't exist in |desc|. - // |desc| can be null. This means that all channels are deleted. - void RemoveUnusedChannels(const cricket::SessionDescription* desc); - - // Allocates media channels based on the |desc|. If |desc| doesn't have - // the BUNDLE option, this method will disable BUNDLE in PortAllocator. - // This method will also delete any existing media channels before creating. - bool CreateChannels(const cricket::SessionDescription* desc); - - // Helper methods to create media channels. - bool CreateVoiceChannel(const cricket::ContentInfo* content, - const std::string* bundle_transport); - bool CreateVideoChannel(const cricket::ContentInfo* content, - const std::string* bundle_transport); - bool CreateDataChannel(const cricket::ContentInfo* content, - const std::string* bundle_transport); - - std::unique_ptr GetSessionStats_n( - const ChannelNamePairs& channel_name_pairs); - - bool CreateSctpTransport_n(const std::string& content_name, - const std::string& transport_name); - // For bundling. - void ChangeSctpTransport_n(const std::string& transport_name); - void DestroySctpTransport_n(); - // SctpTransport signal handlers. Needed to marshal signals from the network - // to signaling thread. - void OnSctpTransportReadyToSendData_n(); - // This may be called with "false" if the direction of the m= section causes - // us to tear down the SCTP connection. - void OnSctpTransportReadyToSendData_s(bool ready); - void OnSctpTransportDataReceived_n(const cricket::ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& payload); - // Beyond just firing the signal to the signaling thread, listens to SCTP - // CONTROL messages on unused SIDs and processes them as OPEN messages. - void OnSctpTransportDataReceived_s(const cricket::ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& payload); - void OnSctpStreamClosedRemotely_n(int sid); - - bool ValidateBundleSettings(const cricket::SessionDescription* desc); - bool HasRtcpMuxEnabled(const cricket::ContentInfo* content); - // Below methods are helper methods which verifies SDP. - bool ValidateSessionDescription(const SessionDescriptionInterface* sdesc, - cricket::ContentSource source, - std::string* err_desc); - - // Check if a call to SetLocalDescription is acceptable with |action|. - bool ExpectSetLocalDescription(Action action); - // Check if a call to SetRemoteDescription is acceptable with |action|. - bool ExpectSetRemoteDescription(Action action); - // Verifies a=setup attribute as per RFC 5763. - bool ValidateDtlsSetupAttribute(const cricket::SessionDescription* desc, - Action action); - - // Returns true if we are ready to push down the remote candidate. - // |remote_desc| is the new remote description, or NULL if the current remote - // description should be used. Output |valid| is true if the candidate media - // index is valid. - bool ReadyToUseRemoteCandidate(const IceCandidateInterface* candidate, - const SessionDescriptionInterface* remote_desc, - bool* valid); - - // Returns true if SRTP (either using DTLS-SRTP or SDES) is required by - // this session. - bool SrtpRequired() const; - - // TransportController signal handlers. - void OnTransportControllerConnectionState(cricket::IceConnectionState state); - void OnTransportControllerGatheringState(cricket::IceGatheringState state); - void OnTransportControllerCandidatesGathered( - const std::string& transport_name, - const std::vector& candidates); - void OnTransportControllerCandidatesRemoved( - const std::vector& candidates); - void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error); - - std::string GetSessionErrorMsg(); - - // Invoked when TransportController connection completion is signaled. - // Reports stats for all transports in use. - void ReportTransportStats(); - - // Gather the usage of IPv4/IPv6 as best connection. - void ReportBestConnectionState(const cricket::TransportStats& stats); - - void ReportNegotiatedCiphers(const cricket::TransportStats& stats); - - void OnSentPacket_w(const rtc::SentPacket& sent_packet); - - const std::string GetTransportName(const std::string& content_name); - - void DestroyRtcpTransport_n(const std::string& transport_name); - void RemoveAndDestroyVideoChannel(cricket::VideoChannel* video_channel); - void DestroyVideoChannel(cricket::VideoChannel* video_channel); - void RemoveAndDestroyVoiceChannel(cricket::VoiceChannel* voice_channel); - void DestroyVoiceChannel(cricket::VoiceChannel* voice_channel); - void DestroyDataChannel(); - - Error error_ = ERROR_NONE; - std::string error_desc_; - - const std::string session_id_; - rtc::Optional initial_offerer_; - - const std::unique_ptr transport_controller_; - const std::unique_ptr sctp_factory_; - // TODO(steveanton): voice_channels_ and video_channels_ used to be a single - // VoiceChannel/VideoChannel respectively but are being changed to support - // multiple m= lines in unified plan. But until more work is done, these can - // only have 0 or 1 channel each. - // These channels are owned by ChannelManager. - std::vector voice_channels_; - std::vector video_channels_; - // |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_| - // when using SCTP. - cricket::RtpDataChannel* rtp_data_channel_ = nullptr; - - std::unique_ptr sctp_transport_; - // |sctp_transport_name_| keeps track of what DTLS transport the SCTP - // transport is using (which can change due to bundling). - rtc::Optional sctp_transport_name_; - // |sctp_content_name_| is the content name (MID) in SDP. - rtc::Optional sctp_content_name_; - // Value cached on signaling thread. Only updated when SctpReadyToSendData - // fires on the signaling thread. - bool sctp_ready_to_send_data_ = false; - // Same as signals provided by SctpTransport, but these are guaranteed to - // fire on the signaling thread, whereas SctpTransport fires on the networking - // thread. - // |sctp_invoker_| is used so that any signals queued on the signaling thread - // from the network thread are immediately discarded if the SctpTransport is - // destroyed (due to m= section being rejected). - // TODO(deadbeef): Use a proxy object to ensure that method calls/signals - // are marshalled to the right thread. Could almost use proxy.h for this, - // but it doesn't have a mechanism for marshalling sigslot::signals - std::unique_ptr sctp_invoker_; - sigslot::signal1 SignalSctpReadyToSendData; - sigslot::signal2 - SignalSctpDataReceived; - sigslot::signal1 SignalSctpStreamClosedRemotely; - - std::unique_ptr current_local_description_; - std::unique_ptr pending_local_description_; - std::unique_ptr current_remote_description_; - std::unique_ptr pending_remote_description_; - bool dtls_enabled_; - // Specifies which kind of data channel is allowed. This is controlled - // by the chrome command-line flag and constraints: - // 1. If chrome command-line switch 'enable-sctp-data-channels' is enabled, - // constraint kEnableDtlsSrtp is true, and constaint kEnableRtpDataChannels is - // not set or false, SCTP is allowed (DCT_SCTP); - // 2. If constraint kEnableRtpDataChannels is true, RTP is allowed (DCT_RTP); - // 3. If both 1&2 are false, data channel is not allowed (DCT_NONE). - cricket::DataChannelType data_channel_type_; - // List of content names for which the remote side triggered an ICE restart. - std::set pending_ice_restarts_; - - std::unique_ptr webrtc_session_desc_factory_; - - // Member variables for caching global options. - cricket::AudioOptions audio_options_; - cricket::VideoOptions video_options_; - - RTC_DISALLOW_COPY_AND_ASSIGN(WebRtcSession); -}; -} // namespace webrtc - -#endif // PC_WEBRTCSESSION_H_