From 6796e211c1f8c600e6115cf6a87553a52a5f57fc Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 19 May 2020 14:31:54 +0400 Subject: [PATCH 1/8] Temp --- submodules/TgVoipWebrtcCustom/BUILD | 2 + .../Sources/RtcConnection.mm | 25 +- .../Sources/tg_jsep_transport.cpp | 868 ++ .../Sources/tg_jsep_transport.h | 417 + .../Sources/tg_jsep_transport_controller.cpp | 1697 ++++ .../Sources/tg_jsep_transport_controller.h | 478 ++ .../Sources/tg_peer_connection.cpp | 7234 +++++++++++++++++ .../Sources/tg_peer_connection.h | 1377 ++++ .../Sources/tg_peer_connection_factory.cpp | 406 + .../Sources/tg_peer_connection_factory.h | 138 + .../Sources/tg_rtp_data_engine.cpp | 338 + .../Sources/tg_rtp_data_engine.h | 109 + .../Sources/tg_rtp_transport.cpp | 292 + .../Sources/tg_rtp_transport.h | 133 + 14 files changed, 13510 insertions(+), 4 deletions(-) create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h diff --git a/submodules/TgVoipWebrtcCustom/BUILD b/submodules/TgVoipWebrtcCustom/BUILD index dd16734df7..5bbfd2e05f 100644 --- a/submodules/TgVoipWebrtcCustom/BUILD +++ b/submodules/TgVoipWebrtcCustom/BUILD @@ -7,6 +7,8 @@ objc_library( "Sources/**/*.m", "Sources/**/*.mm", "Sources/**/*.h", + "Sources/**/*.cpp", + "Sources/**/*.h", ]), hdrs = glob([ "PublicHeaders/**/*.h", diff --git a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm index 87280bb488..b279ccde2b 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm +++ b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm @@ -4,8 +4,9 @@ #include #include "api/scoped_refptr.h" +#include "api/proxy.h" +#include "api/peer_connection_factory_proxy.h" #include "rtc_base/thread.h" -#include "api/peer_connection_interface.h" #include "api/task_queue/default_task_queue_factory.h" #include "media/engine/webrtc_media_engine.h" #include "sdk/objc/native/api/audio_device_module.h" @@ -23,6 +24,9 @@ #include "sdk/objc/api/RTCVideoRendererAdapter.h" #include "sdk/objc/native/api/video_frame.h" +#include "tg_peer_connection.h" +#include "tg_peer_connection_factory.h" + #include "VideoCameraCapturer.h" #import "VideoMetalView.h" @@ -207,7 +211,7 @@ public: _networkThread = rtc::Thread::CreateWithSocketServer(); _networkThread->SetName("network_thread", _networkThread.get()); - BOOL result = _networkThread->Start(); + bool result = _networkThread->Start(); assert(result); _workerThread = rtc::Thread::Create(); @@ -239,7 +243,19 @@ public: std::make_unique(dependencies.task_queue_factory.get()); dependencies.network_controller_factory = nil; dependencies.media_transport_factory = nil; - _nativeFactory = webrtc::CreateModularPeerConnectionFactory(std::move(dependencies)); + + rtc::scoped_refptr pc_factory( + new rtc::RefCountedObject( + std::move(dependencies))); + // Call Initialize synchronously but make sure it is executed on + // |signaling_thread|. + webrtc::MethodCall call(pc_factory.get(), &webrtc::TgPeerConnectionFactory::Initialize); + result = call.Marshal(RTC_FROM_HERE, pc_factory->signaling_thread()); + + if (!result) { + return nil; + } + _nativeFactory = webrtc::PeerConnectionFactoryProxy::Create(pc_factory->signaling_thread(), pc_factory); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; @@ -263,6 +279,7 @@ public: //config.type = webrtc::PeerConnectionInterface::kRelay; _observer.reset(new PeerConnectionObserverImpl(_discoveredIceCandidate, _connectionStateChanged)); + _peerConnection = _nativeFactory->CreatePeerConnection(config, nullptr, nullptr, _observer.get()); assert(_peerConnection != nullptr); @@ -320,7 +337,7 @@ public: AVCaptureDeviceFormat *bestFormat = nil; for (AVCaptureDeviceFormat *format in sortedFormats) { CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - if (dimensions.width >= 600 || dimensions.height >= 600) { + if (dimensions.width >= 1000 || dimensions.height >= 1000) { bestFormat = format; break; } diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp new file mode 100644 index 0000000000..dbaaedc4b3 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp @@ -0,0 +1,868 @@ +/* + * Copyright 2018 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 "tg_jsep_transport.h" + +#include +#include + +#include +#include +#include // for std::pair + +#include "api/array_view.h" +#include "api/candidate.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/p2p_transport_channel.h" +#include "pc/sctp_data_channel_transport.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" + +using webrtc::SdpType; + +namespace cricket { + +static bool VerifyIceParams(const TgJsepTransportDescription& jsep_description) { + // For legacy protocols. + // TODO(zhihuang): Remove this once the legacy protocol is no longer + // supported. + if (jsep_description.transport_desc.ice_ufrag.empty() && + jsep_description.transport_desc.ice_pwd.empty()) { + return true; + } + + if (jsep_description.transport_desc.ice_ufrag.length() < + ICE_UFRAG_MIN_LENGTH || + jsep_description.transport_desc.ice_ufrag.length() > + ICE_UFRAG_MAX_LENGTH) { + return false; + } + if (jsep_description.transport_desc.ice_pwd.length() < ICE_PWD_MIN_LENGTH || + jsep_description.transport_desc.ice_pwd.length() > ICE_PWD_MAX_LENGTH) { + return false; + } + return true; +} + +TgJsepTransportDescription::TgJsepTransportDescription() {} + +TgJsepTransportDescription::TgJsepTransportDescription( + bool rtcp_mux_enabled, + const std::vector& cryptos, + const std::vector& encrypted_header_extension_ids, + int rtp_abs_sendtime_extn_id, + const TransportDescription& transport_desc, + absl::optional media_alt_protocol, + absl::optional data_alt_protocol) + : rtcp_mux_enabled(rtcp_mux_enabled), + cryptos(cryptos), + encrypted_header_extension_ids(encrypted_header_extension_ids), + rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id), + transport_desc(transport_desc), + media_alt_protocol(media_alt_protocol), + data_alt_protocol(data_alt_protocol) {} + +TgJsepTransportDescription::TgJsepTransportDescription( + const TgJsepTransportDescription& from) + : rtcp_mux_enabled(from.rtcp_mux_enabled), + cryptos(from.cryptos), + encrypted_header_extension_ids(from.encrypted_header_extension_ids), + rtp_abs_sendtime_extn_id(from.rtp_abs_sendtime_extn_id), + transport_desc(from.transport_desc), + media_alt_protocol(from.media_alt_protocol), + data_alt_protocol(from.data_alt_protocol) {} + +TgJsepTransportDescription::~TgJsepTransportDescription() = default; + +TgJsepTransportDescription& TgJsepTransportDescription::operator=( + const TgJsepTransportDescription& from) { + if (this == &from) { + return *this; + } + rtcp_mux_enabled = from.rtcp_mux_enabled; + cryptos = from.cryptos; + encrypted_header_extension_ids = from.encrypted_header_extension_ids; + rtp_abs_sendtime_extn_id = from.rtp_abs_sendtime_extn_id; + transport_desc = from.transport_desc; + media_alt_protocol = from.media_alt_protocol; + data_alt_protocol = from.data_alt_protocol; + + return *this; +} + +TgJsepTransport::TgJsepTransport( + const std::string& mid, + const rtc::scoped_refptr& local_certificate, + rtc::scoped_refptr ice_transport, + rtc::scoped_refptr rtcp_ice_transport, + std::unique_ptr unencrypted_rtp_transport, + std::unique_ptr sdes_transport, + std::unique_ptr dtls_srtp_transport, + std::unique_ptr datagram_rtp_transport, + std::unique_ptr rtp_dtls_transport, + std::unique_ptr rtcp_dtls_transport, + std::unique_ptr sctp_transport, + std::unique_ptr datagram_transport, + webrtc::DataChannelTransportInterface* data_channel_transport) + : network_thread_(rtc::Thread::Current()), + mid_(mid), + local_certificate_(local_certificate), + ice_transport_(std::move(ice_transport)), + rtcp_ice_transport_(std::move(rtcp_ice_transport)), + unencrypted_rtp_transport_(std::move(unencrypted_rtp_transport)), + sdes_transport_(std::move(sdes_transport)), + dtls_srtp_transport_(std::move(dtls_srtp_transport)), + rtp_dtls_transport_( + rtp_dtls_transport ? new rtc::RefCountedObject( + std::move(rtp_dtls_transport)) + : nullptr), + rtcp_dtls_transport_( + rtcp_dtls_transport + ? new rtc::RefCountedObject( + std::move(rtcp_dtls_transport)) + : nullptr), + sctp_data_channel_transport_( + sctp_transport ? std::make_unique( + sctp_transport.get()) + : nullptr), + sctp_transport_(sctp_transport + ? new rtc::RefCountedObject( + std::move(sctp_transport)) + : nullptr), + datagram_transport_(std::move(datagram_transport)), + datagram_rtp_transport_(std::move(datagram_rtp_transport)), + data_channel_transport_(data_channel_transport) { + RTC_DCHECK(ice_transport_); + RTC_DCHECK(rtp_dtls_transport_); + // |rtcp_ice_transport_| must be present iff |rtcp_dtls_transport_| is + // present. + RTC_DCHECK_EQ((rtcp_ice_transport_ != nullptr), + (rtcp_dtls_transport_ != nullptr)); + // Verify the "only one out of these three can be set" invariant. + if (unencrypted_rtp_transport_) { + RTC_DCHECK(!sdes_transport); + RTC_DCHECK(!dtls_srtp_transport); + } else if (sdes_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport); + RTC_DCHECK(!dtls_srtp_transport); + } else { + RTC_DCHECK(dtls_srtp_transport_); + RTC_DCHECK(!unencrypted_rtp_transport); + RTC_DCHECK(!sdes_transport); + } + + if (sctp_transport_) { + sctp_transport_->SetDtlsTransport(rtp_dtls_transport_); + } + + if (datagram_rtp_transport_ && default_rtp_transport()) { + composite_rtp_transport_ = std::make_unique( + std::vector{ + datagram_rtp_transport_.get(), default_rtp_transport()}); + } + + if (data_channel_transport_ && sctp_data_channel_transport_) { + composite_data_channel_transport_ = + std::make_unique( + std::vector{ + data_channel_transport_, sctp_data_channel_transport_.get()}); + } +} + +TgJsepTransport::~TgJsepTransport() { + if (sctp_transport_) { + sctp_transport_->Clear(); + } + + // Clear all DtlsTransports. There may be pointers to these from + // other places, so we can't assume they'll be deleted by the destructor. + rtp_dtls_transport_->Clear(); + if (rtcp_dtls_transport_) { + rtcp_dtls_transport_->Clear(); + } + + // ICE will be the last transport to be deleted. +} + +webrtc::RTCError TgJsepTransport::SetLocalJsepTransportDescription( + const TgJsepTransportDescription& jsep_description, + SdpType type) { + webrtc::RTCError error; + + RTC_DCHECK_RUN_ON(network_thread_); + if (!VerifyIceParams(jsep_description)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Invalid ice-ufrag or ice-pwd length."); + } + + if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type, + ContentSource::CS_LOCAL)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to setup RTCP mux."); + } + + // If doing SDES, setup the SDES crypto parameters. + { + rtc::CritScope scope(&accessor_lock_); + if (sdes_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport_); + RTC_DCHECK(!dtls_srtp_transport_); + if (!SetSdes(jsep_description.cryptos, + jsep_description.encrypted_header_extension_ids, type, + ContentSource::CS_LOCAL)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to setup SDES crypto parameters."); + } + } else if (dtls_srtp_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport_); + RTC_DCHECK(!sdes_transport_); + dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds( + jsep_description.encrypted_header_extension_ids); + } + } + bool ice_restarting = + local_description_ != nullptr && + IceCredentialsChanged(local_description_->transport_desc.ice_ufrag, + local_description_->transport_desc.ice_pwd, + jsep_description.transport_desc.ice_ufrag, + jsep_description.transport_desc.ice_pwd); + local_description_.reset(new TgJsepTransportDescription(jsep_description)); + + rtc::SSLFingerprint* local_fp = + local_description_->transport_desc.identity_fingerprint.get(); + + if (!local_fp) { + local_certificate_ = nullptr; + } else { + error = VerifyCertificateFingerprint(local_certificate_, local_fp); + if (!error.ok()) { + local_description_.reset(); + return error; + } + } + { + rtc::CritScope scope(&accessor_lock_); + RTC_DCHECK(rtp_dtls_transport_->internal()); + SetLocalIceParameters(rtp_dtls_transport_->internal()->ice_transport()); + + if (rtcp_dtls_transport_) { + RTC_DCHECK(rtcp_dtls_transport_->internal()); + SetLocalIceParameters(rtcp_dtls_transport_->internal()->ice_transport()); + } + } + // If PRANSWER/ANSWER is set, we should decide transport protocol type. + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + error = NegotiateAndSetDtlsParameters(type); + NegotiateDatagramTransport(type); + } + if (!error.ok()) { + local_description_.reset(); + return error; + } + { + rtc::CritScope scope(&accessor_lock_); + if (needs_ice_restart_ && ice_restarting) { + needs_ice_restart_ = false; + RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag cleared for transport " + << mid(); + } + } + + return webrtc::RTCError::OK(); +} + +webrtc::RTCError TgJsepTransport::SetRemoteJsepTransportDescription( + const TgJsepTransportDescription& jsep_description, + webrtc::SdpType type) { + webrtc::RTCError error; + + RTC_DCHECK_RUN_ON(network_thread_); + if (!VerifyIceParams(jsep_description)) { + remote_description_.reset(); + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Invalid ice-ufrag or ice-pwd length."); + } + + if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type, + ContentSource::CS_REMOTE)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to setup RTCP mux."); + } + + // If doing SDES, setup the SDES crypto parameters. + { + rtc::CritScope lock(&accessor_lock_); + if (sdes_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport_); + RTC_DCHECK(!dtls_srtp_transport_); + if (!SetSdes(jsep_description.cryptos, + jsep_description.encrypted_header_extension_ids, type, + ContentSource::CS_REMOTE)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to setup SDES crypto parameters."); + } + sdes_transport_->CacheRtpAbsSendTimeHeaderExtension( + jsep_description.rtp_abs_sendtime_extn_id); + } else if (dtls_srtp_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport_); + RTC_DCHECK(!sdes_transport_); + dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds( + jsep_description.encrypted_header_extension_ids); + dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension( + jsep_description.rtp_abs_sendtime_extn_id); + } + } + + remote_description_.reset(new TgJsepTransportDescription(jsep_description)); + RTC_DCHECK(rtp_dtls_transport()); + SetRemoteIceParameters(rtp_dtls_transport()->ice_transport()); + + if (rtcp_dtls_transport()) { + SetRemoteIceParameters(rtcp_dtls_transport()->ice_transport()); + } + + // If PRANSWER/ANSWER is set, we should decide transport protocol type. + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + error = NegotiateAndSetDtlsParameters(SdpType::kOffer); + NegotiateDatagramTransport(type); + } + if (!error.ok()) { + remote_description_.reset(); + return error; + } + return webrtc::RTCError::OK(); +} + +webrtc::RTCError TgJsepTransport::AddRemoteCandidates( + const Candidates& candidates) { + RTC_DCHECK_RUN_ON(network_thread_); + if (!local_description_ || !remote_description_) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE, + mid() + + " is not ready to use the remote candidate " + "because the local or remote description is " + "not set."); + } + + for (const cricket::Candidate& candidate : candidates) { + auto transport = + candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP + ? rtp_dtls_transport_ + : rtcp_dtls_transport_; + if (!transport) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Candidate has an unknown component: " + + candidate.ToSensitiveString() + " for mid " + + mid()); + } + RTC_DCHECK(transport->internal() && transport->internal()->ice_transport()); + transport->internal()->ice_transport()->AddRemoteCandidate(candidate); + } + return webrtc::RTCError::OK(); +} + +void TgJsepTransport::SetNeedsIceRestartFlag() { + rtc::CritScope scope(&accessor_lock_); + if (!needs_ice_restart_) { + needs_ice_restart_ = true; + RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag set for transport " << mid(); + } +} + +absl::optional TgJsepTransport::GetDtlsRole() const { + RTC_DCHECK_RUN_ON(network_thread_); + rtc::CritScope scope(&accessor_lock_); + RTC_DCHECK(rtp_dtls_transport_); + RTC_DCHECK(rtp_dtls_transport_->internal()); + rtc::SSLRole dtls_role; + if (!rtp_dtls_transport_->internal()->GetDtlsRole(&dtls_role)) { + return absl::optional(); + } + + return absl::optional(dtls_role); +} + +absl::optional +TgJsepTransport::GetTransportParameters() const { + rtc::CritScope scope(&accessor_lock_); + if (!datagram_transport()) { + return absl::nullopt; + } + + OpaqueTransportParameters params; + params.parameters = datagram_transport()->GetTransportParameters(); + return params; +} + +bool TgJsepTransport::GetStats(TransportStats* stats) { + RTC_DCHECK_RUN_ON(network_thread_); + rtc::CritScope scope(&accessor_lock_); + stats->transport_name = mid(); + stats->channel_stats.clear(); + RTC_DCHECK(rtp_dtls_transport_->internal()); + bool ret = GetTransportStats(rtp_dtls_transport_->internal(), stats); + if (rtcp_dtls_transport_) { + RTC_DCHECK(rtcp_dtls_transport_->internal()); + ret &= GetTransportStats(rtcp_dtls_transport_->internal(), stats); + } + return ret; +} + +webrtc::RTCError TgJsepTransport::VerifyCertificateFingerprint( + const rtc::RTCCertificate* certificate, + const rtc::SSLFingerprint* fingerprint) const { + RTC_DCHECK_RUN_ON(network_thread_); + if (!fingerprint) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "No fingerprint"); + } + if (!certificate) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Fingerprint provided but no identity available."); + } + std::unique_ptr fp_tmp = + rtc::SSLFingerprint::CreateUnique(fingerprint->algorithm, + *certificate->identity()); + RTC_DCHECK(fp_tmp.get() != NULL); + if (*fp_tmp == *fingerprint) { + return webrtc::RTCError::OK(); + } + char ss_buf[1024]; + rtc::SimpleStringBuilder desc(ss_buf); + desc << "Local fingerprint does not match identity. Expected: "; + desc << fp_tmp->ToString(); + desc << " Got: " << fingerprint->ToString(); + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + std::string(desc.str())); +} + +void TgJsepTransport::SetActiveResetSrtpParams(bool active_reset_srtp_params) { + RTC_DCHECK_RUN_ON(network_thread_); + rtc::CritScope scope(&accessor_lock_); + if (dtls_srtp_transport_) { + RTC_LOG(INFO) + << "Setting active_reset_srtp_params of DtlsSrtpTransport to: " + << active_reset_srtp_params; + dtls_srtp_transport_->SetActiveResetSrtpParams(active_reset_srtp_params); + } +} + +void TgJsepTransport::SetLocalIceParameters(IceTransportInternal* ice_transport) { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK(ice_transport); + RTC_DCHECK(local_description_); + ice_transport->SetIceParameters( + local_description_->transport_desc.GetIceParameters()); +} + +void TgJsepTransport::SetRemoteIceParameters( + IceTransportInternal* ice_transport) { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK(ice_transport); + RTC_DCHECK(remote_description_); + ice_transport->SetRemoteIceParameters( + remote_description_->transport_desc.GetIceParameters()); + ice_transport->SetRemoteIceMode(remote_description_->transport_desc.ice_mode); +} + +webrtc::RTCError TgJsepTransport::SetNegotiatedDtlsParameters( + DtlsTransportInternal* dtls_transport, + absl::optional dtls_role, + rtc::SSLFingerprint* remote_fingerprint) { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK(dtls_transport); + // Set SSL role. Role must be set before fingerprint is applied, which + // initiates DTLS setup. + if (dtls_role && !dtls_transport->SetDtlsRole(*dtls_role)) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to set SSL role for the transport."); + } + // Apply remote fingerprint. + if (!remote_fingerprint || + !dtls_transport->SetRemoteFingerprint( + remote_fingerprint->algorithm, remote_fingerprint->digest.cdata(), + remote_fingerprint->digest.size())) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "Failed to apply remote fingerprint."); + } + return webrtc::RTCError::OK(); +} + +bool TgJsepTransport::SetRtcpMux(bool enable, + webrtc::SdpType type, + ContentSource source) { + RTC_DCHECK_RUN_ON(network_thread_); + bool ret = false; + switch (type) { + case SdpType::kOffer: + ret = rtcp_mux_negotiator_.SetOffer(enable, source); + break; + case SdpType::kPrAnswer: + // This may activate RTCP muxing, but we don't yet destroy the transport + // because the final answer may deactivate it. + ret = rtcp_mux_negotiator_.SetProvisionalAnswer(enable, source); + break; + case SdpType::kAnswer: + ret = rtcp_mux_negotiator_.SetAnswer(enable, source); + if (ret && rtcp_mux_negotiator_.IsActive()) { + ActivateRtcpMux(); + } + break; + default: + RTC_NOTREACHED(); + } + + if (!ret) { + return false; + } + + auto transport = rtp_transport(); + transport->SetRtcpMuxEnabled(rtcp_mux_negotiator_.IsActive()); + return ret; +} + +void TgJsepTransport::ActivateRtcpMux() { + { + // Don't hold the network_thread_ lock while calling other functions, + // since they might call other functions that call RTC_DCHECK_RUN_ON. + // TODO(https://crbug.com/webrtc/10318): Simplify when possible. + RTC_DCHECK_RUN_ON(network_thread_); + } + { + rtc::CritScope scope(&accessor_lock_); + if (unencrypted_rtp_transport_) { + RTC_DCHECK(!sdes_transport_); + RTC_DCHECK(!dtls_srtp_transport_); + unencrypted_rtp_transport_->SetRtcpPacketTransport(nullptr); + } else if (sdes_transport_) { + RTC_DCHECK(!unencrypted_rtp_transport_); + RTC_DCHECK(!dtls_srtp_transport_); + sdes_transport_->SetRtcpPacketTransport(nullptr); + } else if (dtls_srtp_transport_) { + RTC_DCHECK(dtls_srtp_transport_); + RTC_DCHECK(!unencrypted_rtp_transport_); + RTC_DCHECK(!sdes_transport_); + dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport(), + /*rtcp_dtls_transport=*/nullptr); + } + rtcp_dtls_transport_ = nullptr; // Destroy this reference. + } + // Notify the JsepTransportController to update the aggregate states. + SignalRtcpMuxActive(); +} + +bool TgJsepTransport::SetSdes(const std::vector& cryptos, + const std::vector& encrypted_extension_ids, + webrtc::SdpType type, + ContentSource source) { + RTC_DCHECK_RUN_ON(network_thread_); + rtc::CritScope scope(&accessor_lock_); + bool ret = false; + ret = sdes_negotiator_.Process(cryptos, type, source); + if (!ret) { + return ret; + } + + if (source == ContentSource::CS_LOCAL) { + recv_extension_ids_ = encrypted_extension_ids; + } else { + send_extension_ids_ = encrypted_extension_ids; + } + + // If setting an SDES answer succeeded, apply the negotiated parameters + // to the SRTP transport. + if ((type == SdpType::kPrAnswer || type == SdpType::kAnswer) && ret) { + if (sdes_negotiator_.send_cipher_suite() && + sdes_negotiator_.recv_cipher_suite()) { + RTC_DCHECK(send_extension_ids_); + RTC_DCHECK(recv_extension_ids_); + ret = sdes_transport_->SetRtpParams( + *(sdes_negotiator_.send_cipher_suite()), + sdes_negotiator_.send_key().data(), + static_cast(sdes_negotiator_.send_key().size()), + *(send_extension_ids_), *(sdes_negotiator_.recv_cipher_suite()), + sdes_negotiator_.recv_key().data(), + static_cast(sdes_negotiator_.recv_key().size()), + *(recv_extension_ids_)); + } else { + RTC_LOG(LS_INFO) << "No crypto keys are provided for SDES."; + if (type == SdpType::kAnswer) { + // Explicitly reset the |sdes_transport_| if no crypto param is + // provided in the answer. No need to call |ResetParams()| for + // |sdes_negotiator_| because it resets the params inside |SetAnswer|. + sdes_transport_->ResetParams(); + } + } + } + return ret; +} + +webrtc::RTCError TgJsepTransport::NegotiateAndSetDtlsParameters( + SdpType local_description_type) { + RTC_DCHECK_RUN_ON(network_thread_); + if (!local_description_ || !remote_description_) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE, + "Applying an answer transport description " + "without applying any offer."); + } + std::unique_ptr remote_fingerprint; + absl::optional negotiated_dtls_role; + + rtc::SSLFingerprint* local_fp = + local_description_->transport_desc.identity_fingerprint.get(); + rtc::SSLFingerprint* remote_fp = + remote_description_->transport_desc.identity_fingerprint.get(); + if (remote_fp && local_fp) { + remote_fingerprint = std::make_unique(*remote_fp); + webrtc::RTCError error = + NegotiateDtlsRole(local_description_type, + local_description_->transport_desc.connection_role, + remote_description_->transport_desc.connection_role, + &negotiated_dtls_role); + if (!error.ok()) { + return error; + } + } else if (local_fp && (local_description_type == SdpType::kAnswer)) { + return webrtc::RTCError( + webrtc::RTCErrorType::INVALID_PARAMETER, + "Local fingerprint supplied when caller didn't offer DTLS."); + } else { + // We are not doing DTLS + remote_fingerprint = std::make_unique( + "", rtc::ArrayView()); + } + // Now that we have negotiated everything, push it downward. + // Note that we cache the result so that if we have race conditions + // between future SetRemote/SetLocal invocations and new transport + // creation, we have the negotiation state saved until a new + // negotiation happens. + RTC_DCHECK(rtp_dtls_transport()); + webrtc::RTCError error = SetNegotiatedDtlsParameters( + rtp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get()); + if (!error.ok()) { + return error; + } + + if (rtcp_dtls_transport()) { + error = SetNegotiatedDtlsParameters( + rtcp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get()); + } + return error; +} + +webrtc::RTCError TgJsepTransport::NegotiateDtlsRole( + SdpType local_description_type, + ConnectionRole local_connection_role, + ConnectionRole remote_connection_role, + absl::optional* negotiated_dtls_role) { + // From RFC 4145, section-4.1, The following are the values that the + // 'setup' attribute can take in an offer/answer exchange: + // Offer Answer + // ________________ + // active passive / holdconn + // passive active / holdconn + // actpass active / passive / holdconn + // holdconn holdconn + // + // Set the role that is most conformant with RFC 5763, Section 5, bullet 1 + // The endpoint MUST use the setup attribute defined in [RFC4145]. + // The endpoint that is the offerer MUST use the setup attribute + // value of setup:actpass and be prepared to receive a client_hello + // before it receives the answer. The answerer MUST use either a + // setup attribute value of setup:active or setup:passive. Note that + // if the answerer uses setup:passive, then the DTLS handshake will + // not begin until the answerer is received, which adds additional + // latency. setup:active allows the answer and the DTLS handshake to + // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever + // party is active MUST initiate a DTLS handshake by sending a + // ClientHello over each flow (host/port quartet). + // IOW - actpass and passive modes should be treated as server and + // active as client. + bool is_remote_server = false; + if (local_description_type == SdpType::kOffer) { + if (local_connection_role != CONNECTIONROLE_ACTPASS) { + return webrtc::RTCError( + webrtc::RTCErrorType::INVALID_PARAMETER, + "Offerer must use actpass value for setup attribute."); + } + + if (remote_connection_role == CONNECTIONROLE_ACTIVE || + remote_connection_role == CONNECTIONROLE_PASSIVE || + remote_connection_role == CONNECTIONROLE_NONE) { + is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE); + } else { + return webrtc::RTCError( + webrtc::RTCErrorType::INVALID_PARAMETER, + "Answerer must use either active or passive value " + "for setup attribute."); + } + // If remote is NONE or ACTIVE it will act as client. + } else { + if (remote_connection_role != CONNECTIONROLE_ACTPASS && + remote_connection_role != CONNECTIONROLE_NONE) { + // Accept a remote role attribute that's not "actpass", but matches the + // current negotiated role. This is allowed by dtls-sdp, though our + // implementation will never generate such an offer as it's not + // recommended. + // + // See https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-dtls-sdp, + // section 5.5. + auto current_dtls_role = GetDtlsRole(); + if (!current_dtls_role || + (*current_dtls_role == rtc::SSL_CLIENT && + remote_connection_role == CONNECTIONROLE_ACTIVE) || + (*current_dtls_role == rtc::SSL_SERVER && + remote_connection_role == CONNECTIONROLE_PASSIVE)) { + return webrtc::RTCError( + webrtc::RTCErrorType::INVALID_PARAMETER, + "Offerer must use actpass value or current negotiated role for " + "setup attribute."); + } + } + + if (local_connection_role == CONNECTIONROLE_ACTIVE || + local_connection_role == CONNECTIONROLE_PASSIVE) { + is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE); + } else { + return webrtc::RTCError( + webrtc::RTCErrorType::INVALID_PARAMETER, + "Answerer must use either active or passive value " + "for setup attribute."); + } + + // If local is passive, local will act as server. + } + + *negotiated_dtls_role = + (is_remote_server ? rtc::SSL_CLIENT : rtc::SSL_SERVER); + return webrtc::RTCError::OK(); +} + +bool TgJsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport, + TransportStats* stats) { + RTC_DCHECK_RUN_ON(network_thread_); + rtc::CritScope scope(&accessor_lock_); + RTC_DCHECK(dtls_transport); + TransportChannelStats substats; + if (rtcp_dtls_transport_) { + substats.component = dtls_transport == rtcp_dtls_transport_->internal() + ? ICE_CANDIDATE_COMPONENT_RTCP + : ICE_CANDIDATE_COMPONENT_RTP; + } else { + substats.component = ICE_CANDIDATE_COMPONENT_RTP; + } + dtls_transport->GetSslVersionBytes(&substats.ssl_version_bytes); + dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite); + dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite); + substats.dtls_state = dtls_transport->dtls_state(); + if (!dtls_transport->ice_transport()->GetStats( + &substats.ice_transport_stats)) { + return false; + } + stats->channel_stats.push_back(substats); + return true; +} + +void TgJsepTransport::NegotiateDatagramTransport(SdpType type) { + RTC_DCHECK(type == SdpType::kAnswer || type == SdpType::kPrAnswer); + rtc::CritScope lock(&accessor_lock_); + if (!datagram_transport_) { + return; // No need to negotiate the use of datagram transport. + } + + bool compatible_datagram_transport = + remote_description_->transport_desc.opaque_parameters && + remote_description_->transport_desc.opaque_parameters == + local_description_->transport_desc.opaque_parameters; + + bool use_datagram_transport_for_media = + compatible_datagram_transport && + remote_description_->media_alt_protocol == + remote_description_->transport_desc.opaque_parameters->protocol && + remote_description_->media_alt_protocol == + local_description_->media_alt_protocol; + + bool use_datagram_transport_for_data = + compatible_datagram_transport && + remote_description_->data_alt_protocol == + remote_description_->transport_desc.opaque_parameters->protocol && + remote_description_->data_alt_protocol == + local_description_->data_alt_protocol; + + RTC_LOG(LS_INFO) + << "Negotiating datagram transport, use_datagram_transport_for_media=" + << use_datagram_transport_for_media + << ", use_datagram_transport_for_data=" << use_datagram_transport_for_data + << " answer type=" << (type == SdpType::kAnswer ? "answer" : "pr_answer"); + + // A provisional or full or answer lets the peer start sending on one of the + // transports. + if (composite_rtp_transport_) { + composite_rtp_transport_->SetSendTransport( + use_datagram_transport_for_media ? datagram_rtp_transport_.get() + : default_rtp_transport()); + } + if (composite_data_channel_transport_) { + composite_data_channel_transport_->SetSendTransport( + use_datagram_transport_for_data ? data_channel_transport_ + : sctp_data_channel_transport_.get()); + } + + if (type != SdpType::kAnswer) { + return; + } + + if (composite_rtp_transport_) { + if (use_datagram_transport_for_media) { + // Negotiated use of datagram transport for RTP, so remove the + // non-datagram RTP transport. + composite_rtp_transport_->RemoveTransport(default_rtp_transport()); + if (unencrypted_rtp_transport_) { + unencrypted_rtp_transport_ = nullptr; + } else if (sdes_transport_) { + sdes_transport_ = nullptr; + } else { + dtls_srtp_transport_ = nullptr; + } + } else { + composite_rtp_transport_->RemoveTransport(datagram_rtp_transport_.get()); + datagram_rtp_transport_ = nullptr; + } + } + + if (composite_data_channel_transport_) { + if (use_datagram_transport_for_data) { + // Negotiated use of datagram transport for data channels, so remove the + // non-datagram data channel transport. + composite_data_channel_transport_->RemoveTransport( + sctp_data_channel_transport_.get()); + sctp_data_channel_transport_ = nullptr; + sctp_transport_ = nullptr; + } else { + composite_data_channel_transport_->RemoveTransport( + data_channel_transport_); + data_channel_transport_ = nullptr; + } + } else if (data_channel_transport_ && !use_datagram_transport_for_data) { + // The datagram transport has been rejected without a fallback. We still + // need to inform the application and delete it. + SignalDataChannelTransportNegotiated(this, nullptr); + data_channel_transport_ = nullptr; + } + + if (!use_datagram_transport_for_media && !use_datagram_transport_for_data) { + // Datagram transport is not being used for anything, so clean it up. + datagram_transport_ = nullptr; + } +} + +} // namespace cricket diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h new file mode 100644 index 0000000000..9959f0346a --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h @@ -0,0 +1,417 @@ +/* + * Copyright 2018 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 TG_PC_JSEP_TRANSPORT_H_ +#define TG_PC_JSEP_TRANSPORT_H_ + +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/candidate.h" +#include "api/ice_transport_interface.h" +#include "api/jsep.h" +#include "api/transport/datagram_transport_interface.h" +#include "media/sctp/sctp_transport_internal.h" +#include "p2p/base/dtls_transport.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/transport_info.h" +#include "pc/composite_data_channel_transport.h" +#include "pc/composite_rtp_transport.h" +#include "pc/dtls_srtp_transport.h" +#include "pc/dtls_transport.h" +#include "pc/rtcp_mux_filter.h" +#include "pc/rtp_transport.h" +#include "pc/sctp_transport.h" +#include "pc/session_description.h" +#include "pc/srtp_filter.h" +#include "pc/srtp_transport.h" +#include "pc/transport_stats.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/message_queue.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread_checker.h" + +#include "tg_rtp_transport.h" + +namespace cricket { + +class DtlsTransportInternal; + +struct TgJsepTransportDescription { + public: + TgJsepTransportDescription(); + TgJsepTransportDescription( + bool rtcp_mux_enabled, + const std::vector& cryptos, + const std::vector& encrypted_header_extension_ids, + int rtp_abs_sendtime_extn_id, + const TransportDescription& transport_description, + absl::optional media_alt_protocol, + absl::optional data_alt_protocol); + TgJsepTransportDescription(const TgJsepTransportDescription& from); + ~TgJsepTransportDescription(); + + TgJsepTransportDescription& operator=(const TgJsepTransportDescription& from); + + bool rtcp_mux_enabled = true; + std::vector cryptos; + std::vector encrypted_header_extension_ids; + int rtp_abs_sendtime_extn_id = -1; + // TODO(zhihuang): Add the ICE and DTLS related variables and methods from + // TransportDescription and remove this extra layer of abstraction. + TransportDescription transport_desc; + + // Alt-protocols that apply to this TgJsepTransport. Presence indicates a + // request to use an alternative protocol for media and/or data. The + // alt-protocol is handled by a datagram transport. If one or both of these + // values are present, TgJsepTransport will attempt to negotiate use of the + // datagram transport for media and/or data. + absl::optional media_alt_protocol; + absl::optional data_alt_protocol; +}; + +// Helper class used by TgJsepTransportController that processes +// TransportDescriptions. A TransportDescription represents the +// transport-specific properties of an SDP m= section, processed according to +// JSEP. Each transport consists of DTLS and ICE transport channels for RTP +// (and possibly RTCP, if rtcp-mux isn't used). +// +// On Threading: TgJsepTransport performs work solely on the network thread, and +// so its methods should only be called on the network thread. +class TgJsepTransport : public sigslot::has_slots<> { + public: + // |mid| is just used for log statements in order to identify the Transport. + // Note that |local_certificate| is allowed to be null since a remote + // description may be set before a local certificate is generated. + TgJsepTransport( + const std::string& mid, + const rtc::scoped_refptr& local_certificate, + rtc::scoped_refptr ice_transport, + rtc::scoped_refptr rtcp_ice_transport, + std::unique_ptr unencrypted_rtp_transport, + std::unique_ptr sdes_transport, + std::unique_ptr dtls_srtp_transport, + std::unique_ptr datagram_rtp_transport, + std::unique_ptr rtp_dtls_transport, + std::unique_ptr rtcp_dtls_transport, + std::unique_ptr sctp_transport, + std::unique_ptr datagram_transport, + webrtc::DataChannelTransportInterface* data_channel_transport); + + ~TgJsepTransport() override; + + // Returns the MID of this transport. This is only used for logging. + const std::string& mid() const { return mid_; } + + // Must be called before applying local session description. + // Needed in order to verify the local fingerprint. + void SetLocalCertificate( + const rtc::scoped_refptr& local_certificate) { + RTC_DCHECK_RUN_ON(network_thread_); + local_certificate_ = local_certificate; + } + + // Return the local certificate provided by SetLocalCertificate. + rtc::scoped_refptr GetLocalCertificate() const { + RTC_DCHECK_RUN_ON(network_thread_); + return local_certificate_; + } + + webrtc::RTCError SetLocalJsepTransportDescription( + const TgJsepTransportDescription& jsep_description, + webrtc::SdpType type); + + // Set the remote TransportDescription to be used by DTLS and ICE channels + // that are part of this Transport. + webrtc::RTCError SetRemoteJsepTransportDescription( + const TgJsepTransportDescription& jsep_description, + webrtc::SdpType type); + webrtc::RTCError AddRemoteCandidates(const Candidates& candidates); + + // 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. + // + // This and the below method can be called safely from any thread as long as + // SetXTransportDescription is not in progress. + 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). + bool needs_ice_restart() const { + rtc::CritScope scope(&accessor_lock_); + return needs_ice_restart_; + } + + // Returns role if negotiated, or empty absl::optional if it hasn't been + // negotiated yet. + absl::optional GetDtlsRole() const; + + absl::optional GetTransportParameters() const; + + // TODO(deadbeef): Make this const. See comment in transportcontroller.h. + bool GetStats(TransportStats* stats); + + const TgJsepTransportDescription* local_description() const { + RTC_DCHECK_RUN_ON(network_thread_); + return local_description_.get(); + } + + const TgJsepTransportDescription* remote_description() const { + RTC_DCHECK_RUN_ON(network_thread_); + return remote_description_.get(); + } + + webrtc::RtpTransportInternal* rtp_transport() const { + rtc::CritScope scope(&accessor_lock_); + if (composite_rtp_transport_) { + return composite_rtp_transport_.get(); + } else if (datagram_rtp_transport_) { + return datagram_rtp_transport_.get(); + } else { + return default_rtp_transport(); + } + } + + const DtlsTransportInternal* rtp_dtls_transport() const { + rtc::CritScope scope(&accessor_lock_); + if (rtp_dtls_transport_) { + return rtp_dtls_transport_->internal(); + } else { + return nullptr; + } + } + + DtlsTransportInternal* rtp_dtls_transport() { + rtc::CritScope scope(&accessor_lock_); + if (rtp_dtls_transport_) { + return rtp_dtls_transport_->internal(); + } else { + return nullptr; + } + } + + const DtlsTransportInternal* rtcp_dtls_transport() const { + rtc::CritScope scope(&accessor_lock_); + if (rtcp_dtls_transport_) { + return rtcp_dtls_transport_->internal(); + } else { + return nullptr; + } + } + + DtlsTransportInternal* rtcp_dtls_transport() { + rtc::CritScope scope(&accessor_lock_); + if (rtcp_dtls_transport_) { + return rtcp_dtls_transport_->internal(); + } else { + return nullptr; + } + } + + rtc::scoped_refptr RtpDtlsTransport() { + rtc::CritScope scope(&accessor_lock_); + return rtp_dtls_transport_; + } + + rtc::scoped_refptr SctpTransport() const { + rtc::CritScope scope(&accessor_lock_); + return sctp_transport_; + } + + webrtc::DataChannelTransportInterface* data_channel_transport() const { + rtc::CritScope scope(&accessor_lock_); + if (composite_data_channel_transport_) { + return composite_data_channel_transport_.get(); + } else if (sctp_data_channel_transport_) { + return sctp_data_channel_transport_.get(); + } + return data_channel_transport_; + } + + // Returns datagram transport, if available. + webrtc::DatagramTransportInterface* datagram_transport() const { + rtc::CritScope scope(&accessor_lock_); + return datagram_transport_.get(); + } + + // This is signaled when RTCP-mux becomes active and + // |rtcp_dtls_transport_| is destroyed. The TgJsepTransportController will + // handle the signal and update the aggregate transport states. + sigslot::signal<> SignalRtcpMuxActive; + + // Signals that a data channel transport was negotiated and may be used to + // send data. The first parameter is |this|. The second parameter is the + // transport that was negotiated, or null if negotiation rejected the data + // channel transport. The third parameter (bool) indicates whether the + // negotiation was provisional or final. If true, it is provisional, if + // false, it is final. + sigslot::signal2 + SignalDataChannelTransportNegotiated; + + // TODO(deadbeef): The methods below are only public for testing. Should make + // them utility functions or objects so they can be tested independently from + // this class. + + // Returns an error if the certificate's identity does not match the + // fingerprint, or either is NULL. + webrtc::RTCError VerifyCertificateFingerprint( + const rtc::RTCCertificate* certificate, + const rtc::SSLFingerprint* fingerprint) const; + + void SetActiveResetSrtpParams(bool active_reset_srtp_params); + + private: + bool SetRtcpMux(bool enable, webrtc::SdpType type, ContentSource source); + + void ActivateRtcpMux(); + + bool SetSdes(const std::vector& cryptos, + const std::vector& encrypted_extension_ids, + webrtc::SdpType type, + ContentSource source); + + // Negotiates and sets the DTLS parameters based on the current local and + // remote transport description, such as the DTLS role to use, and whether + // DTLS should be activated. + // + // Called when an answer TransportDescription is applied. + webrtc::RTCError NegotiateAndSetDtlsParameters( + webrtc::SdpType local_description_type); + + // Negotiates the DTLS role based off the offer and answer as specified by + // RFC 4145, section-4.1. Returns an RTCError if role cannot be determined + // from the local description and remote description. + webrtc::RTCError NegotiateDtlsRole( + webrtc::SdpType local_description_type, + ConnectionRole local_connection_role, + ConnectionRole remote_connection_role, + absl::optional* negotiated_dtls_role); + + // Pushes down the ICE parameters from the local description, such + // as the ICE ufrag and pwd. + void SetLocalIceParameters(IceTransportInternal* ice); + + // Pushes down the ICE parameters from the remote description. + void SetRemoteIceParameters(IceTransportInternal* ice); + + // Pushes down the DTLS parameters obtained via negotiation. + webrtc::RTCError SetNegotiatedDtlsParameters( + DtlsTransportInternal* dtls_transport, + absl::optional dtls_role, + rtc::SSLFingerprint* remote_fingerprint); + + bool GetTransportStats(DtlsTransportInternal* dtls_transport, + TransportStats* stats); + + // Deactivates, signals removal, and deletes |composite_rtp_transport_| if the + // current state of negotiation is sufficient to determine which rtp_transport + // and data channel transport to use. + void NegotiateDatagramTransport(webrtc::SdpType type) + RTC_RUN_ON(network_thread_); + + // Returns the default (non-datagram) rtp transport, if any. + webrtc::RtpTransportInternal* default_rtp_transport() const + RTC_EXCLUSIVE_LOCKS_REQUIRED(accessor_lock_) { + if (dtls_srtp_transport_) { + return dtls_srtp_transport_.get(); + } else if (sdes_transport_) { + return sdes_transport_.get(); + } else if (unencrypted_rtp_transport_) { + return unencrypted_rtp_transport_.get(); + } else { + return nullptr; + } + } + + // Owning thread, for safety checks + const rtc::Thread* const network_thread_; + // Critical scope for fields accessed off-thread + // TODO(https://bugs.webrtc.org/10300): Stop doing this. + rtc::CriticalSection accessor_lock_; + const std::string mid_; + // needs-ice-restart bit as described in JSEP. + bool needs_ice_restart_ RTC_GUARDED_BY(accessor_lock_) = false; + rtc::scoped_refptr local_certificate_ + RTC_GUARDED_BY(network_thread_); + std::unique_ptr local_description_ + RTC_GUARDED_BY(network_thread_); + std::unique_ptr remote_description_ + RTC_GUARDED_BY(network_thread_); + + // Ice transport which may be used by any of upper-layer transports (below). + // Owned by TgJsepTransport and guaranteed to outlive the transports below. + const rtc::scoped_refptr ice_transport_; + const rtc::scoped_refptr rtcp_ice_transport_; + + // To avoid downcasting and make it type safe, keep three unique pointers for + // different SRTP mode and only one of these is non-nullptr. + std::unique_ptr unencrypted_rtp_transport_ + RTC_GUARDED_BY(accessor_lock_); + std::unique_ptr sdes_transport_ + RTC_GUARDED_BY(accessor_lock_); + std::unique_ptr dtls_srtp_transport_ + RTC_GUARDED_BY(accessor_lock_); + + // If multiple RTP transports are in use, |composite_rtp_transport_| will be + // passed to callers. This is only valid for offer-only, receive-only + // scenarios, as it is not possible for the composite to correctly choose + // which transport to use for sending. + std::unique_ptr composite_rtp_transport_ + RTC_GUARDED_BY(accessor_lock_); + + rtc::scoped_refptr rtp_dtls_transport_ + RTC_GUARDED_BY(accessor_lock_); + rtc::scoped_refptr rtcp_dtls_transport_ + RTC_GUARDED_BY(accessor_lock_); + rtc::scoped_refptr datagram_dtls_transport_ + RTC_GUARDED_BY(accessor_lock_); + + std::unique_ptr + sctp_data_channel_transport_ RTC_GUARDED_BY(accessor_lock_); + rtc::scoped_refptr sctp_transport_ + RTC_GUARDED_BY(accessor_lock_); + + SrtpFilter sdes_negotiator_ RTC_GUARDED_BY(network_thread_); + RtcpMuxFilter rtcp_mux_negotiator_ RTC_GUARDED_BY(network_thread_); + + // Cache the encrypted header extension IDs for SDES negoitation. + absl::optional> send_extension_ids_ + RTC_GUARDED_BY(network_thread_); + absl::optional> recv_extension_ids_ + RTC_GUARDED_BY(network_thread_); + + // Optional datagram transport (experimental). + std::unique_ptr datagram_transport_ + RTC_GUARDED_BY(accessor_lock_); + + std::unique_ptr datagram_rtp_transport_ + RTC_GUARDED_BY(accessor_lock_); + + // Non-SCTP data channel transport. Set to |datagram_transport_| if that + // transport should be used for data chanels. Unset otherwise. + webrtc::DataChannelTransportInterface* data_channel_transport_ + RTC_GUARDED_BY(accessor_lock_) = nullptr; + + // Composite data channel transport, used during negotiation. + std::unique_ptr + composite_data_channel_transport_ RTC_GUARDED_BY(accessor_lock_); + + RTC_DISALLOW_COPY_AND_ASSIGN(TgJsepTransport); +}; + +} // namespace cricket + +#endif // PC_JSEP_TRANSPORT_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp new file mode 100644 index 0000000000..62c5a2eaa5 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp @@ -0,0 +1,1697 @@ +/* + * Copyright 2017 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 "tg_jsep_transport_controller.h" + +#include +#include + +#include "absl/algorithm/container.h" +#include "api/ice_transport_factory.h" +#include "api/transport/datagram_transport_interface.h" +#include "api/transport/media/media_transport_interface.h" +#include "p2p/base/ice_transport_internal.h" +#include "p2p/base/port.h" +#include "pc/datagram_rtp_transport.h" +#include "pc/srtp_filter.h" +#include "rtc_base/bind.h" +#include "rtc_base/checks.h" +#include "rtc_base/thread.h" + +using webrtc::SdpType; + +namespace { + +webrtc::RTCError VerifyCandidate(const cricket::Candidate& cand) { + // No address zero. + if (cand.address().IsNil() || cand.address().IsAnyIP()) { + return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, + "candidate has address of zero"); + } + + // Disallow all ports below 1024, except for 80 and 443 on public addresses. + int port = cand.address().port(); + if (cand.protocol() == cricket::TCP_PROTOCOL_NAME && + (cand.tcptype() == cricket::TCPTYPE_ACTIVE_STR || port == 0)) { + // Expected for active-only candidates per + // http://tools.ietf.org/html/rfc6544#section-4.5 so no error. + // Libjingle clients emit port 0, in "active" mode. + return webrtc::RTCError::OK(); + } + if (port < 1024) { + if ((port != 80) && (port != 443)) { + return webrtc::RTCError( + webrtc::RTCErrorType::INVALID_PARAMETER, + "candidate has port below 1024, but not 80 or 443"); + } + + if (cand.address().IsPrivateIP()) { + return webrtc::RTCError( + webrtc::RTCErrorType::INVALID_PARAMETER, + "candidate has port of 80 or 443 with private IP address"); + } + } + + return webrtc::RTCError::OK(); +} + +webrtc::RTCError VerifyCandidates(const cricket::Candidates& candidates) { + for (const cricket::Candidate& candidate : candidates) { + webrtc::RTCError error = VerifyCandidate(candidate); + if (!error.ok()) { + return error; + } + } + return webrtc::RTCError::OK(); +} + +} // namespace + +namespace webrtc { + +TgJsepTransportController::TgJsepTransportController( + rtc::Thread* signaling_thread, + rtc::Thread* network_thread, + cricket::PortAllocator* port_allocator, + AsyncResolverFactory* async_resolver_factory, + Config config) + : signaling_thread_(signaling_thread), + network_thread_(network_thread), + port_allocator_(port_allocator), + async_resolver_factory_(async_resolver_factory), + config_(config) { + // The |transport_observer| is assumed to be non-null. + RTC_DCHECK(config_.transport_observer); + RTC_DCHECK(config_.rtcp_handler); + RTC_DCHECK(config_.ice_transport_factory); +} + +TgJsepTransportController::~TgJsepTransportController() { + // Channel destructors may try to send packets, so this needs to happen on + // the network thread. + network_thread_->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgJsepTransportController::DestroyAllJsepTransports_n, this)); +} + +RTCError TgJsepTransportController::SetLocalDescription( + SdpType type, + const cricket::SessionDescription* description) { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke( + RTC_FROM_HERE, [=] { return SetLocalDescription(type, description); }); + } + + if (!initial_offerer_.has_value()) { + initial_offerer_.emplace(type == SdpType::kOffer); + if (*initial_offerer_) { + SetIceRole_n(cricket::ICEROLE_CONTROLLING); + } else { + SetIceRole_n(cricket::ICEROLE_CONTROLLED); + } + } + return ApplyDescription_n(/*local=*/true, type, description); +} + +RTCError TgJsepTransportController::SetRemoteDescription( + SdpType type, + const cricket::SessionDescription* description) { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke( + RTC_FROM_HERE, [=] { return SetRemoteDescription(type, description); }); + } + + return ApplyDescription_n(/*local=*/false, type, description); +} + +RtpTransportInternal* TgJsepTransportController::GetRtpTransport( + const std::string& mid) const { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport) { + return nullptr; + } + return jsep_transport->rtp_transport(); +} + +MediaTransportConfig TgJsepTransportController::GetMediaTransportConfig( + const std::string& mid) const { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport) { + return MediaTransportConfig(); + } + + DatagramTransportInterface* datagram_transport = nullptr; + if (config_.use_datagram_transport) { + datagram_transport = jsep_transport->datagram_transport(); + } + + if (datagram_transport) { + return MediaTransportConfig( + /*rtp_max_packet_size=*/datagram_transport->GetLargestDatagramSize()); + } else { + return MediaTransportConfig(); + } +} + +DataChannelTransportInterface* TgJsepTransportController::GetDataChannelTransport( + const std::string& mid) const { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport) { + return nullptr; + } + return jsep_transport->data_channel_transport(); +} + +cricket::DtlsTransportInternal* TgJsepTransportController::GetDtlsTransport( + const std::string& mid) { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport) { + return nullptr; + } + return jsep_transport->rtp_dtls_transport(); +} + +const cricket::DtlsTransportInternal* +TgJsepTransportController::GetRtcpDtlsTransport(const std::string& mid) const { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport) { + return nullptr; + } + return jsep_transport->rtcp_dtls_transport(); +} + +rtc::scoped_refptr +TgJsepTransportController::LookupDtlsTransportByMid(const std::string& mid) { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport) { + return nullptr; + } + return jsep_transport->RtpDtlsTransport(); +} + +rtc::scoped_refptr TgJsepTransportController::GetSctpTransport( + const std::string& mid) const { + auto jsep_transport = GetJsepTransportForMid(mid); + if (!jsep_transport) { + return nullptr; + } + return jsep_transport->SctpTransport(); +} + +void TgJsepTransportController::SetIceConfig(const cricket::IceConfig& config) { + if (!network_thread_->IsCurrent()) { + network_thread_->Invoke(RTC_FROM_HERE, [&] { SetIceConfig(config); }); + return; + } + + ice_config_ = config; + for (auto& dtls : GetDtlsTransports()) { + dtls->ice_transport()->SetIceConfig(ice_config_); + } +} + +void TgJsepTransportController::SetNeedsIceRestartFlag() { + for (auto& kv : jsep_transports_by_name_) { + kv.second->SetNeedsIceRestartFlag(); + } +} + +bool TgJsepTransportController::NeedsIceRestart( + const std::string& transport_name) const { + const cricket::TgJsepTransport* transport = + GetJsepTransportByName(transport_name); + if (!transport) { + return false; + } + return transport->needs_ice_restart(); +} + +absl::optional TgJsepTransportController::GetDtlsRole( + const std::string& mid) const { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke>( + RTC_FROM_HERE, [&] { return GetDtlsRole(mid); }); + } + + const cricket::TgJsepTransport* t = GetJsepTransportForMid(mid); + if (!t) { + return absl::optional(); + } + return t->GetDtlsRole(); +} + +bool TgJsepTransportController::SetLocalCertificate( + const rtc::scoped_refptr& certificate) { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke( + RTC_FROM_HERE, [&] { return SetLocalCertificate(certificate); }); + } + + // Can't change a certificate, or set a null certificate. + if (certificate_ || !certificate) { + return false; + } + certificate_ = certificate; + + // Set certificate for TgJsepTransport, which verifies it matches the + // fingerprint in SDP, and DTLS transport. + // Fallback from DTLS to SDES is not supported. + for (auto& kv : jsep_transports_by_name_) { + kv.second->SetLocalCertificate(certificate_); + } + for (auto& dtls : GetDtlsTransports()) { + bool set_cert_success = dtls->SetLocalCertificate(certificate_); + RTC_DCHECK(set_cert_success); + } + return true; +} + +rtc::scoped_refptr +TgJsepTransportController::GetLocalCertificate( + const std::string& transport_name) const { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke>( + RTC_FROM_HERE, [&] { return GetLocalCertificate(transport_name); }); + } + + const cricket::TgJsepTransport* t = GetJsepTransportByName(transport_name); + if (!t) { + return nullptr; + } + return t->GetLocalCertificate(); +} + +std::unique_ptr +TgJsepTransportController::GetRemoteSSLCertChain( + const std::string& transport_name) const { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke>( + RTC_FROM_HERE, [&] { return GetRemoteSSLCertChain(transport_name); }); + } + + // Get the certificate from the RTP transport's DTLS handshake. Should be + // identical to the RTCP transport's, since they were given the same remote + // fingerprint. + auto jsep_transport = GetJsepTransportByName(transport_name); + if (!jsep_transport) { + return nullptr; + } + auto dtls = jsep_transport->rtp_dtls_transport(); + if (!dtls) { + return nullptr; + } + + return dtls->GetRemoteSSLCertChain(); +} + +void TgJsepTransportController::MaybeStartGathering() { + if (!network_thread_->IsCurrent()) { + network_thread_->Invoke(RTC_FROM_HERE, + [&] { MaybeStartGathering(); }); + return; + } + + for (auto& dtls : GetDtlsTransports()) { + dtls->ice_transport()->MaybeStartGathering(); + } +} + +RTCError TgJsepTransportController::AddRemoteCandidates( + const std::string& transport_name, + const cricket::Candidates& candidates) { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke(RTC_FROM_HERE, [&] { + return AddRemoteCandidates(transport_name, candidates); + }); + } + + // Verify each candidate before passing down to the transport layer. + RTCError error = VerifyCandidates(candidates); + if (!error.ok()) { + return error; + } + auto jsep_transport = GetJsepTransportByName(transport_name); + if (!jsep_transport) { + RTC_LOG(LS_WARNING) << "Not adding candidate because the TgJsepTransport " + "doesn't exist. Ignore it."; + return RTCError::OK(); + } + return jsep_transport->AddRemoteCandidates(candidates); +} + +RTCError TgJsepTransportController::RemoveRemoteCandidates( + const cricket::Candidates& candidates) { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke( + RTC_FROM_HERE, [&] { return RemoveRemoteCandidates(candidates); }); + } + + // Verify each candidate before passing down to the transport layer. + RTCError error = VerifyCandidates(candidates); + if (!error.ok()) { + return error; + } + + std::map candidates_by_transport_name; + for (const cricket::Candidate& cand : candidates) { + if (!cand.transport_name().empty()) { + candidates_by_transport_name[cand.transport_name()].push_back(cand); + } else { + RTC_LOG(LS_ERROR) << "Not removing candidate because it does not have a " + "transport name set: " + << cand.ToSensitiveString(); + } + } + + for (const auto& kv : candidates_by_transport_name) { + const std::string& transport_name = kv.first; + const cricket::Candidates& candidates = kv.second; + cricket::TgJsepTransport* jsep_transport = + GetJsepTransportByName(transport_name); + if (!jsep_transport) { + RTC_LOG(LS_WARNING) + << "Not removing candidate because the TgJsepTransport doesn't exist."; + continue; + } + for (const cricket::Candidate& candidate : candidates) { + cricket::DtlsTransportInternal* dtls = + candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP + ? jsep_transport->rtp_dtls_transport() + : jsep_transport->rtcp_dtls_transport(); + if (dtls) { + dtls->ice_transport()->RemoveRemoteCandidate(candidate); + } + } + } + return RTCError::OK(); +} + +bool TgJsepTransportController::GetStats(const std::string& transport_name, + cricket::TransportStats* stats) { + if (!network_thread_->IsCurrent()) { + return network_thread_->Invoke( + RTC_FROM_HERE, [=] { return GetStats(transport_name, stats); }); + } + + cricket::TgJsepTransport* transport = GetJsepTransportByName(transport_name); + if (!transport) { + return false; + } + return transport->GetStats(stats); +} + +void TgJsepTransportController::SetActiveResetSrtpParams( + bool active_reset_srtp_params) { + if (!network_thread_->IsCurrent()) { + network_thread_->Invoke(RTC_FROM_HERE, [=] { + SetActiveResetSrtpParams(active_reset_srtp_params); + }); + return; + } + + RTC_LOG(INFO) + << "Updating the active_reset_srtp_params for JsepTransportController: " + << active_reset_srtp_params; + config_.active_reset_srtp_params = active_reset_srtp_params; + for (auto& kv : jsep_transports_by_name_) { + kv.second->SetActiveResetSrtpParams(active_reset_srtp_params); + } +} + +void TgJsepTransportController::SetMediaTransportSettings( + bool use_datagram_transport, + bool use_datagram_transport_for_data_channels, + bool use_datagram_transport_for_data_channels_receive_only) { + config_.use_datagram_transport = use_datagram_transport; + config_.use_datagram_transport_for_data_channels = + use_datagram_transport_for_data_channels; + config_.use_datagram_transport_for_data_channels_receive_only = + use_datagram_transport_for_data_channels_receive_only; +} + +void TgJsepTransportController::RollbackTransportForMids( + const std::vector& mids) { + if (!network_thread_->IsCurrent()) { + network_thread_->Invoke(RTC_FROM_HERE, + [=] { RollbackTransportForMids(mids); }); + return; + } + for (auto&& mid : mids) { + RemoveTransportForMid(mid); + } + for (auto&& mid : mids) { + MaybeDestroyJsepTransport(mid); + } +} + +rtc::scoped_refptr +TgJsepTransportController::CreateIceTransport(const std::string& transport_name, + bool rtcp) { + int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP + : cricket::ICE_CANDIDATE_COMPONENT_RTP; + + IceTransportInit init; + init.set_port_allocator(port_allocator_); + init.set_async_resolver_factory(async_resolver_factory_); + init.set_event_log(config_.event_log); + return config_.ice_transport_factory->CreateIceTransport( + transport_name, component, std::move(init)); +} + +std::unique_ptr +TgJsepTransportController::CreateDtlsTransport( + const cricket::ContentInfo& content_info, + cricket::IceTransportInternal* ice, + DatagramTransportInterface* datagram_transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + + std::unique_ptr dtls; + + if (datagram_transport) { + RTC_DCHECK(config_.use_datagram_transport || + config_.use_datagram_transport_for_data_channels); + } else if (config_.dtls_transport_factory) { + dtls = config_.dtls_transport_factory->CreateDtlsTransport( + ice, config_.crypto_options); + } else { + dtls = std::make_unique(ice, config_.crypto_options, + config_.event_log); + } + + RTC_DCHECK(dtls); + dtls->SetSslMaxProtocolVersion(config_.ssl_max_version); + dtls->ice_transport()->SetIceRole(ice_role_); + dtls->ice_transport()->SetIceTiebreaker(ice_tiebreaker_); + dtls->ice_transport()->SetIceConfig(ice_config_); + if (certificate_) { + bool set_cert_success = dtls->SetLocalCertificate(certificate_); + RTC_DCHECK(set_cert_success); + } + + // Connect to signals offered by the DTLS and ICE transport. + dtls->SignalWritableState.connect( + this, &TgJsepTransportController::OnTransportWritableState_n); + dtls->SignalReceivingState.connect( + this, &TgJsepTransportController::OnTransportReceivingState_n); + dtls->SignalDtlsHandshakeError.connect( + this, &TgJsepTransportController::OnDtlsHandshakeError); + dtls->ice_transport()->SignalGatheringState.connect( + this, &TgJsepTransportController::OnTransportGatheringState_n); + dtls->ice_transport()->SignalCandidateGathered.connect( + this, &TgJsepTransportController::OnTransportCandidateGathered_n); + dtls->ice_transport()->SignalCandidateError.connect( + this, &TgJsepTransportController::OnTransportCandidateError_n); + dtls->ice_transport()->SignalCandidatesRemoved.connect( + this, &TgJsepTransportController::OnTransportCandidatesRemoved_n); + dtls->ice_transport()->SignalRoleConflict.connect( + this, &TgJsepTransportController::OnTransportRoleConflict_n); + dtls->ice_transport()->SignalStateChanged.connect( + this, &TgJsepTransportController::OnTransportStateChanged_n); + dtls->ice_transport()->SignalIceTransportStateChanged.connect( + this, &TgJsepTransportController::OnTransportStateChanged_n); + dtls->ice_transport()->SignalCandidatePairChanged.connect( + this, &TgJsepTransportController::OnTransportCandidatePairChanged_n); + return dtls; +} + +std::unique_ptr +TgJsepTransportController::CreateUnencryptedRtpTransport( + const std::string& transport_name, + rtc::PacketTransportInternal* rtp_packet_transport, + rtc::PacketTransportInternal* rtcp_packet_transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + auto unencrypted_rtp_transport = + std::make_unique(rtcp_packet_transport == nullptr); + unencrypted_rtp_transport->SetRtpPacketTransport(rtp_packet_transport); + if (rtcp_packet_transport) { + unencrypted_rtp_transport->SetRtcpPacketTransport(rtcp_packet_transport); + } + return unencrypted_rtp_transport; +} + +std::unique_ptr +TgJsepTransportController::CreateSdesTransport( + const std::string& transport_name, + cricket::DtlsTransportInternal* rtp_dtls_transport, + cricket::DtlsTransportInternal* rtcp_dtls_transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + auto srtp_transport = + std::make_unique(rtcp_dtls_transport == nullptr); + RTC_DCHECK(rtp_dtls_transport); + srtp_transport->SetRtpPacketTransport(rtp_dtls_transport); + if (rtcp_dtls_transport) { + srtp_transport->SetRtcpPacketTransport(rtcp_dtls_transport); + } + if (config_.enable_external_auth) { + srtp_transport->EnableExternalAuth(); + } + return srtp_transport; +} + +std::unique_ptr +TgJsepTransportController::CreateDtlsSrtpTransport( + const std::string& transport_name, + cricket::DtlsTransportInternal* rtp_dtls_transport, + cricket::DtlsTransportInternal* rtcp_dtls_transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + auto dtls_srtp_transport = std::make_unique( + rtcp_dtls_transport == nullptr); + if (config_.enable_external_auth) { + dtls_srtp_transport->EnableExternalAuth(); + } + + dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport, + rtcp_dtls_transport); + dtls_srtp_transport->SetActiveResetSrtpParams( + config_.active_reset_srtp_params); + dtls_srtp_transport->SignalDtlsStateChange.connect( + this, &TgJsepTransportController::UpdateAggregateStates_n); + return dtls_srtp_transport; +} + +std::vector +TgJsepTransportController::GetDtlsTransports() { + std::vector dtls_transports; + for (auto it = jsep_transports_by_name_.begin(); + it != jsep_transports_by_name_.end(); ++it) { + auto jsep_transport = it->second.get(); + RTC_DCHECK(jsep_transport); + if (jsep_transport->rtp_dtls_transport()) { + dtls_transports.push_back(jsep_transport->rtp_dtls_transport()); + } + + if (jsep_transport->rtcp_dtls_transport()) { + dtls_transports.push_back(jsep_transport->rtcp_dtls_transport()); + } + } + return dtls_transports; +} + +RTCError TgJsepTransportController::ApplyDescription_n( + bool local, + SdpType type, + const cricket::SessionDescription* description) { + RTC_DCHECK(network_thread_->IsCurrent()); + RTC_DCHECK(description); + + if (local) { + local_desc_ = description; + } else { + remote_desc_ = description; + } + + RTCError error; + error = ValidateAndMaybeUpdateBundleGroup(local, type, description); + if (!error.ok()) { + return error; + } + + std::vector merged_encrypted_extension_ids; + absl::optional bundle_media_alt_protocol; + absl::optional bundle_data_alt_protocol; + if (bundle_group_) { + merged_encrypted_extension_ids = + MergeEncryptedHeaderExtensionIdsForBundle(description); + error = GetAltProtocolsForBundle(description, &bundle_media_alt_protocol, + &bundle_data_alt_protocol); + if (!error.ok()) { + return error; + } + } + + for (const cricket::ContentInfo& content_info : description->contents()) { + // Don't create transports for rejected m-lines and bundled m-lines." + if (content_info.rejected || + (IsBundled(content_info.name) && content_info.name != *bundled_mid())) { + continue; + } + error = MaybeCreateJsepTransport(local, content_info, *description); + if (!error.ok()) { + return error; + } + } + + RTC_DCHECK(description->contents().size() == + description->transport_infos().size()); + for (size_t i = 0; i < description->contents().size(); ++i) { + const cricket::ContentInfo& content_info = description->contents()[i]; + const cricket::MediaContentDescription* media_description = + content_info.media_description(); + const cricket::TransportInfo& transport_info = + description->transport_infos()[i]; + if (content_info.rejected) { + HandleRejectedContent(content_info, description); + continue; + } + + if (IsBundled(content_info.name) && content_info.name != *bundled_mid()) { + if (!HandleBundledContent(content_info)) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "Failed to process the bundled m= section."); + } + continue; + } + + error = ValidateContent(content_info); + if (!error.ok()) { + return error; + } + + std::vector extension_ids; + absl::optional media_alt_protocol; + absl::optional data_alt_protocol; + if (bundled_mid() && content_info.name == *bundled_mid()) { + extension_ids = merged_encrypted_extension_ids; + media_alt_protocol = bundle_media_alt_protocol; + data_alt_protocol = bundle_data_alt_protocol; + } else { + extension_ids = GetEncryptedHeaderExtensionIds(content_info); + switch (media_description->type()) { + case cricket::MEDIA_TYPE_AUDIO: + case cricket::MEDIA_TYPE_VIDEO: + media_alt_protocol = media_description->alt_protocol(); + break; + case cricket::MEDIA_TYPE_DATA: + data_alt_protocol = media_description->alt_protocol(); + break; + } + } + + int rtp_abs_sendtime_extn_id = + GetRtpAbsSendTimeHeaderExtensionId(content_info); + + cricket::TgJsepTransport* transport = + GetJsepTransportForMid(content_info.name); + RTC_DCHECK(transport); + + SetIceRole_n(DetermineIceRole(transport, transport_info, type, local)); + + cricket::TgJsepTransportDescription jsep_description = + CreateJsepTransportDescription(content_info, transport_info, + extension_ids, rtp_abs_sendtime_extn_id, + media_alt_protocol, data_alt_protocol); + if (local) { + error = + transport->SetLocalJsepTransportDescription(jsep_description, type); + } else { + error = + transport->SetRemoteJsepTransportDescription(jsep_description, type); + } + + if (!error.ok()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Failed to apply the description for " + + content_info.name + ": " + error.message()); + } + } + return RTCError::OK(); +} + +RTCError TgJsepTransportController::ValidateAndMaybeUpdateBundleGroup( + bool local, + SdpType type, + const cricket::SessionDescription* description) { + RTC_DCHECK(description); + const cricket::ContentGroup* new_bundle_group = + description->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + + // The BUNDLE group containing a MID that no m= section has is invalid. + if (new_bundle_group) { + for (const auto& content_name : new_bundle_group->content_names()) { + if (!description->GetContentByName(content_name)) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "The BUNDLE group contains MID:" + content_name + + " matching no m= section."); + } + } + } + + if (type == SdpType::kAnswer) { + const cricket::ContentGroup* offered_bundle_group = + local ? remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE) + : local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + + if (new_bundle_group) { + // The BUNDLE group in answer should be a subset of offered group. + for (const auto& content_name : new_bundle_group->content_names()) { + if (!offered_bundle_group || + !offered_bundle_group->HasContentName(content_name)) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "The BUNDLE group in answer contains a MID that was " + "not in the offered group."); + } + } + } + + if (bundle_group_) { + for (const auto& content_name : bundle_group_->content_names()) { + // An answer that removes m= sections from pre-negotiated BUNDLE group + // without rejecting it, is invalid. + if (!new_bundle_group || + !new_bundle_group->HasContentName(content_name)) { + auto* content_info = description->GetContentByName(content_name); + if (!content_info || !content_info->rejected) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "Answer cannot remove m= section " + content_name + + " from already-established BUNDLE group."); + } + } + } + } + } + + if (config_.bundle_policy == + PeerConnectionInterface::kBundlePolicyMaxBundle && + !description->HasGroup(cricket::GROUP_TYPE_BUNDLE)) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "max-bundle is used but no bundle group found."); + } + + if (ShouldUpdateBundleGroup(type, description)) { + bundle_group_ = *new_bundle_group; + } + + if (!bundled_mid()) { + return RTCError::OK(); + } + + auto bundled_content = description->GetContentByName(*bundled_mid()); + if (!bundled_content) { + return RTCError( + RTCErrorType::INVALID_PARAMETER, + "An m= section associated with the BUNDLE-tag doesn't exist."); + } + + // If the |bundled_content| is rejected, other contents in the bundle group + // should be rejected. + if (bundled_content->rejected) { + for (const auto& content_name : bundle_group_->content_names()) { + auto other_content = description->GetContentByName(content_name); + if (!other_content->rejected) { + return RTCError( + RTCErrorType::INVALID_PARAMETER, + "The m= section:" + content_name + " should be rejected."); + } + } + } + + return RTCError::OK(); +} + +RTCError TgJsepTransportController::ValidateContent( + const cricket::ContentInfo& content_info) { + if (config_.rtcp_mux_policy == + PeerConnectionInterface::kRtcpMuxPolicyRequire && + content_info.type == cricket::MediaProtocolType::kRtp && + !content_info.media_description()->rtcp_mux()) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "The m= section:" + content_info.name + + " is invalid. RTCP-MUX is not " + "enabled when it is required."); + } + return RTCError::OK(); +} + +void TgJsepTransportController::HandleRejectedContent( + const cricket::ContentInfo& content_info, + const cricket::SessionDescription* description) { + // If the content is rejected, let the + // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first, + // then destroy the cricket::TgJsepTransport. + RemoveTransportForMid(content_info.name); + if (content_info.name == bundled_mid()) { + for (const auto& content_name : bundle_group_->content_names()) { + RemoveTransportForMid(content_name); + } + bundle_group_.reset(); + } else if (IsBundled(content_info.name)) { + // Remove the rejected content from the |bundle_group_|. + bundle_group_->RemoveContentName(content_info.name); + // Reset the bundle group if nothing left. + if (!bundle_group_->FirstContentName()) { + bundle_group_.reset(); + } + } + MaybeDestroyJsepTransport(content_info.name); +} + +bool TgJsepTransportController::HandleBundledContent( + const cricket::ContentInfo& content_info) { + auto jsep_transport = GetJsepTransportByName(*bundled_mid()); + RTC_DCHECK(jsep_transport); + // If the content is bundled, let the + // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first, + // then destroy the cricket::JsepTransport. + if (SetTransportForMid(content_info.name, jsep_transport)) { + // TODO(bugs.webrtc.org/9719) For media transport this is far from ideal, + // because it means that we first create media transport and start + // connecting it, and then we destroy it. We will need to address it before + // video path is enabled. + MaybeDestroyJsepTransport(content_info.name); + return true; + } + return false; +} + +bool TgJsepTransportController::SetTransportForMid( + const std::string& mid, + cricket::TgJsepTransport* jsep_transport) { + RTC_DCHECK(jsep_transport); + if (mid_to_transport_[mid] == jsep_transport) { + return true; + } + + mid_to_transport_[mid] = jsep_transport; + return config_.transport_observer->OnTransportChanged( + mid, jsep_transport->rtp_transport(), jsep_transport->RtpDtlsTransport(), + jsep_transport->data_channel_transport()); +} + +void TgJsepTransportController::RemoveTransportForMid(const std::string& mid) { + bool ret = config_.transport_observer->OnTransportChanged(mid, nullptr, + nullptr, nullptr); + // Calling OnTransportChanged with nullptr should always succeed, since it is + // only expected to fail when adding media to a transport (not removing). + RTC_DCHECK(ret); + mid_to_transport_.erase(mid); +} + +cricket::TgJsepTransportDescription +TgJsepTransportController::CreateJsepTransportDescription( + const cricket::ContentInfo& content_info, + const cricket::TransportInfo& transport_info, + const std::vector& encrypted_extension_ids, + int rtp_abs_sendtime_extn_id, + absl::optional media_alt_protocol, + absl::optional data_alt_protocol) { + const cricket::MediaContentDescription* content_desc = + content_info.media_description(); + RTC_DCHECK(content_desc); + bool rtcp_mux_enabled = content_info.type == cricket::MediaProtocolType::kSctp + ? true + : content_desc->rtcp_mux(); + + return cricket::TgJsepTransportDescription( + rtcp_mux_enabled, content_desc->cryptos(), encrypted_extension_ids, + rtp_abs_sendtime_extn_id, transport_info.description, media_alt_protocol, + data_alt_protocol); +} + +bool TgJsepTransportController::ShouldUpdateBundleGroup( + SdpType type, + const cricket::SessionDescription* description) { + if (config_.bundle_policy == + PeerConnectionInterface::kBundlePolicyMaxBundle) { + return true; + } + + if (type != SdpType::kAnswer) { + return false; + } + + RTC_DCHECK(local_desc_ && remote_desc_); + const cricket::ContentGroup* local_bundle = + local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + const cricket::ContentGroup* remote_bundle = + remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + return local_bundle && remote_bundle; +} + +std::vector TgJsepTransportController::GetEncryptedHeaderExtensionIds( + const cricket::ContentInfo& content_info) { + const cricket::MediaContentDescription* content_desc = + content_info.media_description(); + + if (!config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions) { + return std::vector(); + } + + std::vector encrypted_header_extension_ids; + for (const auto& extension : content_desc->rtp_header_extensions()) { + if (!extension.encrypt) { + continue; + } + if (!absl::c_linear_search(encrypted_header_extension_ids, extension.id)) { + encrypted_header_extension_ids.push_back(extension.id); + } + } + return encrypted_header_extension_ids; +} + +std::vector +TgJsepTransportController::MergeEncryptedHeaderExtensionIdsForBundle( + const cricket::SessionDescription* description) { + RTC_DCHECK(description); + RTC_DCHECK(bundle_group_); + + std::vector merged_ids; + // Union the encrypted header IDs in the group when bundle is enabled. + for (const cricket::ContentInfo& content_info : description->contents()) { + if (bundle_group_->HasContentName(content_info.name)) { + std::vector extension_ids = + GetEncryptedHeaderExtensionIds(content_info); + for (int id : extension_ids) { + if (!absl::c_linear_search(merged_ids, id)) { + merged_ids.push_back(id); + } + } + } + } + return merged_ids; +} + +RTCError TgJsepTransportController::GetAltProtocolsForBundle( + const cricket::SessionDescription* description, + absl::optional* media_alt_protocol, + absl::optional* data_alt_protocol) { + RTC_DCHECK(description); + RTC_DCHECK(bundle_group_); + RTC_DCHECK(media_alt_protocol); + RTC_DCHECK(data_alt_protocol); + + bool found_media = false; + bool found_data = false; + for (const cricket::ContentInfo& content : description->contents()) { + if (bundle_group_->HasContentName(content.name)) { + const cricket::MediaContentDescription* media_description = + content.media_description(); + switch (media_description->type()) { + case cricket::MEDIA_TYPE_AUDIO: + case cricket::MEDIA_TYPE_VIDEO: + if (found_media && + *media_alt_protocol != media_description->alt_protocol()) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "The BUNDLE group contains conflicting " + "alt-protocols for media ('" + + media_alt_protocol->value_or("") + "' and '" + + media_description->alt_protocol().value_or("") + + "')"); + } + found_media = true; + *media_alt_protocol = media_description->alt_protocol(); + break; + case cricket::MEDIA_TYPE_DATA: + if (found_data && + *data_alt_protocol != media_description->alt_protocol()) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "The BUNDLE group contains conflicting " + "alt-protocols for data ('" + + data_alt_protocol->value_or("") + "' and '" + + media_description->alt_protocol().value_or("") + + "')"); + } + found_data = true; + *data_alt_protocol = media_description->alt_protocol(); + break; + } + } + } + return RTCError::OK(); +} + +int TgJsepTransportController::GetRtpAbsSendTimeHeaderExtensionId( + const cricket::ContentInfo& content_info) { + if (!config_.enable_external_auth) { + return -1; + } + + const cricket::MediaContentDescription* content_desc = + content_info.media_description(); + + const webrtc::RtpExtension* send_time_extension = + webrtc::RtpExtension::FindHeaderExtensionByUri( + content_desc->rtp_header_extensions(), + webrtc::RtpExtension::kAbsSendTimeUri); + return send_time_extension ? send_time_extension->id : -1; +} + +const cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportForMid( + const std::string& mid) const { + auto it = mid_to_transport_.find(mid); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + +cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportForMid( + const std::string& mid) { + auto it = mid_to_transport_.find(mid); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + +const cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportByName( + const std::string& transport_name) const { + auto it = jsep_transports_by_name_.find(transport_name); + return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get(); +} + +cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportByName( + const std::string& transport_name) { + auto it = jsep_transports_by_name_.find(transport_name); + return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get(); +} + +// TODO(sukhanov): Refactor to avoid code duplication for Media and Datagram +// transports setup. +std::unique_ptr +TgJsepTransportController::MaybeCreateDatagramTransport( + const cricket::ContentInfo& content_info, + const cricket::SessionDescription& description, + bool local) { + if (config_.media_transport_factory == nullptr) { + return nullptr; + } + + if (!(config_.use_datagram_transport || + config_.use_datagram_transport_for_data_channels)) { + return nullptr; + } + + // Caller (offerer) datagram transport. + if (offer_datagram_transport_) { + RTC_DCHECK(local); + RTC_LOG(LS_INFO) << "Offered datagram transport has now been activated."; + return std::move(offer_datagram_transport_); + } + + const cricket::TransportDescription* transport_description = + description.GetTransportDescriptionByName(content_info.mid()); + RTC_DCHECK(transport_description) + << "Missing transport description for mid=" << content_info.mid(); + + if (!transport_description->opaque_parameters) { + RTC_LOG(LS_INFO) + << "No opaque transport parameters, not creating datagram transport"; + return nullptr; + } + + if (transport_description->opaque_parameters->protocol != + config_.media_transport_factory->GetTransportName()) { + RTC_LOG(LS_INFO) << "Opaque transport parameters for protocol=" + << transport_description->opaque_parameters->protocol + << ", which does not match supported protocol=" + << config_.media_transport_factory->GetTransportName(); + return nullptr; + } + + RTC_DCHECK(!local); + // When bundle is enabled, two TgJsepTransports are created, and then + // the second transport is destroyed (right away). + // For datagram transport, we don't want to create the second + // datagram transport in the first place. + RTC_LOG(LS_INFO) << "Returning new, client datagram transport."; + + MediaTransportSettings settings; + settings.is_caller = local; + settings.remote_transport_parameters = + transport_description->opaque_parameters->parameters; + settings.event_log = config_.event_log; + + auto datagram_transport_result = + config_.media_transport_factory->CreateDatagramTransport(network_thread_, + settings); + + // TODO(sukhanov): Proper error handling. + RTC_CHECK(datagram_transport_result.ok()); + + return datagram_transport_result.MoveValue(); +} + +RTCError TgJsepTransportController::MaybeCreateJsepTransport( + bool local, + const cricket::ContentInfo& content_info, + const cricket::SessionDescription& description) { + RTC_DCHECK(network_thread_->IsCurrent()); + cricket::TgJsepTransport* transport = GetJsepTransportByName(content_info.name); + if (transport) { + return RTCError::OK(); + } + + const cricket::MediaContentDescription* content_desc = + content_info.media_description(); + if (certificate_ && !content_desc->cryptos().empty()) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "SDES and DTLS-SRTP cannot be enabled at the same time."); + } + + rtc::scoped_refptr ice = + CreateIceTransport(content_info.name, /*rtcp=*/false); + RTC_DCHECK(ice); + + std::unique_ptr datagram_transport = + MaybeCreateDatagramTransport(content_info, description, local); + if (datagram_transport) { + datagram_transport->Connect(ice->internal()); + } + + std::unique_ptr rtp_dtls_transport = + CreateDtlsTransport(content_info, ice->internal(), nullptr); + + //std::unique_ptr rtcp_dtls_transport; + std::unique_ptr unencrypted_rtp_transport; + //std::unique_ptr sdes_transport; + //std::unique_ptr dtls_srtp_transport; + //std::unique_ptr datagram_rtp_transport; + + //rtc::scoped_refptr rtcp_ice; + //if (config_.rtcp_mux_policy != + // PeerConnectionInterface::kRtcpMuxPolicyRequire && + // content_info.type == cricket::MediaProtocolType::kRtp) { + // RTC_DCHECK(datagram_transport == nullptr); + // rtcp_ice = CreateIceTransport(content_info.name, /*rtcp=*/true); + // rtcp_dtls_transport = + // CreateDtlsTransport(content_info, rtcp_ice->internal(), + // /*datagram_transport=*/nullptr); + //} + + /*// Only create a datagram RTP transport if the datagram transport should be + // used for RTP. + if (datagram_transport && config_.use_datagram_transport) { + // TODO(sukhanov): We use unencrypted RTP transport over DatagramTransport, + // because MediaTransport encrypts. In the future we may want to + // implement our own version of RtpTransport over MediaTransport, because + // it will give us more control over things like: + // - Fusing + // - Rtp header compression + // - Handling Rtcp feedback. + RTC_LOG(LS_INFO) << "Creating UnencryptedRtpTransport, because datagram " + "transport is used."; + RTC_DCHECK(!rtcp_dtls_transport); + datagram_rtp_transport = std::make_unique( + content_info.media_description()->rtp_header_extensions(), + ice->internal(), datagram_transport.get()); + }*/ + + /*if (config_.disable_encryption) { + RTC_LOG(LS_INFO) + << "Creating UnencryptedRtpTransport, becayse encryption is disabled.";*/ + unencrypted_rtp_transport = CreateUnencryptedRtpTransport( + content_info.name, rtp_dtls_transport.get(), /*rtcp_dtls_transport.get()*/nullptr); + /*} else if (!content_desc->cryptos().empty()) { + sdes_transport = CreateSdesTransport( + content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); + RTC_LOG(LS_INFO) << "Creating SdesTransport."; + } else { + RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport."; + dtls_srtp_transport = CreateDtlsSrtpTransport( + content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); + }*/ + + /*std::unique_ptr sctp_transport; + if (config_.sctp_factory) { + sctp_transport = + config_.sctp_factory->CreateSctpTransport(rtp_dtls_transport.get()); + }*/ + + /*DataChannelTransportInterface* data_channel_transport = nullptr; + if (config_.use_datagram_transport_for_data_channels) { + data_channel_transport = datagram_transport.get(); + }*/ + + std::unique_ptr jsep_transport = + std::make_unique( + content_info.name, certificate_, std::move(ice), /*std::move(rtcp_ice)*/nullptr, + std::move(unencrypted_rtp_transport), /*std::move(sdes_transport)*/nullptr, + /*std::move(dtls_srtp_transport)*/nullptr, /*std::move(datagram_rtp_transport)*/nullptr, + std::move(rtp_dtls_transport), /*std::move(rtcp_dtls_transport)*/nullptr, + /*std::move(sctp_transport)*/nullptr, /*std::move(datagram_transport)*/nullptr, + /*data_channel_transport*/nullptr); + + jsep_transport->rtp_transport()->SignalRtcpPacketReceived.connect( + this, &TgJsepTransportController::OnRtcpPacketReceived_n); + + jsep_transport->SignalRtcpMuxActive.connect( + this, &TgJsepTransportController::UpdateAggregateStates_n); + jsep_transport->SignalDataChannelTransportNegotiated.connect( + this, &TgJsepTransportController::OnDataChannelTransportNegotiated_n); + SetTransportForMid(content_info.name, jsep_transport.get()); + + jsep_transports_by_name_[content_info.name] = std::move(jsep_transport); + UpdateAggregateStates_n(); + return RTCError::OK(); +} + +void TgJsepTransportController::MaybeDestroyJsepTransport( + const std::string& mid) { + auto jsep_transport = GetJsepTransportByName(mid); + if (!jsep_transport) { + return; + } + + // Don't destroy the JsepTransport if there are still media sections referring + // to it. + for (const auto& kv : mid_to_transport_) { + if (kv.second == jsep_transport) { + return; + } + } + + jsep_transports_by_name_.erase(mid); + UpdateAggregateStates_n(); +} + +void TgJsepTransportController::DestroyAllJsepTransports_n() { + RTC_DCHECK(network_thread_->IsCurrent()); + + for (const auto& jsep_transport : jsep_transports_by_name_) { + config_.transport_observer->OnTransportChanged(jsep_transport.first, + nullptr, nullptr, nullptr); + } + + jsep_transports_by_name_.clear(); +} + +void TgJsepTransportController::SetIceRole_n(cricket::IceRole ice_role) { + RTC_DCHECK(network_thread_->IsCurrent()); + + ice_role_ = ice_role; + for (auto& dtls : GetDtlsTransports()) { + dtls->ice_transport()->SetIceRole(ice_role_); + } +} + +cricket::IceRole TgJsepTransportController::DetermineIceRole( + cricket::TgJsepTransport* jsep_transport, + const cricket::TransportInfo& transport_info, + SdpType type, + bool local) { + cricket::IceRole ice_role = ice_role_; + auto tdesc = transport_info.description; + if (local) { + // The initial offer side may use ICE Lite, in which case, per RFC5245 + // Section 5.1.1, the answer side should take the controlling role if it is + // in the full ICE mode. + // + // When both sides use ICE Lite, the initial offer side must take the + // controlling role, and this is the default logic implemented in + // SetLocalDescription in TgJsepTransportController. + if (jsep_transport->remote_description() && + jsep_transport->remote_description()->transport_desc.ice_mode == + cricket::ICEMODE_LITE && + ice_role_ == cricket::ICEROLE_CONTROLLED && + tdesc.ice_mode == cricket::ICEMODE_FULL) { + ice_role = cricket::ICEROLE_CONTROLLING; + } + + // Older versions of Chrome expect the ICE role to be re-determined when an + // ICE restart occurs, and also don't perform conflict resolution correctly, + // so for now we can't safely stop doing this, unless the application opts + // in by setting |config_.redetermine_role_on_ice_restart_| to false. See: + // https://bugs.chromium.org/p/chromium/issues/detail?id=628676 + // TODO(deadbeef): Remove this when these old versions of Chrome reach a low + // enough population. + if (config_.redetermine_role_on_ice_restart && + jsep_transport->local_description() && + cricket::IceCredentialsChanged( + jsep_transport->local_description()->transport_desc.ice_ufrag, + jsep_transport->local_description()->transport_desc.ice_pwd, + tdesc.ice_ufrag, tdesc.ice_pwd) && + // Don't change the ICE role if the remote endpoint is ICE lite; we + // should always be controlling in that case. + (!jsep_transport->remote_description() || + jsep_transport->remote_description()->transport_desc.ice_mode != + cricket::ICEMODE_LITE)) { + ice_role = (type == SdpType::kOffer) ? cricket::ICEROLE_CONTROLLING + : cricket::ICEROLE_CONTROLLED; + } + } else { + // If our role is cricket::ICEROLE_CONTROLLED and the remote endpoint + // supports only ice_lite, this local endpoint should take the CONTROLLING + // role. + // TODO(deadbeef): This is a session-level attribute, so it really shouldn't + // be in a TransportDescription in the first place... + if (ice_role_ == cricket::ICEROLE_CONTROLLED && + tdesc.ice_mode == cricket::ICEMODE_LITE) { + ice_role = cricket::ICEROLE_CONTROLLING; + } + + // If we use ICE Lite and the remote endpoint uses the full implementation + // of ICE, the local endpoint must take the controlled role, and the other + // side must be the controlling role. + if (jsep_transport->local_description() && + jsep_transport->local_description()->transport_desc.ice_mode == + cricket::ICEMODE_LITE && + ice_role_ == cricket::ICEROLE_CONTROLLING && + tdesc.ice_mode == cricket::ICEMODE_FULL) { + ice_role = cricket::ICEROLE_CONTROLLED; + } + } + + return ice_role; +} + +void TgJsepTransportController::OnTransportWritableState_n( + rtc::PacketTransportInternal* transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + RTC_LOG(LS_INFO) << " Transport " << transport->transport_name() + << " writability changed to " << transport->writable() + << "."; + UpdateAggregateStates_n(); +} + +void TgJsepTransportController::OnTransportReceivingState_n( + rtc::PacketTransportInternal* transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + UpdateAggregateStates_n(); +} + +void TgJsepTransportController::OnTransportGatheringState_n( + cricket::IceTransportInternal* transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + UpdateAggregateStates_n(); +} + +void TgJsepTransportController::OnTransportCandidateGathered_n( + cricket::IceTransportInternal* transport, + const cricket::Candidate& candidate) { + RTC_DCHECK(network_thread_->IsCurrent()); + + // We should never signal peer-reflexive candidates. + if (candidate.type() == cricket::PRFLX_PORT_TYPE) { + RTC_NOTREACHED(); + return; + } + std::string transport_name = transport->transport_name(); + invoker_.AsyncInvoke( + RTC_FROM_HERE, signaling_thread_, [this, transport_name, candidate] { + SignalIceCandidatesGathered(transport_name, {candidate}); + }); +} + +void TgJsepTransportController::OnTransportCandidateError_n( + cricket::IceTransportInternal* transport, + const cricket::IceCandidateErrorEvent& event) { + RTC_DCHECK(network_thread_->IsCurrent()); + + invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, + [this, event] { SignalIceCandidateError(event); }); +} +void TgJsepTransportController::OnTransportCandidatesRemoved_n( + cricket::IceTransportInternal* transport, + const cricket::Candidates& candidates) { + invoker_.AsyncInvoke( + RTC_FROM_HERE, signaling_thread_, + [this, candidates] { SignalIceCandidatesRemoved(candidates); }); +} +void TgJsepTransportController::OnTransportCandidatePairChanged_n( + const cricket::CandidatePairChangeEvent& event) { + invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, [this, event] { + SignalIceCandidatePairChanged(event); + }); +} + +void TgJsepTransportController::OnTransportRoleConflict_n( + cricket::IceTransportInternal* transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + // Note: since the role conflict is handled entirely on the network thread, + // we don't need to worry about role conflicts occurring on two ports at + // once. The first one encountered should immediately reverse the role. + cricket::IceRole reversed_role = (ice_role_ == cricket::ICEROLE_CONTROLLING) + ? cricket::ICEROLE_CONTROLLED + : cricket::ICEROLE_CONTROLLING; + RTC_LOG(LS_INFO) << "Got role conflict; switching to " + << (reversed_role == cricket::ICEROLE_CONTROLLING + ? "controlling" + : "controlled") + << " role."; + SetIceRole_n(reversed_role); +} + +void TgJsepTransportController::OnTransportStateChanged_n( + cricket::IceTransportInternal* transport) { + RTC_DCHECK(network_thread_->IsCurrent()); + RTC_LOG(LS_INFO) << transport->transport_name() << " Transport " + << transport->component() + << " state changed. Check if state is complete."; + UpdateAggregateStates_n(); +} + +void TgJsepTransportController::OnDataChannelTransportNegotiated_n( + cricket::TgJsepTransport* transport, + DataChannelTransportInterface* data_channel_transport) { + for (auto it : mid_to_transport_) { + if (it.second == transport) { + config_.transport_observer->OnTransportChanged( + it.first, transport->rtp_transport(), transport->RtpDtlsTransport(), + data_channel_transport); + } + } +} + +void TgJsepTransportController::UpdateAggregateStates_n() { + RTC_DCHECK(network_thread_->IsCurrent()); + + auto dtls_transports = GetDtlsTransports(); + cricket::IceConnectionState new_connection_state = + cricket::kIceConnectionConnecting; + PeerConnectionInterface::IceConnectionState new_ice_connection_state = + PeerConnectionInterface::IceConnectionState::kIceConnectionNew; + PeerConnectionInterface::PeerConnectionState new_combined_state = + PeerConnectionInterface::PeerConnectionState::kNew; + cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew; + bool any_failed = false; + bool all_connected = !dtls_transports.empty(); + bool all_completed = !dtls_transports.empty(); + bool any_gathering = false; + bool all_done_gathering = !dtls_transports.empty(); + + std::map ice_state_counts; + std::map dtls_state_counts; + + for (const auto& dtls : dtls_transports) { + any_failed = any_failed || dtls->ice_transport()->GetState() == + cricket::IceTransportState::STATE_FAILED; + all_connected = all_connected && dtls->writable(); + all_completed = + all_completed && dtls->writable() && + dtls->ice_transport()->GetState() == + cricket::IceTransportState::STATE_COMPLETED && + dtls->ice_transport()->GetIceRole() == cricket::ICEROLE_CONTROLLING && + dtls->ice_transport()->gathering_state() == + cricket::kIceGatheringComplete; + any_gathering = any_gathering || dtls->ice_transport()->gathering_state() != + cricket::kIceGatheringNew; + all_done_gathering = + all_done_gathering && dtls->ice_transport()->gathering_state() == + cricket::kIceGatheringComplete; + + dtls_state_counts[dtls->dtls_state()]++; + ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++; + } + + if (any_failed) { + new_connection_state = cricket::kIceConnectionFailed; + } else if (all_completed) { + new_connection_state = cricket::kIceConnectionCompleted; + } else if (all_connected) { + new_connection_state = cricket::kIceConnectionConnected; + } + if (ice_connection_state_ != new_connection_state) { + ice_connection_state_ = new_connection_state; + invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, + [this, new_connection_state] { + SignalIceConnectionState(new_connection_state); + }); + } + + // Compute the current RTCIceConnectionState as described in + // https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate. + // The PeerConnection is responsible for handling the "closed" state. + int total_ice_checking = ice_state_counts[IceTransportState::kChecking]; + int total_ice_connected = ice_state_counts[IceTransportState::kConnected]; + int total_ice_completed = ice_state_counts[IceTransportState::kCompleted]; + int total_ice_failed = ice_state_counts[IceTransportState::kFailed]; + int total_ice_disconnected = + ice_state_counts[IceTransportState::kDisconnected]; + int total_ice_closed = ice_state_counts[IceTransportState::kClosed]; + int total_ice_new = ice_state_counts[IceTransportState::kNew]; + int total_ice = (int)dtls_transports.size(); + + if (total_ice_failed > 0) { + // Any RTCIceTransports are in the "failed" state. + new_ice_connection_state = PeerConnectionInterface::kIceConnectionFailed; + } else if (total_ice_disconnected > 0) { + // None of the previous states apply and any RTCIceTransports are in the + // "disconnected" state. + new_ice_connection_state = + PeerConnectionInterface::kIceConnectionDisconnected; + } else if (total_ice_new + total_ice_closed == total_ice) { + // None of the previous states apply and all RTCIceTransports are in the + // "new" or "closed" state, or there are no transports. + new_ice_connection_state = PeerConnectionInterface::kIceConnectionNew; + } else if (total_ice_new + total_ice_checking > 0) { + // None of the previous states apply and any RTCIceTransports are in the + // "new" or "checking" state. + new_ice_connection_state = PeerConnectionInterface::kIceConnectionChecking; + } else if (total_ice_completed + total_ice_closed == total_ice || + all_completed) { + // None of the previous states apply and all RTCIceTransports are in the + // "completed" or "closed" state. + // + // TODO(https://bugs.webrtc.org/10356): The all_completed condition is added + // to mimic the behavior of the old ICE connection state, and should be + // removed once we get end-of-candidates signaling in place. + new_ice_connection_state = PeerConnectionInterface::kIceConnectionCompleted; + } else if (total_ice_connected + total_ice_completed + total_ice_closed == + total_ice) { + // None of the previous states apply and all RTCIceTransports are in the + // "connected", "completed" or "closed" state. + new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected; + } else { + RTC_NOTREACHED(); + } + + if (standardized_ice_connection_state_ != new_ice_connection_state) { + if (standardized_ice_connection_state_ == + PeerConnectionInterface::kIceConnectionChecking && + new_ice_connection_state == + PeerConnectionInterface::kIceConnectionCompleted) { + // Ensure that we never skip over the "connected" state. + invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, [this] { + SignalStandardizedIceConnectionState( + PeerConnectionInterface::kIceConnectionConnected); + }); + } + standardized_ice_connection_state_ = new_ice_connection_state; + invoker_.AsyncInvoke( + RTC_FROM_HERE, signaling_thread_, [this, new_ice_connection_state] { + SignalStandardizedIceConnectionState(new_ice_connection_state); + }); + } + + // Compute the current RTCPeerConnectionState as described in + // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectionstate. + // The PeerConnection is responsible for handling the "closed" state. + // Note that "connecting" is only a valid state for DTLS transports while + // "checking", "completed" and "disconnected" are only valid for ICE + // transports. + int total_connected = total_ice_connected + + dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTED]; + int total_dtls_connecting = + dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTING]; + int total_failed = + total_ice_failed + dtls_state_counts[cricket::DTLS_TRANSPORT_FAILED]; + int total_closed = + total_ice_closed + dtls_state_counts[cricket::DTLS_TRANSPORT_CLOSED]; + int total_new = + total_ice_new + dtls_state_counts[cricket::DTLS_TRANSPORT_NEW]; + int total_transports = total_ice * 2; + + if (total_failed > 0) { + // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state. + new_combined_state = PeerConnectionInterface::PeerConnectionState::kFailed; + } else if (total_ice_disconnected > 0) { + // None of the previous states apply and any RTCIceTransports or + // RTCDtlsTransports are in the "disconnected" state. + new_combined_state = + PeerConnectionInterface::PeerConnectionState::kDisconnected; + } else if (total_new + total_closed == total_transports) { + // None of the previous states apply and all RTCIceTransports and + // RTCDtlsTransports are in the "new" or "closed" state, or there are no + // transports. + new_combined_state = PeerConnectionInterface::PeerConnectionState::kNew; + } else if (total_new + total_dtls_connecting + total_ice_checking > 0) { + // None of the previous states apply and all RTCIceTransports or + // RTCDtlsTransports are in the "new", "connecting" or "checking" state. + new_combined_state = + PeerConnectionInterface::PeerConnectionState::kConnecting; + } else if (total_connected + total_ice_completed + total_closed == + total_transports) { + // None of the previous states apply and all RTCIceTransports and + // RTCDtlsTransports are in the "connected", "completed" or "closed" state. + new_combined_state = + PeerConnectionInterface::PeerConnectionState::kConnected; + } else { + RTC_NOTREACHED(); + } + + if (combined_connection_state_ != new_combined_state) { + combined_connection_state_ = new_combined_state; + invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, + [this, new_combined_state] { + SignalConnectionState(new_combined_state); + }); + } + + if (all_done_gathering) { + new_gathering_state = cricket::kIceGatheringComplete; + } else if (any_gathering) { + new_gathering_state = cricket::kIceGatheringGathering; + } + if (ice_gathering_state_ != new_gathering_state) { + ice_gathering_state_ = new_gathering_state; + invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, + [this, new_gathering_state] { + SignalIceGatheringState(new_gathering_state); + }); + } +} + +void TgJsepTransportController::OnRtcpPacketReceived_n( + rtc::CopyOnWriteBuffer* packet, + int64_t packet_time_us) { + RTC_DCHECK(config_.rtcp_handler); + config_.rtcp_handler(*packet, packet_time_us); +} + +void TgJsepTransportController::OnDtlsHandshakeError( + rtc::SSLHandshakeError error) { + SignalDtlsHandshakeError(error); +} + +absl::optional +TgJsepTransportController::GetTransportParameters(const std::string& mid) { + if (!(config_.use_datagram_transport || + config_.use_datagram_transport_for_data_channels)) { + return absl::nullopt; + } + + cricket::TgJsepTransport* transport = GetJsepTransportForMid(mid); + if (transport) { + absl::optional params = + transport->GetTransportParameters(); + if (params) { + params->protocol = config_.media_transport_factory->GetTransportName(); + } + return params; + } + + RTC_DCHECK(!local_desc_ && !remote_desc_) + << "TgJsepTransport should exist for every mid once any description is set"; + + if (config_.use_datagram_transport_for_data_channels_receive_only) { + return absl::nullopt; + } + + // Need to generate a transport for the offer. + if (!offer_datagram_transport_) { + webrtc::MediaTransportSettings settings; + settings.is_caller = true; + settings.pre_shared_key = rtc::CreateRandomString(32); + settings.event_log = config_.event_log; + auto datagram_transport_or_error = + config_.media_transport_factory->CreateDatagramTransport( + network_thread_, settings); + + if (datagram_transport_or_error.ok()) { + offer_datagram_transport_ = + std::move(datagram_transport_or_error.value()); + } else { + RTC_LOG(LS_INFO) << "Unable to create datagram transport, error=" + << datagram_transport_or_error.error().message(); + } + } + + // We have prepared a transport for the offer, and can now use its parameters. + cricket::OpaqueTransportParameters params; + params.parameters = offer_datagram_transport_->GetTransportParameters(); + params.protocol = config_.media_transport_factory->GetTransportName(); + return params; +} + +} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h new file mode 100644 index 0000000000..2777ab87a2 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h @@ -0,0 +1,478 @@ +/* + * Copyright 2017 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 TG_PC_JSEP_TRANSPORT_CONTROLLER_H_ +#define TG_PC_JSEP_TRANSPORT_CONTROLLER_H_ + +#include +#include +#include +#include +#include + +#include "api/candidate.h" +#include "api/crypto/crypto_options.h" +#include "api/ice_transport_factory.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/transport/media/media_transport_config.h" +#include "media/sctp/sctp_transport_internal.h" +#include "p2p/base/dtls_transport.h" +#include "p2p/base/dtls_transport_factory.h" +#include "p2p/base/p2p_transport_channel.h" +#include "pc/channel.h" +#include "pc/dtls_srtp_transport.h" +#include "pc/dtls_transport.h" +#include "pc/rtp_transport.h" +#include "pc/srtp_transport.h" +#include "rtc_base/async_invoker.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +#include "tg_jsep_transport.h" +#include "tg_rtp_transport.h" + +namespace rtc { +class Thread; +class PacketTransportInternal; +} // namespace rtc + +namespace webrtc { + +class TgJsepTransportController : public sigslot::has_slots<> { + public: + // Used when the RtpTransport/DtlsTransport of the m= section is changed + // because the section is rejected or BUNDLE is enabled. + class Observer { + public: + virtual ~Observer() {} + + // Returns true if media associated with |mid| was successfully set up to be + // demultiplexed on |rtp_transport|. Could return false if two bundled m= + // sections use the same SSRC, for example. + // + // If a data channel transport must be negotiated, |data_channel_transport| + // and |negotiation_state| indicate negotiation status. If + // |data_channel_transport| is null, the data channel transport should not + // be used. Otherwise, the value is a pointer to the transport to be used + // for data channels on |mid|, if any. + // + // The observer should not send data on |data_channel_transport| until + // |negotiation_state| is provisional or final. It should not delete + // |data_channel_transport| or any fallback transport until + // |negotiation_state| is final. + virtual bool OnTransportChanged( + const std::string& mid, + RtpTransportInternal* rtp_transport, + rtc::scoped_refptr dtls_transport, + DataChannelTransportInterface* data_channel_transport) = 0; + }; + + struct Config { + // If |redetermine_role_on_ice_restart| is true, ICE role is redetermined + // upon setting a local transport description that indicates an ICE + // restart. + bool redetermine_role_on_ice_restart = true; + rtc::SSLProtocolVersion ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; + // |crypto_options| is used to determine if created DTLS transports + // negotiate GCM crypto suites or not. + webrtc::CryptoOptions crypto_options; + PeerConnectionInterface::BundlePolicy bundle_policy = + PeerConnectionInterface::kBundlePolicyBalanced; + PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy = + PeerConnectionInterface::kRtcpMuxPolicyRequire; + bool disable_encryption = false; + bool enable_external_auth = false; + // Used to inject the ICE/DTLS transports created externally. + webrtc::IceTransportFactory* ice_transport_factory = nullptr; + cricket::DtlsTransportFactory* dtls_transport_factory = nullptr; + Observer* transport_observer = nullptr; + // Must be provided and valid for the lifetime of the + // TgJsepTransportController instance. + std::function + rtcp_handler; + bool active_reset_srtp_params = false; + RtcEventLog* event_log = nullptr; + + // Factory for SCTP transports. + cricket::SctpTransportInternalFactory* sctp_factory = nullptr; + + // Whether an RtpMediaTransport should be created as default, when no + // MediaTransportFactory is provided. + bool use_rtp_media_transport = false; + + // Use encrypted datagram transport to send packets. + bool use_datagram_transport = false; + + // Use datagram transport's implementation of data channels instead of SCTP. + bool use_datagram_transport_for_data_channels = false; + + // Whether |use_datagram_transport_for_data_channels| applies to outgoing + // calls. If true, |use_datagram_transport_for_data_channels| applies only + // to incoming calls. + bool use_datagram_transport_for_data_channels_receive_only = false; + + // Optional media transport factory (experimental). If provided it will be + // used to create datagram_transport (as long as either + // |use_datagram_transport| or + // |use_datagram_transport_for_data_channels| is set to true). However, + // whether it will be used to send / receive audio and video frames instead + // of RTP is determined by |use_datagram_transport|. Note that currently + // datagram_transport co-exists with RTP / RTCP transports and may use the + // same underlying ICE transport. + MediaTransportFactory* media_transport_factory = nullptr; + }; + + // The ICE related events are signaled on the |signaling_thread|. + // All the transport related methods are called on the |network_thread|. + TgJsepTransportController(rtc::Thread* signaling_thread, + rtc::Thread* network_thread, + cricket::PortAllocator* port_allocator, + AsyncResolverFactory* async_resolver_factory, + Config config); + virtual ~TgJsepTransportController(); + + // The main method to be called; applies a description at the transport + // level, creating/destroying transport objects as needed and updating their + // properties. This includes RTP, DTLS, and ICE (but not SCTP). At least not + // yet? May make sense to in the future. + RTCError SetLocalDescription(SdpType type, + const cricket::SessionDescription* description); + + RTCError SetRemoteDescription(SdpType type, + const cricket::SessionDescription* description); + + // Get transports to be used for the provided |mid|. If bundling is enabled, + // calling GetRtpTransport for multiple MIDs may yield the same object. + RtpTransportInternal* GetRtpTransport(const std::string& mid) const; + cricket::DtlsTransportInternal* GetDtlsTransport(const std::string& mid); + const cricket::DtlsTransportInternal* GetRtcpDtlsTransport( + const std::string& mid) const; + // Gets the externally sharable version of the DtlsTransport. + rtc::scoped_refptr LookupDtlsTransportByMid( + const std::string& mid); + rtc::scoped_refptr GetSctpTransport( + const std::string& mid) const; + + MediaTransportConfig GetMediaTransportConfig(const std::string& mid) const; + + DataChannelTransportInterface* GetDataChannelTransport( + const std::string& mid) const; + + /********************* + * ICE-related methods + ********************/ + // This method is public to allow PeerConnection to update it from + // SetConfiguration. + void SetIceConfig(const cricket::IceConfig& config); + // 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& mid) const; + // Start gathering candidates for any new transports, or transports doing an + // ICE restart. + void MaybeStartGathering(); + RTCError AddRemoteCandidates( + const std::string& mid, + const std::vector& candidates); + RTCError RemoveRemoteCandidates( + const std::vector& candidates); + + /********************** + * DTLS-related methods + *********************/ + // Specifies the identity to use in this session. + // Can only be called once. + bool SetLocalCertificate( + const rtc::scoped_refptr& certificate); + rtc::scoped_refptr GetLocalCertificate( + const std::string& mid) const; + // Caller owns returned certificate chain. This method mainly exists for + // stats reporting. + std::unique_ptr GetRemoteSSLCertChain( + const std::string& mid) const; + // Get negotiated role, if one has been negotiated. + absl::optional GetDtlsRole(const std::string& mid) const; + + // TODO(deadbeef): GetStats isn't const because all the way down to + // OpenSSLStreamAdapter, GetSslCipherSuite and GetDtlsSrtpCryptoSuite are not + // const. Fix this. + bool GetStats(const std::string& mid, cricket::TransportStats* stats); + + bool initial_offerer() const { return initial_offerer_ && *initial_offerer_; } + + void SetActiveResetSrtpParams(bool active_reset_srtp_params); + + // Allows to overwrite the settings from config. You may set or reset the + // media transport configuration on the jsep transport controller, as long as + // you did not call 'GetMediaTransport' or 'MaybeCreateJsepTransport'. Once + // Jsep transport is created, you can't change this setting. + void SetMediaTransportSettings( + bool use_datagram_transport, + bool use_datagram_transport_for_data_channels, + bool use_datagram_transport_for_data_channels_receive_only); + + // TODO(elrello): For now the rollback only removes mid to transport mappings + // and deletes unused transports, but doesn't consider anything more complex. + void RollbackTransportForMids(const std::vector& mids); + + // Gets the transport parameters for the transport identified by |mid|. + // If |mid| is bundled, returns the parameters for the bundled transport. + // If the transport for |mid| has not been created yet, it may be allocated in + // order to generate transport parameters. + absl::optional GetTransportParameters( + const std::string& mid); + + // All of these signals are fired on the signaling thread. + + // If any transport failed => failed, + // Else if all completed => completed, + // Else if all connected => connected, + // Else => connecting + sigslot::signal1 SignalIceConnectionState; + + sigslot::signal1 + SignalConnectionState; + sigslot::signal1 + SignalStandardizedIceConnectionState; + + // If all transports done gathering => complete, + // Else if any are gathering => gathering, + // Else => new + sigslot::signal1 SignalIceGatheringState; + + // (mid, candidates) + sigslot::signal2&> + SignalIceCandidatesGathered; + + sigslot::signal1 + SignalIceCandidateError; + + sigslot::signal1&> + SignalIceCandidatesRemoved; + + sigslot::signal1 + SignalIceCandidatePairChanged; + + sigslot::signal1 SignalDtlsHandshakeError; + + private: + RTCError ApplyDescription_n(bool local, + SdpType type, + const cricket::SessionDescription* description); + RTCError ValidateAndMaybeUpdateBundleGroup( + bool local, + SdpType type, + const cricket::SessionDescription* description); + RTCError ValidateContent(const cricket::ContentInfo& content_info); + + void HandleRejectedContent(const cricket::ContentInfo& content_info, + const cricket::SessionDescription* description); + bool HandleBundledContent(const cricket::ContentInfo& content_info); + + bool SetTransportForMid(const std::string& mid, + cricket::TgJsepTransport* jsep_transport); + void RemoveTransportForMid(const std::string& mid); + + cricket::TgJsepTransportDescription CreateJsepTransportDescription( + const cricket::ContentInfo& content_info, + const cricket::TransportInfo& transport_info, + const std::vector& encrypted_extension_ids, + int rtp_abs_sendtime_extn_id, + absl::optional media_alt_protocol, + absl::optional data_alt_protocol); + + absl::optional bundled_mid() const { + absl::optional bundled_mid; + if (bundle_group_ && bundle_group_->FirstContentName()) { + bundled_mid = *(bundle_group_->FirstContentName()); + } + return bundled_mid; + } + + bool IsBundled(const std::string& mid) const { + return bundle_group_ && bundle_group_->HasContentName(mid); + } + + bool ShouldUpdateBundleGroup(SdpType type, + const cricket::SessionDescription* description); + + std::vector MergeEncryptedHeaderExtensionIdsForBundle( + const cricket::SessionDescription* description); + std::vector GetEncryptedHeaderExtensionIds( + const cricket::ContentInfo& content_info); + + // Extracts the alt-protocol settings that apply to the bundle group. + RTCError GetAltProtocolsForBundle( + const cricket::SessionDescription* description, + absl::optional* media_alt_protocol, + absl::optional* data_alt_protocol); + + int GetRtpAbsSendTimeHeaderExtensionId( + const cricket::ContentInfo& content_info); + + // This method takes the BUNDLE group into account. If the TgJsepTransport is + // destroyed because of BUNDLE, it would return the transport which other + // transports are bundled on (In current implementation, it is the first + // content in the BUNDLE group). + const cricket::TgJsepTransport* GetJsepTransportForMid( + const std::string& mid) const; + cricket::TgJsepTransport* GetJsepTransportForMid(const std::string& mid); + + // Get the JsepTransport without considering the BUNDLE group. Return nullptr + // if the JsepTransport is destroyed. + const cricket::TgJsepTransport* GetJsepTransportByName( + const std::string& transport_name) const; + cricket::TgJsepTransport* GetJsepTransportByName( + const std::string& transport_name); + + // Creates jsep transport. Noop if transport is already created. + // Transport is created either during SetLocalDescription (|local| == true) or + // during SetRemoteDescription (|local| == false). Passing |local| helps to + // differentiate initiator (caller) from answerer (callee). + RTCError MaybeCreateJsepTransport( + bool local, + const cricket::ContentInfo& content_info, + const cricket::SessionDescription& description); + + // Creates datagram transport if config wants to use it, and a=x-mt line is + // present for the current media transport. Returned + // DatagramTransportInterface is not connected, and must be connected to ICE. + // You must call |GenerateOrGetLastMediaTransportOffer| on the caller before + // calling MaybeCreateDatagramTransport. + std::unique_ptr + MaybeCreateDatagramTransport(const cricket::ContentInfo& content_info, + const cricket::SessionDescription& description, + bool local); + + void MaybeDestroyJsepTransport(const std::string& mid); + void DestroyAllJsepTransports_n(); + + void SetIceRole_n(cricket::IceRole ice_role); + + cricket::IceRole DetermineIceRole( + cricket::TgJsepTransport* jsep_transport, + const cricket::TransportInfo& transport_info, + SdpType type, + bool local); + + std::unique_ptr CreateDtlsTransport( + const cricket::ContentInfo& content_info, + cricket::IceTransportInternal* ice, + DatagramTransportInterface* datagram_transport); + rtc::scoped_refptr CreateIceTransport( + const std::string& transport_name, + bool rtcp); + + std::unique_ptr CreateUnencryptedRtpTransport( + const std::string& transport_name, + rtc::PacketTransportInternal* rtp_packet_transport, + rtc::PacketTransportInternal* rtcp_packet_transport); + std::unique_ptr CreateSdesTransport( + const std::string& transport_name, + cricket::DtlsTransportInternal* rtp_dtls_transport, + cricket::DtlsTransportInternal* rtcp_dtls_transport); + std::unique_ptr CreateDtlsSrtpTransport( + const std::string& transport_name, + cricket::DtlsTransportInternal* rtp_dtls_transport, + cricket::DtlsTransportInternal* rtcp_dtls_transport); + + // Collect all the DtlsTransports, including RTP and RTCP, from the + // JsepTransports. JsepTransportController can iterate all the DtlsTransports + // and update the aggregate states. + std::vector GetDtlsTransports(); + + // Handlers for signals from Transport. + void OnTransportWritableState_n(rtc::PacketTransportInternal* transport); + void OnTransportReceivingState_n(rtc::PacketTransportInternal* transport); + void OnTransportGatheringState_n(cricket::IceTransportInternal* transport); + void OnTransportCandidateGathered_n(cricket::IceTransportInternal* transport, + const cricket::Candidate& candidate); + void OnTransportCandidateError_n( + cricket::IceTransportInternal* transport, + const cricket::IceCandidateErrorEvent& event); + void OnTransportCandidatesRemoved_n(cricket::IceTransportInternal* transport, + const cricket::Candidates& candidates); + void OnTransportRoleConflict_n(cricket::IceTransportInternal* transport); + void OnTransportStateChanged_n(cricket::IceTransportInternal* transport); + void OnTransportCandidatePairChanged_n( + const cricket::CandidatePairChangeEvent& event); + void OnDataChannelTransportNegotiated_n( + cricket::TgJsepTransport* transport, + DataChannelTransportInterface* data_channel_transport); + + void UpdateAggregateStates_n(); + + void OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer* packet, + int64_t packet_time_us); + + void OnDtlsHandshakeError(rtc::SSLHandshakeError error); + + rtc::Thread* const signaling_thread_ = nullptr; + rtc::Thread* const network_thread_ = nullptr; + cricket::PortAllocator* const port_allocator_ = nullptr; + AsyncResolverFactory* const async_resolver_factory_ = nullptr; + + std::map> + jsep_transports_by_name_; + // This keeps track of the mapping between media section + // (BaseChannel/SctpTransport) and the TgJsepTransport underneath. + std::map mid_to_transport_; + + // Aggregate states for Transports. + // standardized_ice_connection_state_ is intended to replace + // ice_connection_state, see bugs.webrtc.org/9308 + cricket::IceConnectionState ice_connection_state_ = + cricket::kIceConnectionConnecting; + PeerConnectionInterface::IceConnectionState + standardized_ice_connection_state_ = + PeerConnectionInterface::kIceConnectionNew; + PeerConnectionInterface::PeerConnectionState combined_connection_state_ = + PeerConnectionInterface::PeerConnectionState::kNew; + cricket::IceGatheringState ice_gathering_state_ = cricket::kIceGatheringNew; + + Config config_; + + // Early on in the call we don't know if datagram transport is going to be + // used, but we need to get the server-supported parameters to add to an SDP. + // This server datagram transport will be promoted to the used datagram + // transport after the local description is set, and the ownership will be + // transferred to the actual TgJsepTransport. This "offer" datagram transport is + // not created if it's done on the party that provides answer. This offer + // datagram transport is only created once at the beginning of the connection, + // and never again. + std::unique_ptr offer_datagram_transport_ = + nullptr; + + const cricket::SessionDescription* local_desc_ = nullptr; + const cricket::SessionDescription* remote_desc_ = nullptr; + absl::optional initial_offerer_; + + absl::optional bundle_group_; + + cricket::IceConfig ice_config_; + cricket::IceRole ice_role_ = cricket::ICEROLE_CONTROLLING; + uint64_t ice_tiebreaker_ = rtc::CreateRandomId64(); + rtc::scoped_refptr certificate_; + rtc::AsyncInvoker invoker_; + + RTC_DISALLOW_COPY_AND_ASSIGN(TgJsepTransportController); +}; + +} // namespace webrtc + +#endif // PC_JSEP_TRANSPORT_CONTROLLER_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp new file mode 100644 index 0000000000..bad3bcd2af --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp @@ -0,0 +1,7234 @@ +/* + * 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 "tg_peer_connection.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/strings/match.h" +#include "api/jsep_ice_candidate.h" +#include "api/jsep_session_description.h" +#include "api/media_stream_proxy.h" +#include "api/media_stream_track_proxy.h" +#include "api/rtc_error.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/rtc_event_log_output_file.h" +#include "api/rtp_parameters.h" +#include "api/uma_metrics.h" +#include "api/video/builtin_video_bitrate_allocator_factory.h" +#include "call/call.h" +#include "logging/rtc_event_log/ice_logger.h" +#include "media/base/rid_description.h" +#include "media/sctp/sctp_transport.h" +#include "pc/audio_rtp_receiver.h" +#include "pc/audio_track.h" +#include "pc/channel.h" +#include "pc/channel_manager.h" +#include "pc/dtmf_sender.h" +#include "pc/media_stream.h" +#include "pc/media_stream_observer.h" +#include "pc/remote_audio_source.h" +#include "pc/rtp_media_utils.h" +#include "pc/rtp_receiver.h" +#include "pc/rtp_sender.h" +#include "pc/sctp_transport.h" +#include "pc/sctp_utils.h" +#include "pc/sdp_utils.h" +#include "pc/stream_collection.h" +#include "pc/video_rtp_receiver.h" +#include "pc/video_track.h" +#include "rtc_base/bind.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/fallthrough.h" +#include "rtc_base/trace_event.h" +#include "system_wrappers/include/clock.h" +#include "system_wrappers/include/field_trial.h" +#include "system_wrappers/include/metrics.h" + +#include "tg_jsep_transport.h" +#include "tg_jsep_transport_controller.h" + +using cricket::ContentInfo; +using cricket::ContentInfos; +using cricket::MediaContentDescription; +using cricket::MediaProtocolType; +using cricket::RidDescription; +using cricket::RidDirection; +using cricket::SessionDescription; +using cricket::SimulcastDescription; +using cricket::SimulcastLayer; +using cricket::SimulcastLayerList; +using cricket::StreamParams; +using cricket::TransportInfo; + +using cricket::LOCAL_PORT_TYPE; +using cricket::PRFLX_PORT_TYPE; +using cricket::RELAY_PORT_TYPE; +using cricket::STUN_PORT_TYPE; + +namespace webrtc { + +// Error messages +const char kBundleWithoutRtcpMux[] = + "rtcp-mux must be enabled when BUNDLE " + "is enabled."; +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 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."; + +namespace { + +// Field trials. +// Controls datagram transport support. +const char kDatagramTransportFieldTrial[] = "WebRTC-DatagramTransport"; +// Controls datagram transport data channel support. +const char kDatagramTransportDataChannelFieldTrial[] = + "WebRTC-DatagramTransportDataChannels"; + +// UMA metric names. +const char kSimulcastVersionApplyLocalDescription[] = + "WebRTC.PeerConnection.Simulcast.ApplyLocalDescription"; +const char kSimulcastVersionApplyRemoteDescription[] = + "WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription"; +const char kSimulcastNumberOfEncodings[] = + "WebRTC.PeerConnection.Simulcast.NumberOfSendEncodings"; +const char kSimulcastDisabled[] = "WebRTC.PeerConnection.Simulcast.Disabled"; + +static const char kDefaultStreamId[] = "default"; +static const char kDefaultAudioSenderId[] = "defaulta0"; +static const char kDefaultVideoSenderId[] = "defaultv0"; + +// The length of RTCP CNAMEs. +static const int kRtcpCnameLength = 16; + +enum { + MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0, + MSG_SET_SESSIONDESCRIPTION_FAILED, + MSG_CREATE_SESSIONDESCRIPTION_FAILED, + MSG_GETSTATS, + MSG_REPORT_USAGE_PATTERN, +}; + +static const int REPORT_USAGE_PATTERN_DELAY_MS = 60000; + +struct SetSessionDescriptionMsg : public rtc::MessageData { + explicit SetSessionDescriptionMsg( + webrtc::SetSessionDescriptionObserver* observer) + : observer(observer) {} + + rtc::scoped_refptr observer; + RTCError error; +}; + +struct CreateSessionDescriptionMsg : public rtc::MessageData { + explicit CreateSessionDescriptionMsg( + webrtc::CreateSessionDescriptionObserver* observer) + : observer(observer) {} + + rtc::scoped_refptr observer; + RTCError error; +}; + +struct GetStatsMsg : public rtc::MessageData { + GetStatsMsg(webrtc::StatsObserver* observer, + webrtc::MediaStreamTrackInterface* track) + : observer(observer), track(track) {} + rtc::scoped_refptr observer; + rtc::scoped_refptr track; +}; + +// Check if we can send |new_stream| on a PeerConnection. +bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams, + webrtc::MediaStreamInterface* new_stream) { + if (!new_stream || !current_streams) { + return false; + } + if (current_streams->find(new_stream->id()) != nullptr) { + RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id() + << " is already added."; + return false; + } + return true; +} + +// If the direction is "recvonly" or "inactive", treat the description +// as containing no streams. +// See: https://code.google.com/p/webrtc/issues/detail?id=5054 +std::vector GetActiveStreams( + const cricket::MediaContentDescription* desc) { + return RtpTransceiverDirectionHasSend(desc->direction()) + ? desc->streams() + : std::vector(); +} + +bool IsValidOfferToReceiveMedia(int value) { + typedef PeerConnectionInterface::RTCOfferAnswerOptions Options; + return (value >= Options::kUndefined) && + (value <= Options::kMaxOfferToReceiveMedia); +} + +// Add options to |[audio/video]_media_description_options| from |senders|. +void AddPlanBRtpSenderOptions( + const std::vector>>& senders, + cricket::MediaDescriptionOptions* audio_media_description_options, + cricket::MediaDescriptionOptions* video_media_description_options, + int num_sim_layers) { + for (const auto& sender : senders) { + if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { + if (audio_media_description_options) { + audio_media_description_options->AddAudioSender( + sender->id(), sender->internal()->stream_ids()); + } + } else { + RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO); + if (video_media_description_options) { + video_media_description_options->AddVideoSender( + sender->id(), sender->internal()->stream_ids(), {}, + SimulcastLayerList(), num_sim_layers); + } + } + } +} + +// Add options to |session_options| from |rtp_data_channels|. +void AddRtpDataChannelOptions( + const std::map>& + rtp_data_channels, + cricket::MediaDescriptionOptions* data_media_description_options) { + if (!data_media_description_options) { + return; + } + // Check for data channels. + for (const auto& kv : rtp_data_channels) { + const DataChannel* channel = kv.second; + if (channel->state() == DataChannel::kConnecting || + channel->state() == DataChannel::kOpen) { + // Legacy RTP data channels are signaled with the track/stream ID set to + // the data channel's label. + data_media_description_options->AddRtpDataChannel(channel->label(), + channel->label()); + } + } +} + +uint32_t ConvertIceTransportTypeToCandidateFilter( + PeerConnectionInterface::IceTransportsType type) { + switch (type) { + case PeerConnectionInterface::kNone: + return cricket::CF_NONE; + case PeerConnectionInterface::kRelay: + return cricket::CF_RELAY; + case PeerConnectionInterface::kNoHost: + return (cricket::CF_ALL & ~cricket::CF_HOST); + case PeerConnectionInterface::kAll: + return cricket::CF_ALL; + default: + RTC_NOTREACHED(); + } + return cricket::CF_NONE; +} + +std::string GetSignalingStateString( + PeerConnectionInterface::SignalingState state) { + switch (state) { + case PeerConnectionInterface::kStable: + return "kStable"; + case PeerConnectionInterface::kHaveLocalOffer: + return "kHaveLocalOffer"; + case PeerConnectionInterface::kHaveLocalPrAnswer: + return "kHavePrAnswer"; + case PeerConnectionInterface::kHaveRemoteOffer: + return "kHaveRemoteOffer"; + case PeerConnectionInterface::kHaveRemotePrAnswer: + return "kHaveRemotePrAnswer"; + case PeerConnectionInterface::kClosed: + return "kClosed"; + } + RTC_NOTREACHED(); + return ""; +} + +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_hostname = + !local.address().hostname().empty() && local.address().IsUnresolvedIP(); + bool remote_hostname = !remote.address().hostname().empty() && + remote.address().IsUnresolvedIP(); + bool local_private = IPIsPrivate(local.address().ipaddr()); + bool remote_private = IPIsPrivate(remote.address().ipaddr()); + if (local_hostname) { + if (remote_hostname) { + return kIceCandidatePairHostNameHostName; + } else if (remote_private) { + return kIceCandidatePairHostNameHostPrivate; + } else { + return kIceCandidatePairHostNameHostPublic; + } + } else if (local_private) { + if (remote_hostname) { + return kIceCandidatePairHostPrivateHostName; + } else if (remote_private) { + return kIceCandidatePairHostPrivateHostPrivate; + } else { + return kIceCandidatePairHostPrivateHostPublic; + } + } else { + if (remote_hostname) { + return kIceCandidatePairHostPublicHostName; + } 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; +} + +// Logic to decide if an m= section can be recycled. This means that the new +// m= section is not rejected, but the old local or remote m= section is +// rejected. |old_content_one| and |old_content_two| refer to the m= section +// of the old remote and old local descriptions in no particular order. +// We need to check both the old local and remote because either +// could be the most current from the latest negotation. +bool IsMediaSectionBeingRecycled(SdpType type, + const ContentInfo& content, + const ContentInfo* old_content_one, + const ContentInfo* old_content_two) { + return type == SdpType::kOffer && !content.rejected && + ((old_content_one && old_content_one->rejected) || + (old_content_two && old_content_two->rejected)); +} + +// Verify that the order of media sections in |new_desc| matches +// |current_desc|. The number of m= sections in |new_desc| should be no +// less than |current_desc|. In the case of checking an answer's +// |new_desc|, the |current_desc| is the last offer that was set as the +// local or remote. In the case of checking an offer's |new_desc| we +// check against the local and remote descriptions stored from the last +// negotiation, because either of these could be the most up to date for +// possible rejected m sections. These are the |current_desc| and +// |secondary_current_desc|. +bool MediaSectionsInSameOrder(const SessionDescription& current_desc, + const SessionDescription* secondary_current_desc, + const SessionDescription& new_desc, + const SdpType type) { + if (current_desc.contents().size() > new_desc.contents().size()) { + return false; + } + + for (size_t i = 0; i < current_desc.contents().size(); ++i) { + const cricket::ContentInfo* secondary_content_info = nullptr; + if (secondary_current_desc && + i < secondary_current_desc->contents().size()) { + secondary_content_info = &secondary_current_desc->contents()[i]; + } + if (IsMediaSectionBeingRecycled(type, new_desc.contents()[i], + ¤t_desc.contents()[i], + secondary_content_info)) { + // For new offer descriptions, if the media section can be recycled, it's + // valid for the MID and media type to change. + continue; + } + if (new_desc.contents()[i].name != current_desc.contents()[i].name) { + return false; + } + const MediaContentDescription* new_desc_mdesc = + new_desc.contents()[i].media_description(); + const MediaContentDescription* current_desc_mdesc = + current_desc.contents()[i].media_description(); + if (new_desc_mdesc->type() != current_desc_mdesc->type()) { + return false; + } + } + return true; +} + +bool MediaSectionsHaveSameCount(const SessionDescription& desc1, + const SessionDescription& desc2) { + return desc1.contents().size() == desc2.contents().size(); +} + +void NoteKeyProtocolAndMedia(KeyExchangeProtocolType protocol_type, + cricket::MediaType media_type) { + // Array of structs needed to map {KeyExchangeProtocolType, + // cricket::MediaType} to KeyExchangeProtocolMedia without using std::map in + // order to avoid -Wglobal-constructors and -Wexit-time-destructors. + static constexpr struct { + KeyExchangeProtocolType protocol_type; + cricket::MediaType media_type; + KeyExchangeProtocolMedia protocol_media; + } kEnumCounterKeyProtocolMediaMap[] = { + {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_AUDIO, + kEnumCounterKeyProtocolMediaTypeDtlsAudio}, + {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_VIDEO, + kEnumCounterKeyProtocolMediaTypeDtlsVideo}, + {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_DATA, + kEnumCounterKeyProtocolMediaTypeDtlsData}, + {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_AUDIO, + kEnumCounterKeyProtocolMediaTypeSdesAudio}, + {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_VIDEO, + kEnumCounterKeyProtocolMediaTypeSdesVideo}, + {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_DATA, + kEnumCounterKeyProtocolMediaTypeSdesData}, + }; + + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocol", protocol_type, + kEnumCounterKeyProtocolMax); + + for (const auto& i : kEnumCounterKeyProtocolMediaMap) { + if (i.protocol_type == protocol_type && i.media_type == media_type) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocolByMedia", + i.protocol_media, + kEnumCounterKeyProtocolMediaTypeMax); + } + } +} + +void NoteAddIceCandidateResult(int result) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result, + kAddIceCandidateMax); +} + +// 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 TgJsepTransport negotiation, and backstopped +// by Channel's |srtp_required| check. +RTCError VerifyCrypto(const SessionDescription* desc, bool dtls_enabled) { + const cricket::ContentGroup* bundle = + desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + for (const cricket::ContentInfo& content_info : desc->contents()) { + if (content_info.rejected) { + continue; + } + // Note what media is used with each crypto protocol, for all sections. + NoteKeyProtocolAndMedia(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls + : webrtc::kEnumCounterKeyProtocolSdes, + content_info.media_description()->type()); + const std::string& mid = content_info.name; + if (bundle && bundle->HasContentName(mid) && + mid != *(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 = content_info.media_description(); + const TransportInfo* tinfo = desc->GetTransportInfoByName(mid); + if (!media || !tinfo) { + // Something is not right. + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); + } + if (dtls_enabled) { + if (!tinfo->description.identity_fingerprint) { + RTC_LOG(LS_WARNING) + << "Session description must have DTLS fingerprint if " + "DTLS enabled."; + return RTCError(RTCErrorType::INVALID_PARAMETER, + kSdpWithoutDtlsFingerprint); + } + } else { + if (media->cryptos().empty()) { + RTC_LOG(LS_WARNING) + << "Session description must have SDES when DTLS disabled."; + return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto); + } + } + } + return RTCError::OK(); +} + +// 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); + for (const cricket::ContentInfo& content_info : desc->contents()) { + if (content_info.rejected) { + continue; + } + const std::string& mid = content_info.name; + if (bundle && bundle->HasContentName(mid) && + mid != *(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(mid); + if (!tinfo) { + // Something is not right. + RTC_LOG(LS_ERROR) << kInvalidSdp; + return false; + } + if (tinfo->description.ice_ufrag.empty() || + tinfo->description.ice_pwd.empty()) { + RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; + return false; + } + } + return true; +} + +// 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)) { + RTC_LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name + << "."; + return true; + } + return false; +} + +// Generates a string error message for SetLocalDescription/SetRemoteDescription +// from an RTCError. +std::string GetSetDescriptionErrorMessage(cricket::ContentSource source, + SdpType type, + const RTCError& error) { + rtc::StringBuilder oss; + oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote") + << " " << SdpTypeToString(type) << " sdp: " << error.message(); + return oss.Release(); +} + +std::string GetStreamIdsString(rtc::ArrayView stream_ids) { + std::string output = "streams=["; + const char* separator = ""; + for (const auto& stream_id : stream_ids) { + output.append(separator).append(stream_id); + separator = ", "; + } + output.append("]"); + return output; +} + +absl::optional RTCConfigurationToIceConfigOptionalInt( + int rtc_configuration_parameter) { + if (rtc_configuration_parameter == + webrtc::PeerConnectionInterface::RTCConfiguration::kUndefined) { + return absl::nullopt; + } + return rtc_configuration_parameter; +} + +void ReportSimulcastApiVersion(const char* name, + const SessionDescription& session) { + bool has_legacy = false; + bool has_spec_compliant = false; + for (const ContentInfo& content : session.contents()) { + if (!content.media_description()) { + continue; + } + has_spec_compliant |= content.media_description()->HasSimulcast(); + for (const StreamParams& sp : content.media_description()->streams()) { + has_legacy |= sp.has_ssrc_group(cricket::kSimSsrcGroupSemantics); + } + } + + if (has_legacy) { + RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionLegacy, + kSimulcastApiVersionMax); + } + if (has_spec_compliant) { + RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionSpecCompliant, + kSimulcastApiVersionMax); + } + if (!has_legacy && !has_spec_compliant) { + RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionNone, + kSimulcastApiVersionMax); + } +} + +const ContentInfo* FindTransceiverMSection( + RtpTransceiverProxyWithInternal* transceiver, + const SessionDescriptionInterface* session_description) { + return transceiver->mid() + ? session_description->description()->GetContentByName( + *transceiver->mid()) + : nullptr; +} + +// Wraps a CreateSessionDescriptionObserver and an OperationsChain operation +// complete callback. When the observer is invoked, the wrapped observer is +// invoked followed by invoking the completion callback. +class CreateSessionDescriptionObserverOperationWrapper + : public CreateSessionDescriptionObserver { + public: + CreateSessionDescriptionObserverOperationWrapper( + rtc::scoped_refptr observer, + std::function operation_complete_callback) + : observer_(std::move(observer)), + operation_complete_callback_(std::move(operation_complete_callback)) { + RTC_DCHECK(observer_); + } + ~CreateSessionDescriptionObserverOperationWrapper() override { + RTC_DCHECK(was_called_); + } + + void OnSuccess(SessionDescriptionInterface* desc) override { + RTC_DCHECK(!was_called_); +#ifdef RTC_DCHECK_IS_ON + was_called_ = true; +#endif // RTC_DCHECK_IS_ON + // Completing the operation before invoking the observer allows the observer + // to execute SetLocalDescription() without delay. + operation_complete_callback_(); + observer_->OnSuccess(desc); + } + + void OnFailure(RTCError error) override { + RTC_DCHECK(!was_called_); +#ifdef RTC_DCHECK_IS_ON + was_called_ = true; +#endif // RTC_DCHECK_IS_ON + operation_complete_callback_(); + observer_->OnFailure(std::move(error)); + } + + private: +#ifdef RTC_DCHECK_IS_ON + bool was_called_ = false; +#endif // RTC_DCHECK_IS_ON + rtc::scoped_refptr observer_; + std::function operation_complete_callback_; +}; + +} // namespace + +// Used by parameterless SetLocalDescription() to create an offer or answer. +// Upon completion of creating the session description, SetLocalDescription() is +// invoked with the result. +// For consistency with DoSetLocalDescription(), if the PeerConnection is +// destroyed midst operation, we DO NOT inform the +// |set_local_description_observer| that the operation failed. +// TODO(hbos): If/when we process SLD messages in ~PeerConnection, the +// consistent thing would be to inform the observer here. +class TgPeerConnection::ImplicitCreateSessionDescriptionObserver + : public CreateSessionDescriptionObserver { + public: + ImplicitCreateSessionDescriptionObserver( + rtc::WeakPtr pc, + rtc::scoped_refptr + set_local_description_observer) + : pc_(std::move(pc)), + set_local_description_observer_( + std::move(set_local_description_observer)) {} + ~ImplicitCreateSessionDescriptionObserver() override { + RTC_DCHECK(was_called_); + } + + void SetOperationCompleteCallback( + std::function operation_complete_callback) { + operation_complete_callback_ = std::move(operation_complete_callback); + } + + bool was_called() const { return was_called_; } + + void OnSuccess(SessionDescriptionInterface* desc_ptr) override { + RTC_DCHECK(!was_called_); + std::unique_ptr desc(desc_ptr); + was_called_ = true; + + // Abort early if |pc_| is no longer valid. + if (!pc_) { + operation_complete_callback_(); + return; + } + // DoSetLocalDescription() is currently implemented as a synchronous + // operation but where the |set_local_description_observer_|'s callbacks are + // invoked asynchronously in a post to TgPeerConnection::OnMessage(). + pc_->DoSetLocalDescription(std::move(desc), + std::move(set_local_description_observer_)); + // For backwards-compatability reasons, we declare the operation as + // completed here (rather than in TgPeerConnection::OnMessage()). This ensures + // that subsequent offer/answer operations can start immediately (without + // waiting for OnMessage()). + operation_complete_callback_(); + } + + void OnFailure(RTCError error) override { + RTC_DCHECK(!was_called_); + was_called_ = true; + + // Abort early if |pc_| is no longer valid. + if (!pc_) { + operation_complete_callback_(); + return; + } + // DoSetLocalDescription() reports its failures in a post. We do the + // same thing here for consistency. + pc_->PostSetSessionDescriptionFailure( + set_local_description_observer_, + RTCError(error.type(), + std::string("SetLocalDescription failed to create " + "session description - ") + + error.message())); + operation_complete_callback_(); + } + + private: + bool was_called_ = false; + rtc::WeakPtr pc_; + rtc::scoped_refptr + set_local_description_observer_; + std::function operation_complete_callback_; +}; + +class TgPeerConnection::LocalIceCredentialsToReplace { + public: + // Sets the ICE credentials that need restarting to the ICE credentials of + // the current and pending descriptions. + void SetIceCredentialsFromLocalDescriptions( + const SessionDescriptionInterface* current_local_description, + const SessionDescriptionInterface* pending_local_description) { + ice_credentials_.clear(); + if (current_local_description) { + AppendIceCredentialsFromSessionDescription(*current_local_description); + } + if (pending_local_description) { + AppendIceCredentialsFromSessionDescription(*pending_local_description); + } + } + + void ClearIceCredentials() { ice_credentials_.clear(); } + + // Returns true if we have ICE credentials that need restarting. + bool HasIceCredentials() const { return !ice_credentials_.empty(); } + + // Returns true if |local_description| shares no ICE credentials with the + // ICE credentials that need restarting. + bool SatisfiesIceRestart( + const SessionDescriptionInterface& local_description) const { + for (const auto& transport_info : + local_description.description()->transport_infos()) { + if (ice_credentials_.find(std::make_pair( + transport_info.description.ice_ufrag, + transport_info.description.ice_pwd)) != ice_credentials_.end()) { + return false; + } + } + return true; + } + + private: + void AppendIceCredentialsFromSessionDescription( + const SessionDescriptionInterface& desc) { + for (const auto& transport_info : desc.description()->transport_infos()) { + ice_credentials_.insert( + std::make_pair(transport_info.description.ice_ufrag, + transport_info.description.ice_pwd)); + } + } + + std::set> ice_credentials_; +}; + +// Upon completion, posts a task to execute the callback of the +// SetSessionDescriptionObserver asynchronously on the same thread. At this +// point, the state of the peer connection might no longer reflect the effects +// of the SetRemoteDescription operation, as the peer connection could have been +// modified during the post. +// TODO(hbos): Remove this class once we remove the version of +// PeerConnectionInterface::SetRemoteDescription() that takes a +// SetSessionDescriptionObserver as an argument. +class TgPeerConnection::SetRemoteDescriptionObserverAdapter + : public rtc::RefCountedObject { + public: + SetRemoteDescriptionObserverAdapter( + rtc::scoped_refptr pc, + rtc::scoped_refptr wrapper) + : pc_(std::move(pc)), wrapper_(std::move(wrapper)) {} + + // SetRemoteDescriptionObserverInterface implementation. + void OnSetRemoteDescriptionComplete(RTCError error) override { + if (error.ok()) + pc_->PostSetSessionDescriptionSuccess(wrapper_); + else + pc_->PostSetSessionDescriptionFailure(wrapper_, std::move(error)); + } + + private: + rtc::scoped_refptr pc_; + rtc::scoped_refptr wrapper_; +}; + +bool PeerConnectionInterface::RTCConfiguration::operator==( + const PeerConnectionInterface::RTCConfiguration& o) const { + // This static_assert prevents us from accidentally breaking operator==. + // Note: Order matters! Fields must be ordered the same as RTCConfiguration. + struct stuff_being_tested_for_equality { + IceServers servers; + IceTransportsType type; + BundlePolicy bundle_policy; + RtcpMuxPolicy rtcp_mux_policy; + std::vector> certificates; + int ice_candidate_pool_size; + bool disable_ipv6; + bool disable_ipv6_on_wifi; + int max_ipv6_networks; + bool disable_link_local_networks; + bool enable_rtp_data_channel; + absl::optional screencast_min_bitrate; + absl::optional combined_audio_video_bwe; + absl::optional enable_dtls_srtp; + TcpCandidatePolicy tcp_candidate_policy; + CandidateNetworkPolicy candidate_network_policy; + int audio_jitter_buffer_max_packets; + bool audio_jitter_buffer_fast_accelerate; + int audio_jitter_buffer_min_delay_ms; + bool audio_jitter_buffer_enable_rtx_handling; + int ice_connection_receiving_timeout; + int ice_backup_candidate_pair_ping_interval; + ContinualGatheringPolicy continual_gathering_policy; + bool prioritize_most_likely_ice_candidate_pairs; + struct cricket::MediaConfig media_config; + bool prune_turn_ports; + PortPrunePolicy turn_port_prune_policy; + bool presume_writable_when_fully_relayed; + bool enable_ice_renomination; + bool redetermine_role_on_ice_restart; + bool surface_ice_candidates_on_ice_transport_type_changed; + absl::optional ice_check_interval_strong_connectivity; + absl::optional ice_check_interval_weak_connectivity; + absl::optional ice_check_min_interval; + absl::optional ice_unwritable_timeout; + absl::optional ice_unwritable_min_checks; + absl::optional ice_inactive_timeout; + absl::optional stun_candidate_keepalive_interval; + absl::optional ice_regather_interval_range; + webrtc::TurnCustomizer* turn_customizer; + SdpSemantics sdp_semantics; + absl::optional network_preference; + bool active_reset_srtp_params; + bool use_media_transport; + bool use_media_transport_for_data_channels; + absl::optional use_datagram_transport; + absl::optional use_datagram_transport_for_data_channels; + absl::optional use_datagram_transport_for_data_channels_receive_only; + absl::optional crypto_options; + bool offer_extmap_allow_mixed; + std::string turn_logging_id; + bool enable_implicit_rollback; + absl::optional allow_codec_switching; + }; + static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), + "Did you add something to RTCConfiguration and forget to " + "update operator==?"); + return type == o.type && servers == o.servers && + bundle_policy == o.bundle_policy && + rtcp_mux_policy == o.rtcp_mux_policy && + tcp_candidate_policy == o.tcp_candidate_policy && + candidate_network_policy == o.candidate_network_policy && + audio_jitter_buffer_max_packets == o.audio_jitter_buffer_max_packets && + audio_jitter_buffer_fast_accelerate == + o.audio_jitter_buffer_fast_accelerate && + audio_jitter_buffer_min_delay_ms == + o.audio_jitter_buffer_min_delay_ms && + audio_jitter_buffer_enable_rtx_handling == + o.audio_jitter_buffer_enable_rtx_handling && + ice_connection_receiving_timeout == + o.ice_connection_receiving_timeout && + ice_backup_candidate_pair_ping_interval == + o.ice_backup_candidate_pair_ping_interval && + continual_gathering_policy == o.continual_gathering_policy && + certificates == o.certificates && + prioritize_most_likely_ice_candidate_pairs == + o.prioritize_most_likely_ice_candidate_pairs && + media_config == o.media_config && disable_ipv6 == o.disable_ipv6 && + disable_ipv6_on_wifi == o.disable_ipv6_on_wifi && + max_ipv6_networks == o.max_ipv6_networks && + disable_link_local_networks == o.disable_link_local_networks && + enable_rtp_data_channel == o.enable_rtp_data_channel && + screencast_min_bitrate == o.screencast_min_bitrate && + combined_audio_video_bwe == o.combined_audio_video_bwe && + enable_dtls_srtp == o.enable_dtls_srtp && + ice_candidate_pool_size == o.ice_candidate_pool_size && + prune_turn_ports == o.prune_turn_ports && + turn_port_prune_policy == o.turn_port_prune_policy && + presume_writable_when_fully_relayed == + o.presume_writable_when_fully_relayed && + enable_ice_renomination == o.enable_ice_renomination && + redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart && + surface_ice_candidates_on_ice_transport_type_changed == + o.surface_ice_candidates_on_ice_transport_type_changed && + ice_check_interval_strong_connectivity == + o.ice_check_interval_strong_connectivity && + ice_check_interval_weak_connectivity == + o.ice_check_interval_weak_connectivity && + ice_check_min_interval == o.ice_check_min_interval && + ice_unwritable_timeout == o.ice_unwritable_timeout && + ice_unwritable_min_checks == o.ice_unwritable_min_checks && + ice_inactive_timeout == o.ice_inactive_timeout && + stun_candidate_keepalive_interval == + o.stun_candidate_keepalive_interval && + ice_regather_interval_range == o.ice_regather_interval_range && + turn_customizer == o.turn_customizer && + sdp_semantics == o.sdp_semantics && + network_preference == o.network_preference && + active_reset_srtp_params == o.active_reset_srtp_params && + use_media_transport == o.use_media_transport && + use_media_transport_for_data_channels == + o.use_media_transport_for_data_channels && + use_datagram_transport == o.use_datagram_transport && + use_datagram_transport_for_data_channels == + o.use_datagram_transport_for_data_channels && + use_datagram_transport_for_data_channels_receive_only == + o.use_datagram_transport_for_data_channels_receive_only && + crypto_options == o.crypto_options && + offer_extmap_allow_mixed == o.offer_extmap_allow_mixed && + turn_logging_id == o.turn_logging_id && + enable_implicit_rollback == o.enable_implicit_rollback && + allow_codec_switching == o.allow_codec_switching; +} + +bool PeerConnectionInterface::RTCConfiguration::operator!=( + const PeerConnectionInterface::RTCConfiguration& o) const { + return !(*this == o); +} + +void TgPeerConnection::TransceiverStableState::set_newly_created() { + RTC_DCHECK(!has_m_section_); + newly_created_ = true; +} + +void TgPeerConnection::TransceiverStableState::SetMSectionIfUnset( + absl::optional mid, + absl::optional mline_index) { + if (!has_m_section_) { + mid_ = mid; + mline_index_ = mline_index; + has_m_section_ = true; + } +} + +void TgPeerConnection::TransceiverStableState::SetRemoteStreamIdsIfUnset( + const std::vector& ids) { + if (!remote_stream_ids_.has_value()) { + remote_stream_ids_ = ids; + } +} + +// Generate a RTCP CNAME when a TgPeerConnection is created. +std::string GenerateRtcpCname() { + std::string cname; + if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) { + RTC_LOG(LS_ERROR) << "Failed to generate CNAME."; + RTC_NOTREACHED(); + } + return cname; +} + +bool ValidateOfferAnswerOptions( + const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) { + return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) && + IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video); +} + +// From |rtc_options|, fill parts of |session_options| shared by all generated +// m= sections (in other words, nothing that involves a map/array). +void ExtractSharedMediaSessionOptions( + const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options, + cricket::MediaSessionOptions* session_options) { + session_options->vad_enabled = rtc_options.voice_activity_detection; + session_options->bundle_enabled = rtc_options.use_rtp_mux; + session_options->raw_packetization_for_video = + rtc_options.raw_packetization_for_video; +} + +TgPeerConnection::TgPeerConnection(TgPeerConnectionFactory* factory, + std::unique_ptr event_log, + std::unique_ptr call) + : factory_(factory), + event_log_(std::move(event_log)), + event_log_ptr_(event_log_.get()), + operations_chain_(rtc::OperationsChain::Create()), + datagram_transport_config_( + field_trial::FindFullName(kDatagramTransportFieldTrial)), + datagram_transport_data_channel_config_( + field_trial::FindFullName(kDatagramTransportDataChannelFieldTrial)), + rtcp_cname_(GenerateRtcpCname()), + local_streams_(StreamCollection::Create()), + remote_streams_(StreamCollection::Create()), + call_(std::move(call)), + call_ptr_(call_.get()), + local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()), + weak_ptr_factory_(this) {} + +TgPeerConnection::~TgPeerConnection() { + TRACE_EVENT0("webrtc", "TgPeerConnection::~TgPeerConnection"); + RTC_DCHECK_RUN_ON(signaling_thread()); + + weak_ptr_factory_.InvalidateWeakPtrs(); + + // Need to stop transceivers before destroying the stats collector because + // AudioRtpSender has a reference to the StatsCollector it will update when + // stopping. + for (const auto& transceiver : transceivers_) { + transceiver->Stop(); + } + + stats_.reset(nullptr); + if (stats_collector_) { + stats_collector_->WaitForPendingRequest(); + stats_collector_ = nullptr; + } + + // Don't destroy BaseChannels until after stats has been cleaned up so that + // the last stats request can still read from the channels. + DestroyAllChannels(); + + RTC_LOG(LS_INFO) << "Session: " << session_id() << " is destroyed."; + + webrtc_session_desc_factory_.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] { + RTC_DCHECK_RUN_ON(network_thread()); + port_allocator_.reset(); + }); + // call_ and event_log_ must be destroyed on the worker thread. + worker_thread()->Invoke(RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(worker_thread()); + call_.reset(); + // The event log must outlive call (and any other object that uses it). + event_log_.reset(); + }); + + // Process all pending notifications in the message queue. If we don't do + // this, requests will linger and not know they succeeded or failed. + rtc::MessageList list; + signaling_thread()->Clear(this, rtc::MQID_ANY, &list); + for (auto& msg : list) { + if (msg.message_id == MSG_CREATE_SESSIONDESCRIPTION_FAILED) { + // Processing CreateOffer() and CreateAnswer() messages ensures their + // observers are invoked even if the TgPeerConnection is destroyed early. + OnMessage(&msg); + } else { + // TODO(hbos): Consider processing all pending messages. This would mean + // that SetLocalDescription() and SetRemoteDescription() observers are + // informed of successes and failures; this is currently NOT the case. + delete msg.pdata; + } + } +} + +void TgPeerConnection::DestroyAllChannels() { + // Destroy video channels first since they may have a pointer to a voice + // channel. + for (const auto& transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { + DestroyTransceiverChannel(transceiver); + } + } + for (const auto& transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + DestroyTransceiverChannel(transceiver); + } + } +} + +bool TgPeerConnection::Initialize( + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::Initialize"); + + RTCError config_error = ValidateConfiguration(configuration); + if (!config_error.ok()) { + RTC_LOG(LS_ERROR) << "Invalid configuration: " << config_error.message(); + return false; + } + + if (!dependencies.allocator) { + RTC_LOG(LS_ERROR) + << "TgPeerConnection initialized without a PortAllocator? " + "This shouldn't happen if using PeerConnectionFactory."; + return false; + } + + if (!dependencies.observer) { + // TODO(deadbeef): Why do we do this? + RTC_LOG(LS_ERROR) << "TgPeerConnection initialized without a " + "PeerConnectionObserver"; + return false; + } + + observer_ = dependencies.observer; + async_resolver_factory_ = std::move(dependencies.async_resolver_factory); + port_allocator_ = std::move(dependencies.allocator); + ice_transport_factory_ = std::move(dependencies.ice_transport_factory); + tls_cert_verifier_ = std::move(dependencies.tls_cert_verifier); + + cricket::ServerAddresses stun_servers; + std::vector turn_servers; + + RTCErrorType parse_error = + ParseIceServers(configuration.servers, &stun_servers, &turn_servers); + if (parse_error != RTCErrorType::NONE) { + return false; + } + + // Add the turn logging id to all turn servers + for (cricket::RelayServerConfig& turn_server : turn_servers) { + turn_server.turn_logging_id = configuration.turn_logging_id; + } + + // The port allocator lives on the network thread and should be initialized + // there. + const auto pa_result = + network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::InitializePortAllocator_n, this, + stun_servers, turn_servers, configuration)); + + // If initialization was successful, note if STUN or TURN servers + // were supplied. + if (!stun_servers.empty()) { + NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); + } + if (!turn_servers.empty()) { + NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); + } + + // Send information about IPv4/IPv6 status. + PeerConnectionAddressFamilyCounter address_family; + if (pa_result.enable_ipv6) { + address_family = kPeerConnection_IPv6; + } else { + address_family = kPeerConnection_IPv4; + } + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", address_family, + kPeerConnectionAddressFamilyCounter_Max); + + const PeerConnectionFactoryInterface::Options& options = factory_->options(); + + // 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); + TgJsepTransportController::Config config; + config.redetermine_role_on_ice_restart = + configuration.redetermine_role_on_ice_restart; + config.ssl_max_version = factory_->options().ssl_max_version; + config.disable_encryption = options.disable_encryption; + config.bundle_policy = configuration.bundle_policy; + config.rtcp_mux_policy = configuration.rtcp_mux_policy; + // TODO(bugs.webrtc.org/9891) - Remove options.crypto_options then remove this + // stub. + config.crypto_options = configuration.crypto_options.has_value() + ? *configuration.crypto_options + : options.crypto_options; + config.transport_observer = this; + // It's safe to pass |this| and using |rtcp_invoker_| and the |call_| pointer + // since the TgJsepTransportController instance is owned by this PeerConnection + // instance and is destroyed before both |rtcp_invoker_| and the |call_| + // pointer. + config.rtcp_handler = [this](const rtc::CopyOnWriteBuffer& packet, + int64_t packet_time_us) { + RTC_DCHECK_RUN_ON(network_thread()); + rtcp_invoker_.AsyncInvoke( + RTC_FROM_HERE, worker_thread(), [this, packet, packet_time_us] { + RTC_DCHECK_RUN_ON(worker_thread()); + // |call_| is reset on the worker thread in the PeerConnection + // destructor, so we check that it's still valid before propagating + // the packet. + if (call_) { + call_->Receiver()->DeliverPacket(MediaType::ANY, packet, + packet_time_us); + } + }); + }; + config.event_log = event_log_ptr_; +#if defined(ENABLE_EXTERNAL_AUTH) + config.enable_external_auth = true; +#endif + config.active_reset_srtp_params = configuration.active_reset_srtp_params; + + use_datagram_transport_ = datagram_transport_config_.enabled && + configuration.use_datagram_transport.value_or( + datagram_transport_config_.default_value); + use_datagram_transport_for_data_channels_ = + datagram_transport_data_channel_config_.enabled && + configuration.use_datagram_transport_for_data_channels.value_or( + datagram_transport_data_channel_config_.default_value); + use_datagram_transport_for_data_channels_receive_only_ = + configuration.use_datagram_transport_for_data_channels_receive_only + .value_or(datagram_transport_data_channel_config_.receive_only); + if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + if (!factory_->media_transport_factory()) { + RTC_DCHECK(false) + << "PeerConnecton is initialized with use_datagram_transport = true " + "or use_datagram_transport_for_data_channels = true " + << "but media transport factory is not set in PeerConnectionFactory"; + return false; + } + + config.use_datagram_transport = use_datagram_transport_; + config.use_datagram_transport_for_data_channels = + use_datagram_transport_for_data_channels_; + config.use_datagram_transport_for_data_channels_receive_only = + use_datagram_transport_for_data_channels_receive_only_; + config.media_transport_factory = factory_->media_transport_factory(); + } + + // 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]; + } + + if (options.disable_encryption) { + dtls_enabled_ = false; + } else { + // Enable DTLS by default if we have an identity store or a certificate. + dtls_enabled_ = (dependencies.cert_generator || certificate); + // |configuration| can override the default |dtls_enabled_| value. + if (configuration.enable_dtls_srtp) { + dtls_enabled_ = *(configuration.enable_dtls_srtp); + } + } + + sctp_factory_ = factory_->CreateSctpTransportInternalFactory(); + + if (use_datagram_transport_for_data_channels_) { + if (configuration.enable_rtp_data_channel) { + RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and " + "use_datagram_transport_for_data_channels are " + "incompatible and cannot both be set to true"; + return false; + } + if (configuration.enable_dtls_srtp && !*configuration.enable_dtls_srtp) { + } else { + config.sctp_factory = sctp_factory_.get(); + } + } else if (configuration.enable_rtp_data_channel) { + // Enable creation of RTP data channels if the kEnableRtpDataChannels is + // set. It takes precendence over the disable_sctp_data_channels + // PeerConnectionFactoryInterface::Options. + } else { + // DTLS has to be enabled to use SCTP. + if (!options.disable_sctp_data_channels && dtls_enabled_) { + config.sctp_factory = sctp_factory_.get(); + } + } + + config.ice_transport_factory = ice_transport_factory_.get(); + + transport_controller_.reset(new TgJsepTransportController( + signaling_thread(), network_thread(), port_allocator_.get(), + async_resolver_factory_.get(), config)); + transport_controller_->SignalIceConnectionState.connect( + this, &TgPeerConnection::OnTransportControllerConnectionState); + transport_controller_->SignalStandardizedIceConnectionState.connect( + this, &TgPeerConnection::SetStandardizedIceConnectionState); + transport_controller_->SignalConnectionState.connect( + this, &TgPeerConnection::SetConnectionState); + transport_controller_->SignalIceGatheringState.connect( + this, &TgPeerConnection::OnTransportControllerGatheringState); + transport_controller_->SignalIceCandidatesGathered.connect( + this, &TgPeerConnection::OnTransportControllerCandidatesGathered); + transport_controller_->SignalIceCandidateError.connect( + this, &TgPeerConnection::OnTransportControllerCandidateError); + transport_controller_->SignalIceCandidatesRemoved.connect( + this, &TgPeerConnection::OnTransportControllerCandidatesRemoved); + transport_controller_->SignalDtlsHandshakeError.connect( + this, &TgPeerConnection::OnTransportControllerDtlsHandshakeError); + transport_controller_->SignalIceCandidatePairChanged.connect( + this, &TgPeerConnection::OnTransportControllerCandidateChanged); + + stats_.reset(new StatsCollector(this)); + stats_collector_ = RTCStatsCollector::Create(this); + + configuration_ = configuration; + + transport_controller_->SetIceConfig(ParseIceConfig(configuration)); + + 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 = + configuration.audio_jitter_buffer_max_packets; + + audio_options_.audio_jitter_buffer_fast_accelerate = + configuration.audio_jitter_buffer_fast_accelerate; + + audio_options_.audio_jitter_buffer_min_delay_ms = + configuration.audio_jitter_buffer_min_delay_ms; + + audio_options_.audio_jitter_buffer_enable_rtx_handling = + configuration.audio_jitter_buffer_enable_rtx_handling; + + // 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_) { + dependencies.cert_generator.reset(); + certificate = nullptr; + } else if (certificate) { + // Favor generated certificate over the certificate generator. + dependencies.cert_generator.reset(); + } + + webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( + signaling_thread(), channel_manager(), this, session_id(), + std::move(dependencies.cert_generator), certificate, &ssrc_generator_)); + webrtc_session_desc_factory_->SignalCertificateReady.connect( + this, &TgPeerConnection::OnCertificateReady); + + if (options.disable_encryption) { + webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); + } + + webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( + GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions); + webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan()); + + // Add default audio/video transceivers for Plan B SDP. + if (!IsUnifiedPlan()) { + transceivers_.push_back( + RtpTransceiverProxyWithInternal::Create( + signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_AUDIO))); + transceivers_.push_back( + RtpTransceiverProxyWithInternal::Create( + signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO))); + } + int delay_ms = + return_histogram_very_quickly_ ? 0 : REPORT_USAGE_PATTERN_DELAY_MS; + signaling_thread()->PostDelayed(RTC_FROM_HERE, delay_ms, this, + MSG_REPORT_USAGE_PATTERN, nullptr); + + if (dependencies.video_bitrate_allocator_factory) { + video_bitrate_allocator_factory_ = + std::move(dependencies.video_bitrate_allocator_factory); + } else { + video_bitrate_allocator_factory_ = + CreateBuiltinVideoBitrateAllocatorFactory(); + } + return true; +} + +RTCError TgPeerConnection::ValidateConfiguration( + const RTCConfiguration& config) const { + if (config.ice_regather_interval_range && + config.continual_gathering_policy == GATHER_ONCE) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "ice_regather_interval_range specified but continual " + "gathering policy is GATHER_ONCE"); + } + auto result = + cricket::P2PTransportChannel::ValidateIceConfig(ParseIceConfig(config)); + return result; +} + +rtc::scoped_refptr TgPeerConnection::local_streams() { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified " + "Plan SdpSemantics. Please use GetSenders " + "instead."; + return local_streams_; +} + +rtc::scoped_refptr TgPeerConnection::remote_streams() { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified " + "Plan SdpSemantics. Please use GetReceivers " + "instead."; + return remote_streams_; +} + +bool TgPeerConnection::AddStream(MediaStreamInterface* local_stream) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan " + "SdpSemantics. Please use AddTrack instead."; + TRACE_EVENT0("webrtc", "TgPeerConnection::AddStream"); + if (IsClosed()) { + return false; + } + if (!CanAddLocalMediaStream(local_streams_, local_stream)) { + return false; + } + + local_streams_->AddStream(local_stream); + MediaStreamObserver* observer = new MediaStreamObserver(local_stream); + observer->SignalAudioTrackAdded.connect(this, + &TgPeerConnection::OnAudioTrackAdded); + observer->SignalAudioTrackRemoved.connect( + this, &TgPeerConnection::OnAudioTrackRemoved); + observer->SignalVideoTrackAdded.connect(this, + &TgPeerConnection::OnVideoTrackAdded); + observer->SignalVideoTrackRemoved.connect( + this, &TgPeerConnection::OnVideoTrackRemoved); + stream_observers_.push_back(std::unique_ptr(observer)); + + for (const auto& track : local_stream->GetAudioTracks()) { + AddAudioTrack(track.get(), local_stream); + } + for (const auto& track : local_stream->GetVideoTracks()) { + AddVideoTrack(track.get(), local_stream); + } + + stats_->AddStream(local_stream); + UpdateNegotiationNeeded(); + return true; +} + +void TgPeerConnection::RemoveStream(MediaStreamInterface* local_stream) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified " + "Plan SdpSemantics. Please use RemoveTrack " + "instead."; + TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveStream"); + if (!IsClosed()) { + for (const auto& track : local_stream->GetAudioTracks()) { + RemoveAudioTrack(track.get(), local_stream); + } + for (const auto& track : local_stream->GetVideoTracks()) { + RemoveVideoTrack(track.get(), local_stream); + } + } + local_streams_->RemoveStream(local_stream); + stream_observers_.erase( + std::remove_if( + stream_observers_.begin(), stream_observers_.end(), + [local_stream](const std::unique_ptr& observer) { + return observer->stream()->id().compare(local_stream->id()) == 0; + }), + stream_observers_.end()); + + if (IsClosed()) { + return; + } + UpdateNegotiationNeeded(); +} + +RTCErrorOr> TgPeerConnection::AddTrack( + rtc::scoped_refptr track, + const std::vector& stream_ids) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::AddTrack"); + if (!track) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Track is null."); + } + if (!(track->kind() == MediaStreamTrackInterface::kAudioKind || + track->kind() == MediaStreamTrackInterface::kVideoKind)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Track has invalid kind: " + track->kind()); + } + if (IsClosed()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "TgPeerConnection is closed."); + } + if (FindSenderForTrack(track)) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "Sender already exists for track " + track->id() + "."); + } + auto sender_or_error = + (IsUnifiedPlan() ? AddTrackUnifiedPlan(track, stream_ids) + : AddTrackPlanB(track, stream_ids)); + if (sender_or_error.ok()) { + UpdateNegotiationNeeded(); + stats_->AddTrack(track); + } + return sender_or_error; +} + +RTCErrorOr> +TgPeerConnection::AddTrackPlanB( + rtc::scoped_refptr track, + const std::vector& stream_ids) { + if (stream_ids.size() > 1u) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "AddTrack with more than one stream is not " + "supported with Plan B semantics."); + } + std::vector adjusted_stream_ids = stream_ids; + if (adjusted_stream_ids.empty()) { + adjusted_stream_ids.push_back(rtc::CreateRandomUuid()); + } + cricket::MediaType media_type = + (track->kind() == MediaStreamTrackInterface::kAudioKind + ? cricket::MEDIA_TYPE_AUDIO + : cricket::MEDIA_TYPE_VIDEO); + auto new_sender = + CreateSender(media_type, track->id(), track, adjusted_stream_ids, {}); + if (track->kind() == MediaStreamTrackInterface::kAudioKind) { + new_sender->internal()->SetMediaChannel(voice_media_channel()); + GetAudioTransceiver()->internal()->AddSender(new_sender); + const RtpSenderInfo* sender_info = + FindSenderInfo(local_audio_sender_infos_, + new_sender->internal()->stream_ids()[0], track->id()); + if (sender_info) { + new_sender->internal()->SetSsrc(sender_info->first_ssrc); + } + } else { + RTC_DCHECK_EQ(MediaStreamTrackInterface::kVideoKind, track->kind()); + new_sender->internal()->SetMediaChannel(video_media_channel()); + GetVideoTransceiver()->internal()->AddSender(new_sender); + const RtpSenderInfo* sender_info = + FindSenderInfo(local_video_sender_infos_, + new_sender->internal()->stream_ids()[0], track->id()); + if (sender_info) { + new_sender->internal()->SetSsrc(sender_info->first_ssrc); + } + } + return rtc::scoped_refptr(new_sender); +} + +RTCErrorOr> +TgPeerConnection::AddTrackUnifiedPlan( + rtc::scoped_refptr track, + const std::vector& stream_ids) { + auto transceiver = FindFirstTransceiverForAddedTrack(track); + if (transceiver) { + RTC_LOG(LS_INFO) << "Reusing an existing " + << cricket::MediaTypeToString(transceiver->media_type()) + << " transceiver for AddTrack."; + if (transceiver->direction() == RtpTransceiverDirection::kRecvOnly) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kSendRecv); + } else if (transceiver->direction() == RtpTransceiverDirection::kInactive) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kSendOnly); + } + transceiver->sender()->SetTrack(track); + transceiver->internal()->sender_internal()->set_stream_ids(stream_ids); + transceiver->internal()->set_reused_for_addtrack(true); + } else { + cricket::MediaType media_type = + (track->kind() == MediaStreamTrackInterface::kAudioKind + ? cricket::MEDIA_TYPE_AUDIO + : cricket::MEDIA_TYPE_VIDEO); + RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) + << " transceiver in response to a call to AddTrack."; + std::string sender_id = track->id(); + // Avoid creating a sender with an existing ID by generating a random ID. + // This can happen if this is the second time AddTrack has created a sender + // for this track. + if (FindSenderById(sender_id)) { + sender_id = rtc::CreateRandomUuid(); + } + auto sender = CreateSender(media_type, sender_id, track, stream_ids, {}); + auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); + transceiver = CreateAndAddTransceiver(sender, receiver); + transceiver->internal()->set_created_by_addtrack(true); + transceiver->internal()->set_direction(RtpTransceiverDirection::kSendRecv); + } + return transceiver->sender(); +} + +rtc::scoped_refptr> +TgPeerConnection::FindFirstTransceiverForAddedTrack( + rtc::scoped_refptr track) { + RTC_DCHECK(track); + for (auto transceiver : transceivers_) { + if (!transceiver->sender()->track() && + cricket::MediaTypeToString(transceiver->media_type()) == + track->kind() && + !transceiver->internal()->has_ever_been_used_to_send() && + !transceiver->stopped()) { + return transceiver; + } + } + return nullptr; +} + +bool TgPeerConnection::RemoveTrack(RtpSenderInterface* sender) { + TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveTrack"); + return RemoveTrackNew(sender).ok(); +} + +RTCError TgPeerConnection::RemoveTrackNew( + rtc::scoped_refptr sender) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!sender) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Sender is null."); + } + if (IsClosed()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "TgPeerConnection is closed."); + } + if (IsUnifiedPlan()) { + auto transceiver = FindTransceiverBySender(sender); + if (!transceiver || !sender->track()) { + return RTCError::OK(); + } + sender->SetTrack(nullptr); + if (transceiver->direction() == RtpTransceiverDirection::kSendRecv) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kRecvOnly); + } else if (transceiver->direction() == RtpTransceiverDirection::kSendOnly) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kInactive); + } + } else { + bool removed; + if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { + removed = GetAudioTransceiver()->internal()->RemoveSender(sender); + } else { + RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, sender->media_type()); + removed = GetVideoTransceiver()->internal()->RemoveSender(sender); + } + if (!removed) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "Couldn't find sender " + sender->id() + " to remove."); + } + } + UpdateNegotiationNeeded(); + return RTCError::OK(); +} + +rtc::scoped_refptr> +TgPeerConnection::FindTransceiverBySender( + rtc::scoped_refptr sender) { + for (auto transceiver : transceivers_) { + if (transceiver->sender() == sender) { + return transceiver; + } + } + return nullptr; +} + +RTCErrorOr> +TgPeerConnection::AddTransceiver( + rtc::scoped_refptr track) { + return AddTransceiver(track, RtpTransceiverInit()); +} + +RTCErrorOr> +TgPeerConnection::AddTransceiver( + rtc::scoped_refptr track, + const RtpTransceiverInit& init) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(IsUnifiedPlan()) + << "AddTransceiver is only available with Unified Plan SdpSemantics"; + if (!track) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "track is null"); + } + cricket::MediaType media_type; + if (track->kind() == MediaStreamTrackInterface::kAudioKind) { + media_type = cricket::MEDIA_TYPE_AUDIO; + } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) { + media_type = cricket::MEDIA_TYPE_VIDEO; + } else { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Track kind is not audio or video"); + } + return AddTransceiver(media_type, track, init); +} + +RTCErrorOr> +TgPeerConnection::AddTransceiver(cricket::MediaType media_type) { + return AddTransceiver(media_type, RtpTransceiverInit()); +} + +RTCErrorOr> +TgPeerConnection::AddTransceiver(cricket::MediaType media_type, + const RtpTransceiverInit& init) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(IsUnifiedPlan()) + << "AddTransceiver is only available with Unified Plan SdpSemantics"; + if (!(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "media type is not audio or video"); + } + return AddTransceiver(media_type, nullptr, init); +} + +RTCErrorOr> +TgPeerConnection::AddTransceiver( + cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init, + bool update_negotiation_needed) { + RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO)); + if (track) { + RTC_DCHECK_EQ(media_type, + (track->kind() == MediaStreamTrackInterface::kAudioKind + ? cricket::MEDIA_TYPE_AUDIO + : cricket::MEDIA_TYPE_VIDEO)); + } + + RTC_HISTOGRAM_COUNTS_LINEAR(kSimulcastNumberOfEncodings, + (int)init.send_encodings.size(), 0, 7, 8); + + size_t num_rids = absl::c_count_if(init.send_encodings, + [](const RtpEncodingParameters& encoding) { + return !encoding.rid.empty(); + }); + if (num_rids > 0 && num_rids != init.send_encodings.size()) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "RIDs must be provided for either all or none of the send encodings."); + } + + if (num_rids > 0 && absl::c_any_of(init.send_encodings, + [](const RtpEncodingParameters& encoding) { + return !IsLegalRsidName(encoding.rid); + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Invalid RID value provided."); + } + + if (absl::c_any_of(init.send_encodings, + [](const RtpEncodingParameters& encoding) { + return encoding.ssrc.has_value(); + })) { + LOG_AND_RETURN_ERROR( + RTCErrorType::UNSUPPORTED_PARAMETER, + "Attempted to set an unimplemented parameter of RtpParameters."); + } + + RtpParameters parameters; + parameters.encodings = init.send_encodings; + + // Encodings are dropped from the tail if too many are provided. + if (parameters.encodings.size() > kMaxSimulcastStreams) { + parameters.encodings.erase( + parameters.encodings.begin() + kMaxSimulcastStreams, + parameters.encodings.end()); + } + + // Single RID should be removed. + if (parameters.encodings.size() == 1 && + !parameters.encodings[0].rid.empty()) { + RTC_LOG(LS_INFO) << "Removing RID: " << parameters.encodings[0].rid << "."; + parameters.encodings[0].rid.clear(); + } + + // If RIDs were not provided, they are generated for simulcast scenario. + if (parameters.encodings.size() > 1 && num_rids == 0) { + rtc::UniqueStringGenerator rid_generator; + for (RtpEncodingParameters& encoding : parameters.encodings) { + encoding.rid = rid_generator(); + } + } + + if (UnimplementedRtpParameterHasValue(parameters)) { + LOG_AND_RETURN_ERROR( + RTCErrorType::UNSUPPORTED_PARAMETER, + "Attempted to set an unimplemented parameter of RtpParameters."); + } + + auto result = cricket::CheckRtpParametersValues(parameters); + if (!result.ok()) { + LOG_AND_RETURN_ERROR(result.type(), result.message()); + } + + RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) + << " transceiver in response to a call to AddTransceiver."; + // Set the sender ID equal to the track ID if the track is specified unless + // that sender ID is already in use. + std::string sender_id = + (track && !FindSenderById(track->id()) ? track->id() + : rtc::CreateRandomUuid()); + auto sender = CreateSender(media_type, sender_id, track, init.stream_ids, + parameters.encodings); + auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); + auto transceiver = CreateAndAddTransceiver(sender, receiver); + transceiver->internal()->set_direction(init.direction); + + if (update_negotiation_needed) { + UpdateNegotiationNeeded(); + } + + return rtc::scoped_refptr(transceiver); +} + +rtc::scoped_refptr> +TgPeerConnection::CreateSender( + cricket::MediaType media_type, + const std::string& id, + rtc::scoped_refptr track, + const std::vector& stream_ids, + const std::vector& send_encodings) { + RTC_DCHECK_RUN_ON(signaling_thread()); + rtc::scoped_refptr> sender; + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + RTC_DCHECK(!track || + (track->kind() == MediaStreamTrackInterface::kAudioKind)); + sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), + AudioRtpSender::Create(worker_thread(), id, stats_.get(), this)); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); + } else { + RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); + RTC_DCHECK(!track || + (track->kind() == MediaStreamTrackInterface::kVideoKind)); + sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), VideoRtpSender::Create(worker_thread(), id, this)); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); + } + bool set_track_succeeded = sender->SetTrack(track); + RTC_DCHECK(set_track_succeeded); + sender->internal()->set_stream_ids(stream_ids); + sender->internal()->set_init_send_encodings(send_encodings); + return sender; +} + +rtc::scoped_refptr> +TgPeerConnection::CreateReceiver(cricket::MediaType media_type, + const std::string& receiver_id) { + rtc::scoped_refptr> + receiver; + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), new AudioRtpReceiver(worker_thread(), receiver_id, + std::vector({}))); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); + } else { + RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); + receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), new VideoRtpReceiver(worker_thread(), receiver_id, + std::vector({}))); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); + } + return receiver; +} + +rtc::scoped_refptr> +TgPeerConnection::CreateAndAddTransceiver( + rtc::scoped_refptr> sender, + rtc::scoped_refptr> + receiver) { + // Ensure that the new sender does not have an ID that is already in use by + // another sender. + // Allow receiver IDs to conflict since those come from remote SDP (which + // could be invalid, but should not cause a crash). + RTC_DCHECK(!FindSenderById(sender->id())); + auto transceiver = RtpTransceiverProxyWithInternal::Create( + signaling_thread(), + new RtpTransceiver(sender, receiver, channel_manager())); + transceivers_.push_back(transceiver); + transceiver->internal()->SignalNegotiationNeeded.connect( + this, &TgPeerConnection::OnNegotiationNeeded); + return transceiver; +} + +void TgPeerConnection::OnNegotiationNeeded() { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(!IsClosed()); + UpdateNegotiationNeeded(); +} + +rtc::scoped_refptr TgPeerConnection::CreateSender( + const std::string& kind, + const std::string& stream_id) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "CreateSender is not available with Unified " + "Plan SdpSemantics. Please use AddTransceiver " + "instead."; + TRACE_EVENT0("webrtc", "TgPeerConnection::CreateSender"); + if (IsClosed()) { + return nullptr; + } + + // Internally we need to have one stream with Plan B semantics, so we + // generate a random stream ID if not specified. + std::vector stream_ids; + if (stream_id.empty()) { + stream_ids.push_back(rtc::CreateRandomUuid()); + RTC_LOG(LS_INFO) + << "No stream_id specified for sender. Generated stream ID: " + << stream_ids[0]; + } else { + stream_ids.push_back(stream_id); + } + + // TODO(steveanton): Move construction of the RtpSenders to RtpTransceiver. + rtc::scoped_refptr> new_sender; + if (kind == MediaStreamTrackInterface::kAudioKind) { + auto audio_sender = AudioRtpSender::Create( + worker_thread(), rtc::CreateRandomUuid(), stats_.get(), this); + audio_sender->SetMediaChannel(voice_media_channel()); + new_sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), audio_sender); + GetAudioTransceiver()->internal()->AddSender(new_sender); + } else if (kind == MediaStreamTrackInterface::kVideoKind) { + auto video_sender = + VideoRtpSender::Create(worker_thread(), rtc::CreateRandomUuid(), this); + video_sender->SetMediaChannel(video_media_channel()); + new_sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), video_sender); + GetVideoTransceiver()->internal()->AddSender(new_sender); + } else { + RTC_LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind; + return nullptr; + } + new_sender->internal()->set_stream_ids(stream_ids); + + return new_sender; +} + +std::vector> TgPeerConnection::GetSenders() + const { + RTC_DCHECK_RUN_ON(signaling_thread()); + std::vector> ret; + for (const auto& sender : GetSendersInternal()) { + ret.push_back(sender); + } + return ret; +} + +std::vector>> +TgPeerConnection::GetSendersInternal() const { + std::vector>> + all_senders; + for (const auto& transceiver : transceivers_) { + auto senders = transceiver->internal()->senders(); + all_senders.insert(all_senders.end(), senders.begin(), senders.end()); + } + return all_senders; +} + +std::vector> +TgPeerConnection::GetReceivers() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + std::vector> ret; + for (const auto& receiver : GetReceiversInternal()) { + ret.push_back(receiver); + } + return ret; +} + +std::vector< + rtc::scoped_refptr>> +TgPeerConnection::GetReceiversInternal() const { + std::vector< + rtc::scoped_refptr>> + all_receivers; + for (const auto& transceiver : transceivers_) { + auto receivers = transceiver->internal()->receivers(); + all_receivers.insert(all_receivers.end(), receivers.begin(), + receivers.end()); + } + return all_receivers; +} + +std::vector> +TgPeerConnection::GetTransceivers() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(IsUnifiedPlan()) + << "GetTransceivers is only supported with Unified Plan SdpSemantics."; + std::vector> all_transceivers; + for (const auto& transceiver : transceivers_) { + all_transceivers.push_back(transceiver); + } + return all_transceivers; +} + +bool TgPeerConnection::GetStats(StatsObserver* observer, + MediaStreamTrackInterface* track, + StatsOutputLevel level) { + TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!observer) { + RTC_LOG(LS_ERROR) << "GetStats - observer is NULL."; + return false; + } + + stats_->UpdateStats(level); + // The StatsCollector is used to tell if a track is valid because it may + // remember tracks that the TgPeerConnection previously removed. + if (track && !stats_->IsValidTrack(track->id())) { + RTC_LOG(LS_WARNING) << "GetStats is called with an invalid track: " + << track->id(); + return false; + } + signaling_thread()->Post(RTC_FROM_HERE, this, MSG_GETSTATS, + new GetStatsMsg(observer, track)); + return true; +} + +void TgPeerConnection::GetStats(RTCStatsCollectorCallback* callback) { + TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(stats_collector_); + RTC_DCHECK(callback); + stats_collector_->GetStatsReport(callback); +} + +void TgPeerConnection::GetStats( + rtc::scoped_refptr selector, + rtc::scoped_refptr callback) { + TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(callback); + RTC_DCHECK(stats_collector_); + rtc::scoped_refptr internal_sender; + if (selector) { + for (const auto& proxy_transceiver : transceivers_) { + for (const auto& proxy_sender : + proxy_transceiver->internal()->senders()) { + if (proxy_sender == selector) { + internal_sender = proxy_sender->internal(); + break; + } + } + if (internal_sender) + break; + } + } + // If there is no |internal_sender| then |selector| is either null or does not + // belong to the TgPeerConnection (in Plan B, senders can be removed from the + // TgPeerConnection). This means that "all the stats objects representing the + // selector" is an empty set. Invoking GetStatsReport() with a null selector + // produces an empty stats report. + stats_collector_->GetStatsReport(internal_sender, callback); +} + +void TgPeerConnection::GetStats( + rtc::scoped_refptr selector, + rtc::scoped_refptr callback) { + TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(callback); + RTC_DCHECK(stats_collector_); + rtc::scoped_refptr internal_receiver; + if (selector) { + for (const auto& proxy_transceiver : transceivers_) { + for (const auto& proxy_receiver : + proxy_transceiver->internal()->receivers()) { + if (proxy_receiver == selector) { + internal_receiver = proxy_receiver->internal(); + break; + } + } + if (internal_receiver) + break; + } + } + // If there is no |internal_receiver| then |selector| is either null or does + // not belong to the TgPeerConnection (in Plan B, receivers can be removed from + // the TgPeerConnection). This means that "all the stats objects representing + // the selector" is an empty set. Invoking GetStatsReport() with a null + // selector produces an empty stats report. + stats_collector_->GetStatsReport(internal_receiver, callback); +} + +PeerConnectionInterface::SignalingState TgPeerConnection::signaling_state() { + RTC_DCHECK_RUN_ON(signaling_thread()); + return signaling_state_; +} + +PeerConnectionInterface::IceConnectionState +TgPeerConnection::ice_connection_state() { + RTC_DCHECK_RUN_ON(signaling_thread()); + return ice_connection_state_; +} + +PeerConnectionInterface::IceConnectionState +TgPeerConnection::standardized_ice_connection_state() { + RTC_DCHECK_RUN_ON(signaling_thread()); + return standardized_ice_connection_state_; +} + +PeerConnectionInterface::PeerConnectionState +TgPeerConnection::peer_connection_state() { + RTC_DCHECK_RUN_ON(signaling_thread()); + return connection_state_; +} + +PeerConnectionInterface::IceGatheringState +TgPeerConnection::ice_gathering_state() { + RTC_DCHECK_RUN_ON(signaling_thread()); + return ice_gathering_state_; +} + +void TgPeerConnection::RestartIce() { + RTC_DCHECK_RUN_ON(signaling_thread()); + local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions( + current_local_description_.get(), pending_local_description_.get()); + UpdateNegotiationNeeded(); +} + +void TgPeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer, + const RTCOfferAnswerOptions& options) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + options](std::function operations_chain_callback) { + // Abort early if |this_weak_ptr| is no longer valid. + if (!this_weak_ptr) { + observer_refptr->OnFailure( + RTCError(RTCErrorType::INTERNAL_ERROR, + "CreateOffer failed because the session was shut down")); + operations_chain_callback(); + return; + } + // The operation completes asynchronously when the wrapper is invoked. + rtc::scoped_refptr + observer_wrapper(new rtc::RefCountedObject< + CreateSessionDescriptionObserverOperationWrapper>( + std::move(observer_refptr), + std::move(operations_chain_callback))); + this_weak_ptr->DoCreateOffer(options, observer_wrapper); + }); +} + +void TgPeerConnection::DoCreateOffer( + const RTCOfferAnswerOptions& options, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateOffer"); + + if (!observer) { + RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL."; + return; + } + + if (IsClosed()) { + std::string error = "CreateOffer called when TgPeerConnection is closed."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailure( + observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); + return; + } + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message; + PostCreateSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + if (!ValidateOfferAnswerOptions(options)) { + std::string error = "CreateOffer called with invalid options."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailure( + observer, RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error))); + return; + } + + // Legacy handling for offer_to_receive_audio and offer_to_receive_video. + // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions". + if (IsUnifiedPlan()) { + RTCError error = HandleLegacyOfferOptions(options); + if (!error.ok()) { + PostCreateSessionDescriptionFailure(observer, std::move(error)); + return; + } + } + + cricket::MediaSessionOptions session_options; + GetOptionsForOffer(options, &session_options); + webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); +} + +RTCError TgPeerConnection::HandleLegacyOfferOptions( + const RTCOfferAnswerOptions& options) { + RTC_DCHECK(IsUnifiedPlan()); + + if (options.offer_to_receive_audio == 0) { + RemoveRecvDirectionFromReceivingTransceiversOfType( + cricket::MEDIA_TYPE_AUDIO); + } else if (options.offer_to_receive_audio == 1) { + AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO); + } else if (options.offer_to_receive_audio > 1) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, + "offer_to_receive_audio > 1 is not supported."); + } + + if (options.offer_to_receive_video == 0) { + RemoveRecvDirectionFromReceivingTransceiversOfType( + cricket::MEDIA_TYPE_VIDEO); + } else if (options.offer_to_receive_video == 1) { + AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO); + } else if (options.offer_to_receive_video > 1) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, + "offer_to_receive_video > 1 is not supported."); + } + + return RTCError::OK(); +} + +void TgPeerConnection::RemoveRecvDirectionFromReceivingTransceiversOfType( + cricket::MediaType media_type) { + for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) { + RtpTransceiverDirection new_direction = + RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false); + if (new_direction != transceiver->direction()) { + RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type) + << " transceiver (MID=" + << transceiver->mid().value_or("") << ") from " + << RtpTransceiverDirectionToString( + transceiver->direction()) + << " to " + << RtpTransceiverDirectionToString(new_direction) + << " since CreateOffer specified offer_to_receive=0"; + transceiver->internal()->set_direction(new_direction); + } + } +} + +void TgPeerConnection::AddUpToOneReceivingTransceiverOfType( + cricket::MediaType media_type) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (GetReceivingTransceiversOfType(media_type).empty()) { + RTC_LOG(LS_INFO) + << "Adding one recvonly " << cricket::MediaTypeToString(media_type) + << " transceiver since CreateOffer specified offer_to_receive=1"; + RtpTransceiverInit init; + init.direction = RtpTransceiverDirection::kRecvOnly; + AddTransceiver(media_type, nullptr, init, + /*update_negotiation_needed=*/false); + } +} + +std::vector>> +TgPeerConnection::GetReceivingTransceiversOfType(cricket::MediaType media_type) { + std::vector< + rtc::scoped_refptr>> + receiving_transceivers; + for (const auto& transceiver : transceivers_) { + if (!transceiver->stopped() && transceiver->media_type() == media_type && + RtpTransceiverDirectionHasRecv(transceiver->direction())) { + receiving_transceivers.push_back(transceiver); + } + } + return receiving_transceivers; +} + +void TgPeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer, + const RTCOfferAnswerOptions& options) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + options](std::function operations_chain_callback) { + // Abort early if |this_weak_ptr| is no longer valid. + if (!this_weak_ptr) { + observer_refptr->OnFailure(RTCError( + RTCErrorType::INTERNAL_ERROR, + "CreateAnswer failed because the session was shut down")); + operations_chain_callback(); + return; + } + // The operation completes asynchronously when the wrapper is invoked. + rtc::scoped_refptr + observer_wrapper(new rtc::RefCountedObject< + CreateSessionDescriptionObserverOperationWrapper>( + std::move(observer_refptr), + std::move(operations_chain_callback))); + this_weak_ptr->DoCreateAnswer(options, observer_wrapper); + }); +} + +void TgPeerConnection::DoCreateAnswer( + const RTCOfferAnswerOptions& options, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateAnswer"); + if (!observer) { + RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL."; + return; + } + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message; + PostCreateSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + if (!(signaling_state_ == kHaveRemoteOffer || + signaling_state_ == kHaveLocalPrAnswer)) { + std::string error = + "TgPeerConnection cannot create an answer in a state other than " + "have-remote-offer or have-local-pranswer."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailure( + observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); + return; + } + + // The remote description should be set if we're in the right state. + RTC_DCHECK(remote_description()); + + if (IsUnifiedPlan()) { + if (options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) { + RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not " + "supported with Unified Plan semantics. Use the " + "RtpTransceiver API instead."; + } + if (options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) { + RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not " + "supported with Unified Plan semantics. Use the " + "RtpTransceiver API instead."; + } + } + + cricket::MediaSessionOptions session_options; + GetOptionsForAnswer(options, &session_options); + + webrtc_session_desc_factory_->CreateAnswer(observer, session_options); +} + +void TgPeerConnection::SetLocalDescription( + SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc_ptr) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + desc = std::unique_ptr(desc_ptr)]( + std::function operations_chain_callback) mutable { + // Abort early if |this_weak_ptr| is no longer valid. + if (!this_weak_ptr) { + // For consistency with DoSetLocalDescription(), we DO NOT inform the + // |observer_refptr| that the operation failed in this case. + // TODO(hbos): If/when we process SLD messages in ~TgPeerConnection, + // the consistent thing would be to inform the observer here. + operations_chain_callback(); + return; + } + this_weak_ptr->DoSetLocalDescription(std::move(desc), + std::move(observer_refptr)); + // DoSetLocalDescription() is currently implemented as a synchronous + // operation but where the |observer|'s callbacks are invoked + // asynchronously in a post to OnMessage(). + // For backwards-compatability reasons, we declare the operation as + // completed here (rather than in OnMessage()). This ensures that + // subsequent offer/answer operations can start immediately (without + // waiting for OnMessage()). + operations_chain_callback(); + }); +} + +void TgPeerConnection::SetLocalDescription( + SetSessionDescriptionObserver* observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // The |create_sdp_observer| handles performing DoSetLocalDescription() with + // the resulting description as well as completing the operation. + rtc::scoped_refptr + create_sdp_observer( + new rtc::RefCountedObject( + weak_ptr_factory_.GetWeakPtr(), + rtc::scoped_refptr(observer))); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + create_sdp_observer](std::function operations_chain_callback) { + // The |create_sdp_observer| is responsible for completing the + // operation. + create_sdp_observer->SetOperationCompleteCallback( + std::move(operations_chain_callback)); + // Abort early if |this_weak_ptr| is no longer valid. This triggers the + // same code path as if DoCreateOffer() or DoCreateAnswer() failed. + if (!this_weak_ptr) { + create_sdp_observer->OnFailure(RTCError( + RTCErrorType::INTERNAL_ERROR, + "SetLocalDescription failed because the session was shut down")); + return; + } + switch (this_weak_ptr->signaling_state()) { + case PeerConnectionInterface::kStable: + case PeerConnectionInterface::kHaveLocalOffer: + case PeerConnectionInterface::kHaveRemotePrAnswer: + // TODO(hbos): If [LastCreatedOffer] exists and still represents the + // current state of the system, use that instead of creating another + // offer. + this_weak_ptr->DoCreateOffer(RTCOfferAnswerOptions(), + create_sdp_observer); + break; + case PeerConnectionInterface::kHaveLocalPrAnswer: + case PeerConnectionInterface::kHaveRemoteOffer: + // TODO(hbos): If [LastCreatedAnswer] exists and still represents + // the current state of the system, use that instead of creating + // another answer. + this_weak_ptr->DoCreateAnswer(RTCOfferAnswerOptions(), + create_sdp_observer); + break; + case PeerConnectionInterface::kClosed: + create_sdp_observer->OnFailure(RTCError( + RTCErrorType::INVALID_STATE, + "SetLocalDescription called when TgPeerConnection is closed.")); + break; + } + }); +} + +void TgPeerConnection::DoSetLocalDescription( + std::unique_ptr desc, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetLocalDescription"); + + if (!observer) { + RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL."; + return; + } + + if (!desc) { + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL.")); + return; + } + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message; + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + // For SLD we support only explicit rollback. + if (desc->GetType() == SdpType::kRollback) { + if (IsUnifiedPlan()) { + RTCError error = Rollback(desc->GetType()); + if (error.ok()) { + PostSetSessionDescriptionSuccess(observer); + } else { + PostSetSessionDescriptionFailure(observer, std::move(error)); + } + } else { + PostSetSessionDescriptionFailure( + observer, RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Rollback not supported in Plan B")); + } + return; + } + + RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL); + if (!error.ok()) { + std::string error_message = GetSetDescriptionErrorMessage( + cricket::CS_LOCAL, desc->GetType(), error); + RTC_LOG(LS_ERROR) << error_message; + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + // Grab the description type before moving ownership to ApplyLocalDescription, + // which may destroy it before returning. + const SdpType type = desc->GetType(); + + error = ApplyLocalDescription(std::move(desc)); + // |desc| may be destroyed at this point. + + if (!error.ok()) { + // If ApplyLocalDescription fails, the TgPeerConnection could be in an + // inconsistent state, so act conservatively here and set the session error + // so that future calls to SetLocalDescription/SetRemoteDescription fail. + SetSessionError(SessionError::kContent, error.message()); + std::string error_message = + GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error); + RTC_LOG(LS_ERROR) << error_message; + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + RTC_DCHECK(local_description()); + + PostSetSessionDescriptionSuccess(observer); + + // MaybeStartGathering needs to be called after posting + // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates + // before signaling that SetLocalDescription completed. + transport_controller_->MaybeStartGathering(); + + if (local_description()->GetType() == SdpType::kAnswer) { + // TODO(deadbeef): We already had to hop to the network thread for + // MaybeStartGathering... + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, + port_allocator_.get())); + // Make UMA notes about what was agreed to. + ReportNegotiatedSdpSemantics(*local_description()); + } + + if (IsUnifiedPlan()) { + bool was_negotiation_needed = is_negotiation_needed_; + UpdateNegotiationNeeded(); + if (signaling_state() == kStable && was_negotiation_needed && + is_negotiation_needed_) { + Observer()->OnRenegotiationNeeded(); + } + } + + NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED); +} + +RTCError TgPeerConnection::ApplyLocalDescription( + std::unique_ptr desc) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(desc); + + // Update stats here so that we have the most recent stats for tracks and + // streams that might be removed by updating the session description. + stats_->UpdateStats(kStatsOutputLevelStandard); + + // Take a reference to the old local description since it's used below to + // compare against the new local description. When setting the new local + // description, grab ownership of the replaced session description in case it + // is the same as |old_local_description|, to keep it alive for the duration + // of the method. + const SessionDescriptionInterface* old_local_description = + local_description(); + std::unique_ptr replaced_local_description; + SdpType type = desc->GetType(); + if (type == SdpType::kAnswer) { + replaced_local_description = pending_local_description_ + ? std::move(pending_local_description_) + : std::move(current_local_description_); + current_local_description_ = std::move(desc); + pending_local_description_ = nullptr; + current_remote_description_ = std::move(pending_remote_description_); + } else { + replaced_local_description = std::move(pending_local_description_); + pending_local_description_ = std::move(desc); + } + // The session description to apply now must be accessed by + // |local_description()|. + RTC_DCHECK(local_description()); + + // Report statistics about any use of simulcast. + ReportSimulcastApiVersion(kSimulcastVersionApplyLocalDescription, + *local_description()->description()); + + if (!is_caller_) { + if (remote_description()) { + // Remote description was applied first, so this PC is the callee. + is_caller_ = false; + } else { + // Local description is applied first, so this PC is the caller. + is_caller_ = true; + } + } + + RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type); + if (!error.ok()) { + return error; + } + + if (IsUnifiedPlan()) { + RTCError error = UpdateTransceiversAndDataChannels( + cricket::CS_LOCAL, *local_description(), old_local_description, + remote_description()); + if (!error.ok()) { + return error; + } + std::vector> remove_list; + std::vector> removed_streams; + for (const auto& transceiver : transceivers_) { + // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots. + // Note that code paths that don't set MID won't be able to use + // information about DTLS transports. + if (transceiver->mid()) { + auto dtls_transport = + LookupDtlsTransportByMidInternal(*transceiver->mid()); + transceiver->internal()->sender_internal()->set_transport( + dtls_transport); + transceiver->internal()->receiver_internal()->set_transport( + dtls_transport); + } + + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, local_description()); + if (!content) { + continue; + } + const MediaContentDescription* media_desc = content->media_description(); + // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run + // the following steps: + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and + // transceiver's [[FiredDirection]] slot is either "sendrecv" or + // "recvonly", process the removal of a remote track for the media + // description, given transceiver, removeList, and muteTracks. + if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) && + (transceiver->internal()->fired_direction() && + RtpTransceiverDirectionHasRecv( + *transceiver->internal()->fired_direction()))) { + ProcessRemovalOfRemoteTrack(transceiver, &remove_list, + &removed_streams); + } + // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and + // [[FiredDirection]] slots to direction. + transceiver->internal()->set_current_direction(media_desc->direction()); + transceiver->internal()->set_fired_direction(media_desc->direction()); + } + } + auto observer = Observer(); + for (const auto& transceiver : remove_list) { + observer->OnRemoveTrack(transceiver->receiver()); + } + for (const auto& stream : removed_streams) { + observer->OnRemoveStream(stream); + } + } else { + // Media channels will be created only when offer is set. These may use new + // transports just created by PushdownTransportDescription. + if (type == SdpType::kOffer) { + // TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local + // description is applied. Restore back to old description. + RTCError error = CreateChannels(*local_description()->description()); + if (!error.ok()) { + return error; + } + } + // Remove unused channels if MediaContentDescription is rejected. + RemoveUnusedChannels(local_description()->description()); + } + + error = UpdateSessionState(type, cricket::CS_LOCAL, + local_description()->description()); + if (!error.ok()) { + return error; + } + + if (remote_description()) { + // Now that we have a local description, we can push down remote candidates. + UseCandidatesInSessionDescription(remote_description()); + } + + pending_ice_restarts_.clear(); + if (session_error() != SessionError::kNone) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); + } + + if (IsUnifiedPlan()) { + for (const auto& transceiver : transceivers_) { + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, local_description()); + if (!content) { + continue; + } + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (content->rejected || !channel || channel->local_streams().empty()) { + // 0 is a special value meaning "this sender has no associated send + // stream". Need to call this so the sender won't attempt to configure + // a no longer existing stream and run into DCHECKs in the lower + // layers. + transceiver->internal()->sender_internal()->SetSsrc(0); + } else { + // Get the StreamParams from the channel which could generate SSRCs. + const std::vector& streams = channel->local_streams(); + transceiver->internal()->sender_internal()->set_stream_ids( + streams[0].stream_ids()); + transceiver->internal()->sender_internal()->SetSsrc( + streams[0].first_ssrc()); + } + } + } else { + // Plan B semantics. + + // Update state and SSRC of local MediaStreams and DataChannels based on the + // local session description. + const cricket::ContentInfo* audio_content = + GetFirstAudioContent(local_description()->description()); + if (audio_content) { + if (audio_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_AUDIO); + } else { + const cricket::AudioContentDescription* audio_desc = + audio_content->media_description()->as_audio(); + UpdateLocalSenders(audio_desc->streams(), audio_desc->type()); + } + } + + const cricket::ContentInfo* video_content = + GetFirstVideoContent(local_description()->description()); + if (video_content) { + if (video_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_VIDEO); + } else { + const cricket::VideoContentDescription* video_desc = + video_content->media_description()->as_video(); + UpdateLocalSenders(video_desc->streams(), video_desc->type()); + } + } + } + + if (type == SdpType::kAnswer && + local_ice_credentials_to_replace_->SatisfiesIceRestart( + *current_local_description_)) { + local_ice_credentials_to_replace_->ClearIceCredentials(); + } + + return RTCError::OK(); +} + +// The SDP parser used to populate these values by default for the 'content +// name' if an a=mid line was absent. +static absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) { + switch (media_type) { + case cricket::MEDIA_TYPE_AUDIO: + return cricket::CN_AUDIO; + case cricket::MEDIA_TYPE_VIDEO: + return cricket::CN_VIDEO; + case cricket::MEDIA_TYPE_DATA: + return cricket::CN_DATA; + } + RTC_NOTREACHED(); + return ""; +} + +void TgPeerConnection::FillInMissingRemoteMids( + cricket::SessionDescription* new_remote_description) { + RTC_DCHECK(new_remote_description); + const cricket::ContentInfos no_infos; + const cricket::ContentInfos& local_contents = + (local_description() ? local_description()->description()->contents() + : no_infos); + const cricket::ContentInfos& remote_contents = + (remote_description() ? remote_description()->description()->contents() + : no_infos); + for (size_t i = 0; i < new_remote_description->contents().size(); ++i) { + cricket::ContentInfo& content = new_remote_description->contents()[i]; + if (!content.name.empty()) { + continue; + } + std::string new_mid; + absl::string_view source_explanation; + if (IsUnifiedPlan()) { + if (i < local_contents.size()) { + new_mid = local_contents[i].name; + source_explanation = "from the matching local media section"; + } else if (i < remote_contents.size()) { + new_mid = remote_contents[i].name; + source_explanation = "from the matching previous remote media section"; + } else { + new_mid = mid_generator_(); + source_explanation = "generated just now"; + } + } else { + new_mid = std::string( + GetDefaultMidForPlanB(content.media_description()->type())); + source_explanation = "to match pre-existing behavior"; + } + RTC_DCHECK(!new_mid.empty()); + content.name = new_mid; + new_remote_description->transport_infos()[i].content_name = new_mid; + RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i + << " is missing an a=mid line. Filling in the value '" + << new_mid << "' " << source_explanation << "."; + } +} + +void TgPeerConnection::SetRemoteDescription( + SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc_ptr) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + desc = std::unique_ptr(desc_ptr)]( + std::function operations_chain_callback) mutable { + // Abort early if |this_weak_ptr| is no longer valid. + if (!this_weak_ptr) { + // For consistency with SetRemoteDescriptionObserverAdapter, we DO NOT + // inform the |observer_refptr| that the operation failed in this + // case. + // TODO(hbos): If/when we process SRD messages in ~TgPeerConnection, + // the consistent thing would be to inform the observer here. + operations_chain_callback(); + return; + } + this_weak_ptr->DoSetRemoteDescription( + std::move(desc), + rtc::scoped_refptr( + new SetRemoteDescriptionObserverAdapter( + this_weak_ptr.get(), std::move(observer_refptr)))); + // DoSetRemoteDescription() is currently implemented as a synchronous + // operation but where SetRemoteDescriptionObserverAdapter ensures that + // the |observer|'s callbacks are invoked asynchronously in a post to + // OnMessage(). + // For backwards-compatability reasons, we declare the operation as + // completed here (rather than in OnMessage()). This ensures that + // subsequent offer/answer operations can start immediately (without + // waiting for OnMessage()). + operations_chain_callback(); + }); +} + +void TgPeerConnection::SetRemoteDescription( + std::unique_ptr desc, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer, + desc = std::move(desc)]( + std::function operations_chain_callback) mutable { + // Abort early if |this_weak_ptr| is no longer valid. + if (!this_weak_ptr) { + // For consistency with DoSetRemoteDescription(), we DO inform the + // |observer| that the operation failed in this case. + observer->OnSetRemoteDescriptionComplete(RTCError( + RTCErrorType::INVALID_STATE, + "Failed to set remote offer sdp: failed because the session was " + "shut down")); + operations_chain_callback(); + return; + } + this_weak_ptr->DoSetRemoteDescription(std::move(desc), + std::move(observer)); + // DoSetRemoteDescription() is currently implemented as a synchronous + // operation. The |observer| will already have been informed that it + // completed, and we can mark this operation as complete without any + // loose ends. + operations_chain_callback(); + }); +} + +void TgPeerConnection::DoSetRemoteDescription( + std::unique_ptr desc, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetRemoteDescription"); + + if (!observer) { + RTC_LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL."; + return; + } + + if (!desc) { + observer->OnSetRemoteDescriptionComplete(RTCError( + RTCErrorType::INVALID_PARAMETER, "SessionDescription is NULL.")); + return; + } + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "SetRemoteDescription: " << error_message; + observer->OnSetRemoteDescriptionComplete( + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + if (IsUnifiedPlan()) { + if (configuration_.enable_implicit_rollback) { + if (desc->GetType() == SdpType::kOffer && + signaling_state() == kHaveLocalOffer) { + Rollback(desc->GetType()); + } + } + // Explicit rollback. + if (desc->GetType() == SdpType::kRollback) { + observer->OnSetRemoteDescriptionComplete(Rollback(desc->GetType())); + return; + } + } else if (desc->GetType() == SdpType::kRollback) { + observer->OnSetRemoteDescriptionComplete( + RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Rollback not supported in Plan B")); + return; + } + if (desc->GetType() == SdpType::kOffer) { + // Report to UMA the format of the received offer. + ReportSdpFormatReceived(*desc); + } + + // Handle remote descriptions missing a=mid lines for interop with legacy end + // points. + FillInMissingRemoteMids(desc->description()); + + RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE); + if (!error.ok()) { + std::string error_message = GetSetDescriptionErrorMessage( + cricket::CS_REMOTE, desc->GetType(), error); + RTC_LOG(LS_ERROR) << error_message; + observer->OnSetRemoteDescriptionComplete( + RTCError(error.type(), std::move(error_message))); + return; + } + + // Grab the description type before moving ownership to + // ApplyRemoteDescription, which may destroy it before returning. + const SdpType type = desc->GetType(); + + error = ApplyRemoteDescription(std::move(desc)); + // |desc| may be destroyed at this point. + + if (!error.ok()) { + // If ApplyRemoteDescription fails, the TgPeerConnection could be in an + // inconsistent state, so act conservatively here and set the session error + // so that future calls to SetLocalDescription/SetRemoteDescription fail. + SetSessionError(SessionError::kContent, error.message()); + std::string error_message = + GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type, error); + RTC_LOG(LS_ERROR) << error_message; + observer->OnSetRemoteDescriptionComplete( + RTCError(error.type(), std::move(error_message))); + return; + } + RTC_DCHECK(remote_description()); + + if (type == SdpType::kAnswer) { + // TODO(deadbeef): We already had to hop to the network thread for + // MaybeStartGathering... + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, + port_allocator_.get())); + // Make UMA notes about what was agreed to. + ReportNegotiatedSdpSemantics(*remote_description()); + } + + if (IsUnifiedPlan()) { + bool was_negotiation_needed = is_negotiation_needed_; + UpdateNegotiationNeeded(); + if (signaling_state() == kStable && was_negotiation_needed && + is_negotiation_needed_) { + Observer()->OnRenegotiationNeeded(); + } + } + + observer->OnSetRemoteDescriptionComplete(RTCError::OK()); + NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED); +} + +RTCError TgPeerConnection::ApplyRemoteDescription( + std::unique_ptr desc) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(desc); + + // Update stats here so that we have the most recent stats for tracks and + // streams that might be removed by updating the session description. + stats_->UpdateStats(kStatsOutputLevelStandard); + + // Take a reference to the old remote description since it's used below to + // compare against the new remote description. When setting the new remote + // description, grab ownership of the replaced session description in case it + // is the same as |old_remote_description|, to keep it alive for the duration + // of the method. + const SessionDescriptionInterface* old_remote_description = + remote_description(); + std::unique_ptr replaced_remote_description; + SdpType type = desc->GetType(); + if (type == SdpType::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); + } + // The session description to apply now must be accessed by + // |remote_description()|. + RTC_DCHECK(remote_description()); + + // Report statistics about any use of simulcast. + ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription, + *remote_description()->description()); + + RTCError error = PushdownTransportDescription(cricket::CS_REMOTE, type); + if (!error.ok()) { + return error; + } + // Transport and Media channels will be created only when offer is set. + if (IsUnifiedPlan()) { + RTCError error = UpdateTransceiversAndDataChannels( + cricket::CS_REMOTE, *remote_description(), local_description(), + old_remote_description); + if (!error.ok()) { + return error; + } + } else { + // Media channels will be created only when offer is set. These may use new + // transports just created by PushdownTransportDescription. + if (type == SdpType::kOffer) { + // TODO(mallinath) - Handle CreateChannel failure, as new local + // description is applied. Restore back to old description. + RTCError error = CreateChannels(*remote_description()->description()); + if (!error.ok()) { + return error; + } + } + // Remove unused channels if MediaContentDescription is rejected. + RemoveUnusedChannels(remote_description()->description()); + } + + // NOTE: Candidates allocation will be initiated only when + // SetLocalDescription is called. + error = UpdateSessionState(type, cricket::CS_REMOTE, + remote_description()->description()); + if (!error.ok()) { + return error; + } + + if (local_description() && + !UseCandidatesInSessionDescription(remote_description())) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidCandidates); + } + + 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 (type == SdpType::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, mutable_remote_description()); + } + } + } + + if (session_error() != SessionError::kNone) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); + } + + // 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()->GetType() != SdpType::kOffer && + remote_description()->number_of_mediasections() > 0u && + ice_connection_state() == PeerConnectionInterface::kIceConnectionNew) { + SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); + } + + if (IsUnifiedPlan()) { + std::vector> + now_receiving_transceivers; + std::vector> remove_list; + std::vector> added_streams; + std::vector> removed_streams; + for (const auto& transceiver : transceivers_) { + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, remote_description()); + if (!content) { + continue; + } + const MediaContentDescription* media_desc = content->media_description(); + RtpTransceiverDirection local_direction = + RtpTransceiverDirectionReversed(media_desc->direction()); + // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the + // RTCSessionDescription: Set the associated remote streams given + // transceiver.[[Receiver]], msids, addList, and removeList". + // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription + if (RtpTransceiverDirectionHasRecv(local_direction)) { + std::vector stream_ids; + if (!media_desc->streams().empty()) { + // The remote description has signaled the stream IDs. + stream_ids = media_desc->streams()[0].stream_ids(); + } + transceiver_stable_states_by_transceivers_[transceiver] + .SetRemoteStreamIdsIfUnset(transceiver->receiver()->stream_ids()); + + RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name + << " (" << GetStreamIdsString(stream_ids) << ")."; + SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), + stream_ids, &added_streams, + &removed_streams); + // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6 + // "Set the RTCSessionDescription: If direction is sendrecv or recvonly, + // and transceiver's current direction is neither sendrecv nor recvonly, + // process the addition of a remote track for the media description. + if (!transceiver->fired_direction() || + !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) { + RTC_LOG(LS_INFO) + << "Processing the addition of a remote track for MID=" + << content->name << "."; + now_receiving_transceivers.push_back(transceiver); + } + } + // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's + // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the + // removal of a remote track for the media description, given transceiver, + // removeList, and muteTracks. + if (!RtpTransceiverDirectionHasRecv(local_direction) && + (transceiver->fired_direction() && + RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) { + ProcessRemovalOfRemoteTrack(transceiver, &remove_list, + &removed_streams); + } + // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. + transceiver->internal()->set_fired_direction(local_direction); + // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run + // the following steps: + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to + // direction. + transceiver->internal()->set_current_direction(local_direction); + // 2.2.8.1.11.[3-6]: Set the transport internal slots. + if (transceiver->mid()) { + auto dtls_transport = + LookupDtlsTransportByMidInternal(*transceiver->mid()); + transceiver->internal()->sender_internal()->set_transport( + dtls_transport); + transceiver->internal()->receiver_internal()->set_transport( + dtls_transport); + } + } + // 2.2.8.1.12: If the media description is rejected, and transceiver is + // not already stopped, stop the RTCRtpTransceiver transceiver. + if (content->rejected && !transceiver->stopped()) { + RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name + << " since the media section was rejected."; + transceiver->Stop(); + } + if (!content->rejected && + RtpTransceiverDirectionHasRecv(local_direction)) { + if (!media_desc->streams().empty() && + media_desc->streams()[0].has_ssrcs()) { + uint32_t ssrc = media_desc->streams()[0].first_ssrc(); + transceiver->internal()->receiver_internal()->SetupMediaChannel(ssrc); + } else { + transceiver->internal() + ->receiver_internal() + ->SetupUnsignaledMediaChannel(); + } + } + } + // Once all processing has finished, fire off callbacks. + auto observer = Observer(); + for (const auto& transceiver : now_receiving_transceivers) { + stats_->AddTrack(transceiver->receiver()->track()); + observer->OnTrack(transceiver); + observer->OnAddTrack(transceiver->receiver(), + transceiver->receiver()->streams()); + } + for (const auto& stream : added_streams) { + observer->OnAddStream(stream); + } + for (const auto& transceiver : remove_list) { + observer->OnRemoveTrack(transceiver->receiver()); + } + for (const auto& stream : removed_streams) { + observer->OnRemoveStream(stream); + } + } + + const cricket::ContentInfo* audio_content = + GetFirstAudioContent(remote_description()->description()); + const cricket::ContentInfo* video_content = + GetFirstVideoContent(remote_description()->description()); + const cricket::AudioContentDescription* audio_desc = + GetFirstAudioContentDescription(remote_description()->description()); + const cricket::VideoContentDescription* video_desc = + GetFirstVideoContentDescription(remote_description()->description()); + + // Check if the descriptions include streams, just in case the peer supports + // MSID, but doesn't indicate so with "a=msid-semantic". + if (remote_description()->description()->msid_supported() || + (audio_desc && !audio_desc->streams().empty()) || + (video_desc && !video_desc->streams().empty())) { + remote_peer_supports_msid_ = true; + } + + // We wait to signal new streams until we finish processing the description, + // since only at that point will new streams have all their tracks. + rtc::scoped_refptr new_streams(StreamCollection::Create()); + + if (!IsUnifiedPlan()) { + // 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 the SetRemoteDescription caller + // above so the cleanup that relies on them fails to run. The RemoveSenders + // 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. + if (audio_content) { + if (audio_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_AUDIO); + } else { + bool default_audio_track_needed = + !remote_peer_supports_msid_ && + RtpTransceiverDirectionHasSend(audio_desc->direction()); + UpdateRemoteSendersList(GetActiveStreams(audio_desc), + default_audio_track_needed, audio_desc->type(), + new_streams); + } + } + + // Find all video rtp streams and create corresponding remote VideoTracks + // and MediaStreams. + if (video_content) { + if (video_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_VIDEO); + } else { + bool default_video_track_needed = + !remote_peer_supports_msid_ && + RtpTransceiverDirectionHasSend(video_desc->direction()); + UpdateRemoteSendersList(GetActiveStreams(video_desc), + default_video_track_needed, video_desc->type(), + new_streams); + } + } + + // Iterate new_streams and notify the observer about new MediaStreams. + auto observer = Observer(); + for (size_t i = 0; i < new_streams->count(); ++i) { + MediaStreamInterface* new_stream = new_streams->at(i); + stats_->AddStream(new_stream); + observer->OnAddStream( + rtc::scoped_refptr(new_stream)); + } + + UpdateEndedRemoteMediaStreams(); + } + + if (type == SdpType::kAnswer && + local_ice_credentials_to_replace_->SatisfiesIceRestart( + *current_local_description_)) { + local_ice_credentials_to_replace_->ClearIceCredentials(); + } + + return RTCError::OK(); +} + +void TgPeerConnection::SetAssociatedRemoteStreams( + rtc::scoped_refptr receiver, + const std::vector& stream_ids, + std::vector>* added_streams, + std::vector>* removed_streams) { + std::vector> media_streams; + for (const std::string& stream_id : stream_ids) { + rtc::scoped_refptr stream = + remote_streams_->find(stream_id); + if (!stream) { + stream = MediaStreamProxy::Create(rtc::Thread::Current(), + MediaStream::Create(stream_id)); + remote_streams_->AddStream(stream); + added_streams->push_back(stream); + } + media_streams.push_back(stream); + } + // Special case: "a=msid" missing, use random stream ID. + if (media_streams.empty() && + !(remote_description()->description()->msid_signaling() & + cricket::kMsidSignalingMediaSection)) { + if (!missing_msid_default_stream_) { + missing_msid_default_stream_ = MediaStreamProxy::Create( + rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid())); + added_streams->push_back(missing_msid_default_stream_); + } + media_streams.push_back(missing_msid_default_stream_); + } + std::vector> previous_streams = + receiver->streams(); + // SetStreams() will add/remove the receiver's track to/from the streams. This + // differs from the spec - the spec uses an "addList" and "removeList" to + // update the stream-track relationships in a later step. We do this earlier, + // changing the order of things, but the end-result is the same. + // TODO(hbos): When we remove remote_streams(), use set_stream_ids() + // instead. https://crbug.com/webrtc/9480 + receiver->SetStreams(media_streams); + RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); +} + +void TgPeerConnection::ProcessRemovalOfRemoteTrack( + rtc::scoped_refptr> + transceiver, + std::vector>* remove_list, + std::vector>* removed_streams) { + RTC_DCHECK(transceiver->mid()); + RTC_LOG(LS_INFO) << "Processing the removal of a track for MID=" + << *transceiver->mid(); + std::vector> previous_streams = + transceiver->internal()->receiver_internal()->streams(); + // This will remove the remote track from the streams. + transceiver->internal()->receiver_internal()->set_stream_ids({}); + remove_list->push_back(transceiver); + RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); +} + +void TgPeerConnection::RemoveRemoteStreamsIfEmpty( + const std::vector>& remote_streams, + std::vector>* removed_streams) { + // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of + // streams, see if the stream was removed by checking if this was the last + // receiver with that stream ID. + for (const auto& remote_stream : remote_streams) { + if (remote_stream->GetAudioTracks().empty() && + remote_stream->GetVideoTracks().empty()) { + remote_streams_->RemoveStream(remote_stream); + removed_streams->push_back(remote_stream); + } + } +} + +RTCError TgPeerConnection::UpdateTransceiversAndDataChannels( + cricket::ContentSource source, + const SessionDescriptionInterface& new_session, + const SessionDescriptionInterface* old_local_description, + const SessionDescriptionInterface* old_remote_description) { + RTC_DCHECK(IsUnifiedPlan()); + + const cricket::ContentGroup* bundle_group = nullptr; + if (new_session.GetType() == SdpType::kOffer) { + auto bundle_group_or_error = + GetEarlyBundleGroup(*new_session.description()); + if (!bundle_group_or_error.ok()) { + return bundle_group_or_error.MoveError(); + } + bundle_group = bundle_group_or_error.MoveValue(); + } + + const ContentInfos& new_contents = new_session.description()->contents(); + for (size_t i = 0; i < new_contents.size(); ++i) { + const cricket::ContentInfo& new_content = new_contents[i]; + cricket::MediaType media_type = new_content.media_description()->type(); + mid_generator_.AddKnownId(new_content.name); + if (media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO) { + const cricket::ContentInfo* old_local_content = nullptr; + if (old_local_description && + i < old_local_description->description()->contents().size()) { + old_local_content = + &old_local_description->description()->contents()[i]; + } + const cricket::ContentInfo* old_remote_content = nullptr; + if (old_remote_description && + i < old_remote_description->description()->contents().size()) { + old_remote_content = + &old_remote_description->description()->contents()[i]; + } + auto transceiver_or_error = + AssociateTransceiver(source, new_session.GetType(), i, new_content, + old_local_content, old_remote_content); + if (!transceiver_or_error.ok()) { + return transceiver_or_error.MoveError(); + } + auto transceiver = transceiver_or_error.MoveValue(); + RTCError error = + UpdateTransceiverChannel(transceiver, new_content, bundle_group); + if (!error.ok()) { + return error; + } + } else { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Unknown section type."); + } + } + + return RTCError::OK(); +} + +RTCError TgPeerConnection::UpdateTransceiverChannel( + rtc::scoped_refptr> + transceiver, + const cricket::ContentInfo& content, + const cricket::ContentGroup* bundle_group) { + RTC_DCHECK(IsUnifiedPlan()); + RTC_DCHECK(transceiver); + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (content.rejected) { + if (channel) { + transceiver->internal()->SetChannel(nullptr); + DestroyChannelInterface(channel); + } + } else { + if (!channel) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + channel = CreateVoiceChannel(content.name); + } else { + RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->media_type()); + channel = CreateVideoChannel(content.name); + } + if (!channel) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INTERNAL_ERROR, + "Failed to create channel for mid=" + content.name); + } + transceiver->internal()->SetChannel(channel); + } + } + return RTCError::OK(); +} + +// This method will extract any send encodings that were sent by the remote +// connection. This is currently only relevant for Simulcast scenario (where +// the number of layers may be communicated by the server). +static std::vector GetSendEncodingsFromRemoteDescription( + const MediaContentDescription& desc) { + if (!desc.HasSimulcast()) { + return {}; + } + std::vector result; + const SimulcastDescription& simulcast = desc.simulcast_description(); + + // This is a remote description, the parameters we are after should appear + // as receive streams. + for (const auto& alternatives : simulcast.receive_layers()) { + RTC_DCHECK(!alternatives.empty()); + // There is currently no way to specify or choose from alternatives. + // We will always use the first alternative, which is the most preferred. + const SimulcastLayer& layer = alternatives[0]; + RtpEncodingParameters parameters; + parameters.rid = layer.rid; + parameters.active = !layer.is_paused; + result.push_back(parameters); + } + + return result; +} + +static RTCError UpdateSimulcastLayerStatusInSender( + const std::vector& layers, + rtc::scoped_refptr sender) { + RTC_DCHECK(sender); + RtpParameters parameters = sender->GetParametersInternal(); + std::vector disabled_layers; + + // The simulcast envelope cannot be changed, only the status of the streams. + // So we will iterate over the send encodings rather than the layers. + for (RtpEncodingParameters& encoding : parameters.encodings) { + auto iter = std::find_if(layers.begin(), layers.end(), + [&encoding](const SimulcastLayer& layer) { + return layer.rid == encoding.rid; + }); + // A layer that cannot be found may have been removed by the remote party. + if (iter == layers.end()) { + disabled_layers.push_back(encoding.rid); + continue; + } + + encoding.active = !iter->is_paused; + } + + RTCError result = sender->SetParametersInternal(parameters); + if (result.ok()) { + result = sender->DisableEncodingLayers(disabled_layers); + } + + return result; +} + +static bool SimulcastIsRejected( + const ContentInfo* local_content, + const MediaContentDescription& answer_media_desc) { + bool simulcast_offered = local_content && + local_content->media_description() && + local_content->media_description()->HasSimulcast(); + bool simulcast_answered = answer_media_desc.HasSimulcast(); + bool rids_supported = RtpExtension::FindHeaderExtensionByUri( + answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri); + return simulcast_offered && (!simulcast_answered || !rids_supported); +} + +static RTCError DisableSimulcastInSender( + rtc::scoped_refptr sender) { + RTC_DCHECK(sender); + RtpParameters parameters = sender->GetParametersInternal(); + if (parameters.encodings.size() <= 1) { + return RTCError::OK(); + } + + std::vector disabled_layers; + std::transform( + parameters.encodings.begin() + 1, parameters.encodings.end(), + std::back_inserter(disabled_layers), + [](const RtpEncodingParameters& encoding) { return encoding.rid; }); + return sender->DisableEncodingLayers(disabled_layers); +} + +RTCErrorOr>> +TgPeerConnection::AssociateTransceiver(cricket::ContentSource source, + SdpType type, + size_t mline_index, + const ContentInfo& content, + const ContentInfo* old_local_content, + const ContentInfo* old_remote_content) { + RTC_DCHECK(IsUnifiedPlan()); + // If this is an offer then the m= section might be recycled. If the m= + // section is being recycled (defined as: rejected in the current local or + // remote description and not rejected in new description), dissociate the + // currently associated RtpTransceiver by setting its mid property to null, + // and discard the mapping between the transceiver and its m= section index. + if (IsMediaSectionBeingRecycled(type, content, old_local_content, + old_remote_content)) { + // We want to dissociate the transceiver that has the rejected mid. + const std::string& old_mid = + (old_local_content && old_local_content->rejected) + ? old_local_content->name + : old_remote_content->name; + auto old_transceiver = GetAssociatedTransceiver(old_mid); + if (old_transceiver) { + RTC_LOG(LS_INFO) << "Dissociating transceiver for MID=" << old_mid + << " since the media section is being recycled."; + old_transceiver->internal()->set_mid(absl::nullopt); + old_transceiver->internal()->set_mline_index(absl::nullopt); + } + } + const MediaContentDescription* media_desc = content.media_description(); + auto transceiver = GetAssociatedTransceiver(content.name); + if (source == cricket::CS_LOCAL) { + // Find the RtpTransceiver that corresponds to this m= section, using the + // mapping between transceivers and m= section indices established when + // creating the offer. + if (!transceiver) { + transceiver = GetTransceiverByMLineIndex(mline_index); + } + if (!transceiver) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Unknown transceiver"); + } + } else { + RTC_DCHECK_EQ(source, cricket::CS_REMOTE); + // If the m= section is sendrecv or recvonly, and there are RtpTransceivers + // of the same type... + // When simulcast is requested, a transceiver cannot be associated because + // AddTrack cannot be called to initialize it. + if (!transceiver && + RtpTransceiverDirectionHasRecv(media_desc->direction()) && + !media_desc->HasSimulcast()) { + transceiver = FindAvailableTransceiverToReceive(media_desc->type()); + } + // If no RtpTransceiver was found in the previous step, create one with a + // recvonly direction. + if (!transceiver) { + RTC_LOG(LS_INFO) << "Adding " + << cricket::MediaTypeToString(media_desc->type()) + << " transceiver for MID=" << content.name + << " at i=" << mline_index + << " in response to the remote description."; + std::string sender_id = rtc::CreateRandomUuid(); + std::vector send_encodings = + GetSendEncodingsFromRemoteDescription(*media_desc); + auto sender = CreateSender(media_desc->type(), sender_id, nullptr, {}, + send_encodings); + std::string receiver_id; + if (!media_desc->streams().empty()) { + receiver_id = media_desc->streams()[0].id; + } else { + receiver_id = rtc::CreateRandomUuid(); + } + auto receiver = CreateReceiver(media_desc->type(), receiver_id); + transceiver = CreateAndAddTransceiver(sender, receiver); + transceiver->internal()->set_direction( + RtpTransceiverDirection::kRecvOnly); + if (type == SdpType::kOffer) { + transceiver_stable_states_by_transceivers_[transceiver] + .set_newly_created(); + } + } + // Check if the offer indicated simulcast but the answer rejected it. + // This can happen when simulcast is not supported on the remote party. + if (SimulcastIsRejected(old_local_content, *media_desc)) { + RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true); + RTCError error = + DisableSimulcastInSender(transceiver->internal()->sender_internal()); + if (!error.ok()) { + RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast."; + return std::move(error); + } + } + } + RTC_DCHECK(transceiver); + if (transceiver->media_type() != media_desc->type()) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "Transceiver type does not match media description type."); + } + if (media_desc->HasSimulcast()) { + std::vector layers = + source == cricket::CS_LOCAL + ? media_desc->simulcast_description().send_layers().GetAllLayers() + : media_desc->simulcast_description() + .receive_layers() + .GetAllLayers(); + RTCError error = UpdateSimulcastLayerStatusInSender( + layers, transceiver->internal()->sender_internal()); + if (!error.ok()) { + RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers."; + return std::move(error); + } + } + if (type == SdpType::kOffer) { + bool state_changes = transceiver->internal()->mid() != content.name || + transceiver->internal()->mline_index() != mline_index; + if (state_changes) { + transceiver_stable_states_by_transceivers_[transceiver] + .SetMSectionIfUnset(transceiver->internal()->mid(), + transceiver->internal()->mline_index()); + } + } + // Associate the found or created RtpTransceiver with the m= section by + // setting the value of the RtpTransceiver's mid property to the MID of the m= + // section, and establish a mapping between the transceiver and the index of + // the m= section. + transceiver->internal()->set_mid(content.name); + transceiver->internal()->set_mline_index(mline_index); + return std::move(transceiver); +} + +rtc::scoped_refptr> +TgPeerConnection::GetAssociatedTransceiver(const std::string& mid) const { + RTC_DCHECK(IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->mid() == mid) { + return transceiver; + } + } + return nullptr; +} + +rtc::scoped_refptr> +TgPeerConnection::GetTransceiverByMLineIndex(size_t mline_index) const { + RTC_DCHECK(IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->internal()->mline_index() == mline_index) { + return transceiver; + } + } + return nullptr; +} + +rtc::scoped_refptr> +TgPeerConnection::FindAvailableTransceiverToReceive( + cricket::MediaType media_type) const { + RTC_DCHECK(IsUnifiedPlan()); + // From JSEP section 5.10 (Applying a Remote Description): + // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of + // the same type that were added to the TgPeerConnection by addTrack and are not + // associated with any m= section and are not stopped, find the first such + // RtpTransceiver. + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == media_type && + transceiver->internal()->created_by_addtrack() && !transceiver->mid() && + !transceiver->stopped()) { + return transceiver; + } + } + return nullptr; +} + +const cricket::ContentInfo* TgPeerConnection::FindMediaSectionForTransceiver( + rtc::scoped_refptr> + transceiver, + const SessionDescriptionInterface* sdesc) const { + RTC_DCHECK(transceiver); + RTC_DCHECK(sdesc); + if (IsUnifiedPlan()) { + if (!transceiver->internal()->mid()) { + // This transceiver is not associated with a media section yet. + return nullptr; + } + return sdesc->description()->GetContentByName( + *transceiver->internal()->mid()); + } else { + // Plan B only allows at most one audio and one video section, so use the + // first media section of that type. + return cricket::GetFirstMediaContent(sdesc->description()->contents(), + transceiver->media_type()); + } +} + +PeerConnectionInterface::RTCConfiguration TgPeerConnection::GetConfiguration() { + RTC_DCHECK_RUN_ON(signaling_thread()); + return configuration_; +} + +RTCError TgPeerConnection::SetConfiguration( + const RTCConfiguration& configuration) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::SetConfiguration"); + if (IsClosed()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "SetConfiguration: TgPeerConnection is closed."); + } + + // According to JSEP, after setLocalDescription, changing the candidate pool + // size is not allowed, and changing the set of ICE servers will not result + // in new candidates being gathered. + if (local_description() && configuration.ice_candidate_pool_size != + configuration_.ice_candidate_pool_size) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change candidate pool size after calling " + "SetLocalDescription."); + } + + if (local_description() && + configuration.crypto_options != configuration_.crypto_options) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change crypto_options after calling " + "SetLocalDescription."); + } + + if (local_description() && configuration.use_datagram_transport != + configuration_.use_datagram_transport) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport " + "after calling SetLocalDescription."); + } + + if (remote_description() && configuration.use_datagram_transport != + configuration_.use_datagram_transport) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport " + "after calling SetRemoteDescription."); + } + + if (local_description() && + configuration.use_datagram_transport_for_data_channels != + configuration_.use_datagram_transport_for_data_channels) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels " + "after calling SetLocalDescription."); + } + + if (remote_description() && + configuration.use_datagram_transport_for_data_channels != + configuration_.use_datagram_transport_for_data_channels) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels " + "after calling SetRemoteDescription."); + } + + if (local_description() && + configuration.use_datagram_transport_for_data_channels_receive_only != + configuration_ + .use_datagram_transport_for_data_channels_receive_only) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels_receive_only " + "after calling SetLocalDescription."); + } + + if (remote_description() && + configuration.use_datagram_transport_for_data_channels_receive_only != + configuration_ + .use_datagram_transport_for_data_channels_receive_only) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels_receive_only " + "after calling SetRemoteDescription."); + } + + if ((configuration.use_datagram_transport && + *configuration.use_datagram_transport) || + (configuration.use_datagram_transport_for_data_channels && + *configuration.use_datagram_transport_for_data_channels)) { + RTC_CHECK(configuration.bundle_policy == kBundlePolicyMaxBundle) + << "Media transport requires MaxBundle policy."; + } + + // The simplest (and most future-compatible) way to tell if the config was + // modified in an invalid way is to copy each property we do support + // modifying, then use operator==. There are far more properties we don't + // support modifying than those we do, and more could be added. + RTCConfiguration modified_config = configuration_; + modified_config.servers = configuration.servers; + modified_config.type = configuration.type; + modified_config.ice_candidate_pool_size = + configuration.ice_candidate_pool_size; + modified_config.prune_turn_ports = configuration.prune_turn_ports; + modified_config.turn_port_prune_policy = configuration.turn_port_prune_policy; + modified_config.surface_ice_candidates_on_ice_transport_type_changed = + configuration.surface_ice_candidates_on_ice_transport_type_changed; + modified_config.ice_check_min_interval = configuration.ice_check_min_interval; + modified_config.ice_check_interval_strong_connectivity = + configuration.ice_check_interval_strong_connectivity; + modified_config.ice_check_interval_weak_connectivity = + configuration.ice_check_interval_weak_connectivity; + modified_config.ice_unwritable_timeout = configuration.ice_unwritable_timeout; + modified_config.ice_unwritable_min_checks = + configuration.ice_unwritable_min_checks; + modified_config.ice_inactive_timeout = configuration.ice_inactive_timeout; + modified_config.stun_candidate_keepalive_interval = + configuration.stun_candidate_keepalive_interval; + modified_config.turn_customizer = configuration.turn_customizer; + modified_config.network_preference = configuration.network_preference; + modified_config.active_reset_srtp_params = + configuration.active_reset_srtp_params; + modified_config.use_datagram_transport = configuration.use_datagram_transport; + modified_config.use_datagram_transport_for_data_channels = + configuration.use_datagram_transport_for_data_channels; + modified_config.use_datagram_transport_for_data_channels_receive_only = + configuration.use_datagram_transport_for_data_channels_receive_only; + modified_config.turn_logging_id = configuration.turn_logging_id; + modified_config.allow_codec_switching = configuration.allow_codec_switching; + if (configuration != modified_config) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Modifying the configuration in an unsupported way."); + } + + // Validate the modified configuration. + RTCError validate_error = ValidateConfiguration(modified_config); + if (!validate_error.ok()) { + return validate_error; + } + + // Note that this isn't possible through chromium, since it's an unsigned + // short in WebIDL. + if (configuration.ice_candidate_pool_size < 0 || + configuration.ice_candidate_pool_size > static_cast(UINT16_MAX)) { + return RTCError(RTCErrorType::INVALID_RANGE); + } + + // Parse ICE servers before hopping to network thread. + cricket::ServerAddresses stun_servers; + std::vector turn_servers; + RTCErrorType parse_error = + ParseIceServers(configuration.servers, &stun_servers, &turn_servers); + if (parse_error != RTCErrorType::NONE) { + return RTCError(parse_error); + } + // Add the turn logging id to all turn servers + for (cricket::RelayServerConfig& turn_server : turn_servers) { + turn_server.turn_logging_id = configuration.turn_logging_id; + } + + // Note if STUN or TURN servers were supplied. + if (!stun_servers.empty()) { + NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); + } + if (!turn_servers.empty()) { + NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); + } + + // In theory this shouldn't fail. + if (!network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::ReconfigurePortAllocator_n, this, + stun_servers, turn_servers, modified_config.type, + modified_config.ice_candidate_pool_size, + modified_config.GetTurnPortPrunePolicy(), + modified_config.turn_customizer, + modified_config.stun_candidate_keepalive_interval, + static_cast(local_description())))) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Failed to apply configuration to PortAllocator."); + } + + // As described in JSEP, calling setConfiguration with new ICE servers or + // candidate policy must set a "needs-ice-restart" bit so that the next offer + // triggers an ICE restart which will pick up the changes. + if (modified_config.servers != configuration_.servers || + modified_config.type != configuration_.type || + modified_config.GetTurnPortPrunePolicy() != + configuration_.GetTurnPortPrunePolicy()) { + transport_controller_->SetNeedsIceRestartFlag(); + } + + transport_controller_->SetIceConfig(ParseIceConfig(modified_config)); + + use_datagram_transport_ = datagram_transport_config_.enabled && + modified_config.use_datagram_transport.value_or( + datagram_transport_config_.default_value); + use_datagram_transport_for_data_channels_ = + datagram_transport_data_channel_config_.enabled && + modified_config.use_datagram_transport_for_data_channels.value_or( + datagram_transport_data_channel_config_.default_value); + use_datagram_transport_for_data_channels_receive_only_ = + modified_config.use_datagram_transport_for_data_channels_receive_only + .value_or(datagram_transport_data_channel_config_.receive_only); + transport_controller_->SetMediaTransportSettings( + use_datagram_transport_, use_datagram_transport_for_data_channels_, + use_datagram_transport_for_data_channels_receive_only_); + + if (configuration_.active_reset_srtp_params != + modified_config.active_reset_srtp_params) { + transport_controller_->SetActiveResetSrtpParams( + modified_config.active_reset_srtp_params); + } + + if (modified_config.allow_codec_switching.has_value()) { + cricket::VideoMediaChannel* video_channel = video_media_channel(); + if (video_channel) { + video_channel->SetVideoCodecSwitchingEnabled( + *modified_config.allow_codec_switching); + } + } + + configuration_ = modified_config; + return RTCError::OK(); +} + +bool TgPeerConnection::AddIceCandidate( + const IceCandidateInterface* ice_candidate) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::AddIceCandidate"); + if (IsClosed()) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: TgPeerConnection is closed."; + NoteAddIceCandidateResult(kAddIceCandidateFailClosed); + return false; + } + + if (!remote_description()) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added " + "without any remote session description."; + NoteAddIceCandidateResult(kAddIceCandidateFailNoRemoteDescription); + return false; + } + + if (!ice_candidate) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null."; + NoteAddIceCandidateResult(kAddIceCandidateFailNullCandidate); + return false; + } + + bool valid = false; + bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid); + if (!valid) { + NoteAddIceCandidateResult(kAddIceCandidateFailNotValid); + return false; + } + + // Add this candidate to the remote session description. + if (!mutable_remote_description()->AddCandidate(ice_candidate)) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used."; + NoteAddIceCandidateResult(kAddIceCandidateFailInAddition); + return false; + } + + if (ready) { + bool result = UseCandidate(ice_candidate); + if (result) { + NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED); + NoteAddIceCandidateResult(kAddIceCandidateSuccess); + } else { + NoteAddIceCandidateResult(kAddIceCandidateFailNotUsable); + } + return result; + } else { + RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate."; + NoteAddIceCandidateResult(kAddIceCandidateFailNotReady); + return true; + } +} + +void TgPeerConnection::AddIceCandidate( + std::unique_ptr candidate, + std::function callback) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + candidate = std::move(candidate), callback = std::move(callback)]( + std::function operations_chain_callback) { + if (!this_weak_ptr) { + operations_chain_callback(); + callback(RTCError( + RTCErrorType::INVALID_STATE, + "AddIceCandidate failed because the session was shut down")); + return; + } + if (!this_weak_ptr->AddIceCandidate(candidate.get())) { + operations_chain_callback(); + // Fail with an error type and message consistent with Chromium. + // TODO(hbos): Fail with error types according to spec. + callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Error processing ICE candidate")); + return; + } + operations_chain_callback(); + callback(RTCError::OK()); + }); +} + +bool TgPeerConnection::RemoveIceCandidates( + const std::vector& candidates) { + TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveIceCandidates"); + RTC_DCHECK_RUN_ON(signaling_thread()); + if (IsClosed()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: TgPeerConnection is closed."; + return false; + } + + if (!remote_description()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed " + "without any remote session description."; + return false; + } + + if (candidates.empty()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty."; + return false; + } + + size_t number_removed = + mutable_remote_description()->RemoveCandidates(candidates); + if (number_removed != candidates.size()) { + RTC_LOG(LS_ERROR) + << "RemoveIceCandidates: Failed to remove candidates. Requested " + << candidates.size() << " but only " << number_removed + << " are removed."; + } + + // Remove the candidates from the transport controller. + RTCError error = transport_controller_->RemoveRemoteCandidates(candidates); + if (!error.ok()) { + RTC_LOG(LS_ERROR) + << "RemoveIceCandidates: Error when removing remote candidates: " + << error.message(); + } + return true; +} + +RTCError TgPeerConnection::SetBitrate(const BitrateSettings& bitrate) { + if (!worker_thread()->IsCurrent()) { + return worker_thread()->Invoke( + RTC_FROM_HERE, [&]() { return SetBitrate(bitrate); }); + } + RTC_DCHECK_RUN_ON(worker_thread()); + + const bool has_min = bitrate.min_bitrate_bps.has_value(); + const bool has_start = bitrate.start_bitrate_bps.has_value(); + const bool has_max = bitrate.max_bitrate_bps.has_value(); + if (has_min && *bitrate.min_bitrate_bps < 0) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "min_bitrate_bps <= 0"); + } + if (has_start) { + if (has_min && *bitrate.start_bitrate_bps < *bitrate.min_bitrate_bps) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "start_bitrate_bps < min_bitrate_bps"); + } else if (*bitrate.start_bitrate_bps < 0) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "curent_bitrate_bps < 0"); + } + } + if (has_max) { + if (has_start && *bitrate.max_bitrate_bps < *bitrate.start_bitrate_bps) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max_bitrate_bps < start_bitrate_bps"); + } else if (has_min && *bitrate.max_bitrate_bps < *bitrate.min_bitrate_bps) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max_bitrate_bps < min_bitrate_bps"); + } else if (*bitrate.max_bitrate_bps < 0) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max_bitrate_bps < 0"); + } + } + + RTC_DCHECK(call_.get()); + call_->SetClientBitratePreferences(bitrate); + + return RTCError::OK(); +} + +void TgPeerConnection::SetAudioPlayout(bool playout) { + if (!worker_thread()->IsCurrent()) { + worker_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::SetAudioPlayout, this, playout)); + return; + } + auto audio_state = + factory_->channel_manager()->media_engine()->voice().GetAudioState(); + audio_state->SetPlayout(playout); +} + +void TgPeerConnection::SetAudioRecording(bool recording) { + if (!worker_thread()->IsCurrent()) { + worker_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::SetAudioRecording, this, recording)); + return; + } + auto audio_state = + factory_->channel_manager()->media_engine()->voice().GetAudioState(); + audio_state->SetRecording(recording); +} + +std::unique_ptr +TgPeerConnection::GetRemoteAudioSSLCertificate() { + std::unique_ptr chain = GetRemoteAudioSSLCertChain(); + if (!chain || !chain->GetSize()) { + return nullptr; + } + return chain->Get(0).Clone(); +} + +std::unique_ptr +TgPeerConnection::GetRemoteAudioSSLCertChain() { + RTC_DCHECK_RUN_ON(signaling_thread()); + auto audio_transceiver = GetFirstAudioTransceiver(); + if (!audio_transceiver || !audio_transceiver->internal()->channel()) { + return nullptr; + } + return transport_controller_->GetRemoteSSLCertChain( + audio_transceiver->internal()->channel()->transport_name()); +} + +rtc::scoped_refptr> +TgPeerConnection::GetFirstAudioTransceiver() const { + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + return transceiver; + } + } + return nullptr; +} + +bool TgPeerConnection::StartRtcEventLog(std::unique_ptr output, + int64_t output_period_ms) { + return worker_thread()->Invoke( + RTC_FROM_HERE, + [this, output = std::move(output), output_period_ms]() mutable { + return StartRtcEventLog_w(std::move(output), output_period_ms); + }); +} + +bool TgPeerConnection::StartRtcEventLog( + std::unique_ptr output) { + int64_t output_period_ms = webrtc::RtcEventLog::kImmediateOutput; + if (field_trial::IsEnabled("WebRTC-RtcEventLogNewFormat")) { + output_period_ms = 5000; + } + return StartRtcEventLog(std::move(output), output_period_ms); +} + +void TgPeerConnection::StopRtcEventLog() { + worker_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::StopRtcEventLog_w, this)); +} + +rtc::scoped_refptr +TgPeerConnection::LookupDtlsTransportByMid(const std::string& mid) { + RTC_DCHECK_RUN_ON(signaling_thread()); + return transport_controller_->LookupDtlsTransportByMid(mid); +} + +rtc::scoped_refptr +TgPeerConnection::LookupDtlsTransportByMidInternal(const std::string& mid) { + RTC_DCHECK_RUN_ON(signaling_thread()); + return transport_controller_->LookupDtlsTransportByMid(mid); +} + +rtc::scoped_refptr TgPeerConnection::GetSctpTransport() + const { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!sctp_mid_) { + return nullptr; + } + return transport_controller_->GetSctpTransport(*sctp_mid_); +} + +const SessionDescriptionInterface* TgPeerConnection::local_description() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_local_description_ ? pending_local_description_.get() + : current_local_description_.get(); +} + +const SessionDescriptionInterface* TgPeerConnection::remote_description() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_remote_description_ ? pending_remote_description_.get() + : current_remote_description_.get(); +} + +const SessionDescriptionInterface* TgPeerConnection::current_local_description() + const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return current_local_description_.get(); +} + +const SessionDescriptionInterface* TgPeerConnection::current_remote_description() + const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return current_remote_description_.get(); +} + +const SessionDescriptionInterface* TgPeerConnection::pending_local_description() + const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_local_description_.get(); +} + +const SessionDescriptionInterface* TgPeerConnection::pending_remote_description() + const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_remote_description_.get(); +} + +void TgPeerConnection::Close() { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::Close"); + // Update stats here so that we have the most recent stats for tracks and + // streams before the channels are closed. + stats_->UpdateStats(kStatsOutputLevelStandard); + + ChangeSignalingState(PeerConnectionInterface::kClosed); + NoteUsageEvent(UsageEvent::CLOSE_CALLED); + + for (const auto& transceiver : transceivers_) { + transceiver->Stop(); + } + + // Ensure that all asynchronous stats requests are completed before destroying + // the transport controller below. + if (stats_collector_) { + stats_collector_->WaitForPendingRequest(); + } + + // Don't destroy BaseChannels until after stats has been cleaned up so that + // the last stats request can still read from the channels. + DestroyAllChannels(); + + // The event log is used in the transport controller, which must be outlived + // by the former. CreateOffer by the peer connection is implemented + // asynchronously and if the peer connection is closed without resetting the + // WebRTC session description factory, the session description factory would + // call the transport controller. + webrtc_session_desc_factory_.reset(); + transport_controller_.reset(); + + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, + port_allocator_.get())); + + worker_thread()->Invoke(RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(worker_thread()); + call_.reset(); + // The event log must outlive call (and any other object that uses it). + event_log_.reset(); + }); + ReportUsagePattern(); + // The .h file says that observer can be discarded after close() returns. + // Make sure this is true. + observer_ = nullptr; +} + +void TgPeerConnection::OnMessage(rtc::Message* msg) { + RTC_DCHECK_RUN_ON(signaling_thread()); + switch (msg->message_id) { + case MSG_SET_SESSIONDESCRIPTION_SUCCESS: { + SetSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnSuccess(); + delete param; + break; + } + case MSG_SET_SESSIONDESCRIPTION_FAILED: { + SetSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnFailure(std::move(param->error)); + delete param; + break; + } + case MSG_CREATE_SESSIONDESCRIPTION_FAILED: { + CreateSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnFailure(std::move(param->error)); + delete param; + break; + } + case MSG_GETSTATS: { + GetStatsMsg* param = static_cast(msg->pdata); + StatsReports reports; + stats_->GetStats(param->track, &reports); + param->observer->OnComplete(reports); + delete param; + break; + } + case MSG_REPORT_USAGE_PATTERN: { + ReportUsagePattern(); + break; + } + default: + RTC_NOTREACHED() << "Not implemented"; + break; + } +} + +cricket::VoiceMediaChannel* TgPeerConnection::voice_media_channel() const { + RTC_DCHECK(!IsUnifiedPlan()); + auto* voice_channel = static_cast( + GetAudioTransceiver()->internal()->channel()); + if (voice_channel) { + return voice_channel->media_channel(); + } else { + return nullptr; + } +} + +cricket::VideoMediaChannel* TgPeerConnection::video_media_channel() const { + RTC_DCHECK(!IsUnifiedPlan()); + auto* video_channel = static_cast( + GetVideoTransceiver()->internal()->channel()); + if (video_channel) { + return video_channel->media_channel(); + } else { + return nullptr; + } +} + +void TgPeerConnection::CreateAudioReceiver( + MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) { + std::vector> streams; + streams.push_back(rtc::scoped_refptr(stream)); + // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use + // the constructor taking stream IDs instead. + auto* audio_receiver = new AudioRtpReceiver( + worker_thread(), remote_sender_info.sender_id, streams); + audio_receiver->SetMediaChannel(voice_media_channel()); + if (remote_sender_info.sender_id == kDefaultAudioSenderId) { + audio_receiver->SetupUnsignaledMediaChannel(); + } else { + audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); + } + auto receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), audio_receiver); + GetAudioTransceiver()->internal()->AddReceiver(receiver); + Observer()->OnAddTrack(receiver, streams); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); +} + +void TgPeerConnection::CreateVideoReceiver( + MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) { + std::vector> streams; + streams.push_back(rtc::scoped_refptr(stream)); + // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use + // the constructor taking stream IDs instead. + auto* video_receiver = new VideoRtpReceiver( + worker_thread(), remote_sender_info.sender_id, streams); + video_receiver->SetMediaChannel(video_media_channel()); + if (remote_sender_info.sender_id == kDefaultVideoSenderId) { + video_receiver->SetupUnsignaledMediaChannel(); + } else { + video_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); + } + auto receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), video_receiver); + GetVideoTransceiver()->internal()->AddReceiver(receiver); + Observer()->OnAddTrack(receiver, streams); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); +} + +// TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote +// description. +rtc::scoped_refptr TgPeerConnection::RemoveAndStopReceiver( + const RtpSenderInfo& remote_sender_info) { + auto receiver = FindReceiverById(remote_sender_info.sender_id); + if (!receiver) { + RTC_LOG(LS_WARNING) << "RtpReceiver for track with id " + << remote_sender_info.sender_id << " doesn't exist."; + return nullptr; + } + if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + GetAudioTransceiver()->internal()->RemoveReceiver(receiver); + } else { + GetVideoTransceiver()->internal()->RemoveReceiver(receiver); + } + return receiver; +} + +void TgPeerConnection::AddAudioTrack(AudioTrackInterface* track, + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + RTC_DCHECK(track); + RTC_DCHECK(stream); + auto sender = FindSenderForTrack(track); + if (sender) { + // We already have a sender for this track, so just change the stream_id + // so that it's correct in the next call to CreateOffer. + sender->internal()->set_stream_ids({stream->id()}); + return; + } + + // Normal case; we've never seen this track before. + auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(), track, + {stream->id()}, {}); + new_sender->internal()->SetMediaChannel(voice_media_channel()); + GetAudioTransceiver()->internal()->AddSender(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 + // occur if a local session description that contains the ID of the sender + // is set before AddStream is called. It can also occur if the local + // session description is not changed and RemoveStream is called, and + // later AddStream is called again with the same stream. + const RtpSenderInfo* sender_info = + FindSenderInfo(local_audio_sender_infos_, stream->id(), track->id()); + if (sender_info) { + new_sender->internal()->SetSsrc(sender_info->first_ssrc); + } +} + +// TODO(deadbeef): Don't destroy RtpSenders here; they should be kept around +// indefinitely, when we have unified plan SDP. +void TgPeerConnection::RemoveAudioTrack(AudioTrackInterface* track, + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + auto sender = FindSenderForTrack(track); + if (!sender) { + RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() + << " doesn't exist."; + return; + } + GetAudioTransceiver()->internal()->RemoveSender(sender); +} + +void TgPeerConnection::AddVideoTrack(VideoTrackInterface* track, + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + RTC_DCHECK(track); + RTC_DCHECK(stream); + auto sender = FindSenderForTrack(track); + if (sender) { + // We already have a sender for this track, so just change the stream_id + // so that it's correct in the next call to CreateOffer. + sender->internal()->set_stream_ids({stream->id()}); + return; + } + + // Normal case; we've never seen this track before. + auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(), track, + {stream->id()}, {}); + new_sender->internal()->SetMediaChannel(video_media_channel()); + GetVideoTransceiver()->internal()->AddSender(new_sender); + const RtpSenderInfo* sender_info = + FindSenderInfo(local_video_sender_infos_, stream->id(), track->id()); + if (sender_info) { + new_sender->internal()->SetSsrc(sender_info->first_ssrc); + } +} + +void TgPeerConnection::RemoveVideoTrack(VideoTrackInterface* track, + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + auto sender = FindSenderForTrack(track); + if (!sender) { + RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() + << " doesn't exist."; + return; + } + GetVideoTransceiver()->internal()->RemoveSender(sender); +} + +void TgPeerConnection::SetIceConnectionState(IceConnectionState new_state) { + if (ice_connection_state_ == new_state) { + return; + } + + // After transitioning to "closed", ignore any additional states from + // TransportController (such as "disconnected"). + if (IsClosed()) { + return; + } + + RTC_LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_ + << " => " << new_state; + RTC_DCHECK(ice_connection_state_ != + PeerConnectionInterface::kIceConnectionClosed); + + ice_connection_state_ = new_state; + Observer()->OnIceConnectionChange(ice_connection_state_); +} + +void TgPeerConnection::SetStandardizedIceConnectionState( + PeerConnectionInterface::IceConnectionState new_state) { + if (standardized_ice_connection_state_ == new_state) { + return; + } + + if (IsClosed()) { + return; + } + + RTC_LOG(LS_INFO) << "Changing standardized IceConnectionState " + << standardized_ice_connection_state_ << " => " << new_state; + + standardized_ice_connection_state_ = new_state; + Observer()->OnStandardizedIceConnectionChange(new_state); +} + +void TgPeerConnection::SetConnectionState( + PeerConnectionInterface::PeerConnectionState new_state) { + if (connection_state_ == new_state) + return; + if (IsClosed()) + return; + connection_state_ = new_state; + Observer()->OnConnectionChange(new_state); +} + +void TgPeerConnection::OnIceGatheringChange( + PeerConnectionInterface::IceGatheringState new_state) { + if (IsClosed()) { + return; + } + ice_gathering_state_ = new_state; + Observer()->OnIceGatheringChange(ice_gathering_state_); +} + +void TgPeerConnection::OnIceCandidate( + std::unique_ptr candidate) { + if (IsClosed()) { + return; + } + ReportIceCandidateCollected(candidate->candidate()); + Observer()->OnIceCandidate(candidate.get()); +} + +void TgPeerConnection::OnIceCandidateError(const std::string& address, + int port, + const std::string& url, + int error_code, + const std::string& error_text) { + if (IsClosed()) { + return; + } + Observer()->OnIceCandidateError(address, port, url, error_code, error_text); + // Leftover not to break wpt test during migration to the new API. + Observer()->OnIceCandidateError(address + ":", url, error_code, error_text); +} + +void TgPeerConnection::OnIceCandidatesRemoved( + const std::vector& candidates) { + if (IsClosed()) { + return; + } + Observer()->OnIceCandidatesRemoved(candidates); +} + +void TgPeerConnection::OnSelectedCandidatePairChanged( + const cricket::CandidatePairChangeEvent& event) { + if (IsClosed()) { + return; + } + + if (event.selected_candidate_pair.local_candidate().type() == + LOCAL_PORT_TYPE && + event.selected_candidate_pair.remote_candidate().type() == + LOCAL_PORT_TYPE) { + NoteUsageEvent(UsageEvent::DIRECT_CONNECTION_SELECTED); + } + + Observer()->OnIceSelectedCandidatePairChanged(event); +} + +void TgPeerConnection::ChangeSignalingState( + PeerConnectionInterface::SignalingState signaling_state) { + if (signaling_state_ == signaling_state) { + return; + } + RTC_LOG(LS_INFO) << "Session: " << session_id() << " Old state: " + << GetSignalingStateString(signaling_state_) + << " New state: " + << GetSignalingStateString(signaling_state); + signaling_state_ = signaling_state; + if (signaling_state == kClosed) { + ice_connection_state_ = kIceConnectionClosed; + Observer()->OnIceConnectionChange(ice_connection_state_); + standardized_ice_connection_state_ = + PeerConnectionInterface::IceConnectionState::kIceConnectionClosed; + connection_state_ = PeerConnectionInterface::PeerConnectionState::kClosed; + Observer()->OnConnectionChange(connection_state_); + if (ice_gathering_state_ != kIceGatheringComplete) { + ice_gathering_state_ = kIceGatheringComplete; + Observer()->OnIceGatheringChange(ice_gathering_state_); + } + } + Observer()->OnSignalingChange(signaling_state_); +} + +void TgPeerConnection::OnAudioTrackAdded(AudioTrackInterface* track, + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + AddAudioTrack(track, stream); + UpdateNegotiationNeeded(); +} + +void TgPeerConnection::OnAudioTrackRemoved(AudioTrackInterface* track, + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + RemoveAudioTrack(track, stream); + UpdateNegotiationNeeded(); +} + +void TgPeerConnection::OnVideoTrackAdded(VideoTrackInterface* track, + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + AddVideoTrack(track, stream); + UpdateNegotiationNeeded(); +} + +void TgPeerConnection::OnVideoTrackRemoved(VideoTrackInterface* track, + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + RemoveVideoTrack(track, stream); + UpdateNegotiationNeeded(); +} + +void TgPeerConnection::PostSetSessionDescriptionSuccess( + SetSessionDescriptionObserver* observer) { + SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); + signaling_thread()->Post(RTC_FROM_HERE, this, + MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg); +} + +void TgPeerConnection::PostSetSessionDescriptionFailure( + SetSessionDescriptionObserver* observer, + RTCError&& error) { + RTC_DCHECK(!error.ok()); + SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); + msg->error = std::move(error); + signaling_thread()->Post(RTC_FROM_HERE, this, + MSG_SET_SESSIONDESCRIPTION_FAILED, msg); +} + +void TgPeerConnection::PostCreateSessionDescriptionFailure( + CreateSessionDescriptionObserver* observer, + RTCError error) { + RTC_DCHECK(!error.ok()); + CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer); + msg->error = std::move(error); + signaling_thread()->Post(RTC_FROM_HERE, this, + MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg); +} + +void TgPeerConnection::GetOptionsForOffer( + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + ExtractSharedMediaSessionOptions(offer_answer_options, session_options); + + if (IsUnifiedPlan()) { + GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options); + } else { + GetOptionsForPlanBOffer(offer_answer_options, session_options); + } + + // Apply ICE restart flag and renomination flag. + bool ice_restart = offer_answer_options.ice_restart || + local_ice_credentials_to_replace_->HasIceCredentials(); + for (auto& options : session_options->media_description_options) { + options.transport_options.ice_restart = ice_restart; + options.transport_options.enable_ice_renomination = + configuration_.enable_ice_renomination; + } + + session_options->rtcp_cname = rtcp_cname_; + session_options->crypto_options = GetCryptoOptions(); + session_options->pooled_ice_credentials = + network_thread()->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, + port_allocator_.get())); + session_options->offer_extmap_allow_mixed = + configuration_.offer_extmap_allow_mixed; + + // If datagram transport is in use, add opaque transport parameters. + if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + for (auto& options : session_options->media_description_options) { + absl::optional params = + transport_controller_->GetTransportParameters(options.mid); + if (!params) { + continue; + } + options.transport_options.opaque_parameters = params; + if ((use_datagram_transport_ && + (options.type == cricket::MEDIA_TYPE_AUDIO || + options.type == cricket::MEDIA_TYPE_VIDEO)) || + (use_datagram_transport_for_data_channels_ && + options.type == cricket::MEDIA_TYPE_DATA)) { + options.alt_protocol = params->protocol; + } + } + } + + // Allow fallback for using obsolete SCTP syntax. + // Note that the default in |session_options| is true, while + // the default in |options| is false. + session_options->use_obsolete_sctp_sdp = + offer_answer_options.use_obsolete_sctp_sdp; +} + +void TgPeerConnection::GetOptionsForPlanBOffer( + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + // Figure out transceiver directional preferences. + bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO); + bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO); + + // By default, generate sendrecv/recvonly m= sections. + bool recv_audio = true; + bool recv_video = true; + + // By default, only offer a new m= section if we have media to send with it. + bool offer_new_audio_description = send_audio; + bool offer_new_video_description = send_video; + bool offer_new_data_description = false; + + // The "offer_to_receive_X" options allow those defaults to be overridden. + if (offer_answer_options.offer_to_receive_audio != + RTCOfferAnswerOptions::kUndefined) { + recv_audio = (offer_answer_options.offer_to_receive_audio > 0); + offer_new_audio_description = + offer_new_audio_description || + (offer_answer_options.offer_to_receive_audio > 0); + } + if (offer_answer_options.offer_to_receive_video != + RTCOfferAnswerOptions::kUndefined) { + recv_video = (offer_answer_options.offer_to_receive_video > 0); + offer_new_video_description = + offer_new_video_description || + (offer_answer_options.offer_to_receive_video > 0); + } + + absl::optional audio_index; + absl::optional video_index; + absl::optional data_index; + // 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 (local_description()) { + GenerateMediaDescriptionOptions( + local_description(), + RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), + RtpTransceiverDirectionFromSendRecv(send_video, recv_video), + &audio_index, &video_index, &data_index, session_options); + } + + // Add audio/video/data m= sections to the end if needed. + if (!audio_index && offer_new_audio_description) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions( + cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO, + RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), + false)); + audio_index = session_options->media_description_options.size() - 1; + } + if (!video_index && offer_new_video_description) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions( + cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO, + RtpTransceiverDirectionFromSendRecv(send_video, recv_video), + false)); + video_index = session_options->media_description_options.size() - 1; + } + + cricket::MediaDescriptionOptions* audio_media_description_options = + !audio_index ? nullptr + : &session_options->media_description_options[*audio_index]; + cricket::MediaDescriptionOptions* video_media_description_options = + !video_index ? nullptr + : &session_options->media_description_options[*video_index]; + + AddPlanBRtpSenderOptions(GetSendersInternal(), + audio_media_description_options, + video_media_description_options, + offer_answer_options.num_simulcast_layers); +} + +static cricket::MediaDescriptionOptions +GetMediaDescriptionOptionsForTransceiver( + rtc::scoped_refptr> + transceiver, + const std::string& mid) { + cricket::MediaDescriptionOptions media_description_options( + transceiver->media_type(), mid, transceiver->direction(), + transceiver->stopped()); + media_description_options.codec_preferences = + transceiver->codec_preferences(); + // This behavior is specified in JSEP. The gist is that: + // 1. The MSID is included if the RtpTransceiver's direction is sendonly or + // sendrecv. + // 2. If the MSID is included, then it must be included in any subsequent + // offer/answer exactly the same until the RtpTransceiver is stopped. + if (transceiver->stopped() || + (!RtpTransceiverDirectionHasSend(transceiver->direction()) && + !transceiver->internal()->has_ever_been_used_to_send())) { + return media_description_options; + } + + cricket::SenderOptions sender_options; + sender_options.track_id = transceiver->sender()->id(); + sender_options.stream_ids = transceiver->sender()->stream_ids(); + + // The following sets up RIDs and Simulcast. + // RIDs are included if Simulcast is requested or if any RID was specified. + RtpParameters send_parameters = + transceiver->internal()->sender_internal()->GetParametersInternal(); + bool has_rids = std::any_of(send_parameters.encodings.begin(), + send_parameters.encodings.end(), + [](const RtpEncodingParameters& encoding) { + return !encoding.rid.empty(); + }); + + std::vector send_rids; + SimulcastLayerList send_layers; + for (const RtpEncodingParameters& encoding : send_parameters.encodings) { + if (encoding.rid.empty()) { + continue; + } + send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend)); + send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active)); + } + + if (has_rids) { + sender_options.rids = send_rids; + } + + sender_options.simulcast_layers = send_layers; + // When RIDs are configured, we must set num_sim_layers to 0 to. + // Otherwise, num_sim_layers must be 1 because either there is no + // simulcast, or simulcast is acheived by munging the SDP. + sender_options.num_sim_layers = has_rids ? 0 : 1; + media_description_options.sender_options.push_back(sender_options); + + return media_description_options; +} + +// Returns the ContentInfo at mline index |i|, or null if none exists. +static const ContentInfo* GetContentByIndex( + const SessionDescriptionInterface* sdesc, + size_t i) { + if (!sdesc) { + return nullptr; + } + const ContentInfos& contents = sdesc->description()->contents(); + return (i < contents.size() ? &contents[i] : nullptr); +} + +void TgPeerConnection::GetOptionsForUnifiedPlanOffer( + const RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial + // Offers) and 5.2.2 (Subsequent Offers). + RTC_DCHECK_EQ(session_options->media_description_options.size(), 0); + const ContentInfos no_infos; + const ContentInfos& local_contents = + (local_description() ? local_description()->description()->contents() + : no_infos); + const ContentInfos& remote_contents = + (remote_description() ? remote_description()->description()->contents() + : no_infos); + // The mline indices that can be recycled. New transceivers should reuse these + // slots first. + std::queue recycleable_mline_indices; + // First, go through each media section that exists in either the local or + // remote description and generate a media section in this offer for the + // associated transceiver. If a media section can be recycled, generate a + // default, rejected media section here that can be later overwritten. + for (size_t i = 0; + i < std::max(local_contents.size(), remote_contents.size()); ++i) { + // Either |local_content| or |remote_content| is non-null. + const ContentInfo* local_content = + (i < local_contents.size() ? &local_contents[i] : nullptr); + const ContentInfo* current_local_content = + GetContentByIndex(current_local_description(), i); + const ContentInfo* remote_content = + (i < remote_contents.size() ? &remote_contents[i] : nullptr); + const ContentInfo* current_remote_content = + GetContentByIndex(current_remote_description(), i); + bool had_been_rejected = + (current_local_content && current_local_content->rejected) || + (current_remote_content && current_remote_content->rejected); + const std::string& mid = + (local_content ? local_content->name : remote_content->name); + cricket::MediaType media_type = + (local_content ? local_content->media_description()->type() + : remote_content->media_description()->type()); + if (media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO) { + auto transceiver = GetAssociatedTransceiver(mid); + RTC_CHECK(transceiver); + // A media section is considered eligible for recycling if it is marked as + // rejected in either the current local or current remote description. + if (had_been_rejected && transceiver->stopped()) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions(transceiver->media_type(), mid, + RtpTransceiverDirection::kInactive, + /*stopped=*/true)); + recycleable_mline_indices.push(i); + } else { + session_options->media_description_options.push_back( + GetMediaDescriptionOptionsForTransceiver(transceiver, mid)); + // CreateOffer shouldn't really cause any state changes in + // TgPeerConnection, but we need a way to match new transceivers to new + // media sections in SetLocalDescription and JSEP specifies this is done + // by recording the index of the media section generated for the + // transceiver in the offer. + transceiver->internal()->set_mline_index(i); + } + } + } + + // Next, look for transceivers that are newly added (that is, are not stopped + // and not associated). Reuse media sections marked as recyclable first, + // otherwise append to the end of the offer. New media sections should be + // added in the order they were added to the TgPeerConnection. + for (const auto& transceiver : transceivers_) { + if (transceiver->mid() || transceiver->stopped()) { + continue; + } + size_t mline_index; + if (!recycleable_mline_indices.empty()) { + mline_index = recycleable_mline_indices.front(); + recycleable_mline_indices.pop(); + session_options->media_description_options[mline_index] = + GetMediaDescriptionOptionsForTransceiver(transceiver, + mid_generator_()); + } else { + mline_index = session_options->media_description_options.size(); + session_options->media_description_options.push_back( + GetMediaDescriptionOptionsForTransceiver(transceiver, + mid_generator_())); + } + // See comment above for why CreateOffer changes the transceiver's state. + transceiver->internal()->set_mline_index(mline_index); + } +} + +void TgPeerConnection::GetOptionsForAnswer( + const RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + ExtractSharedMediaSessionOptions(offer_answer_options, session_options); + + if (IsUnifiedPlan()) { + GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options); + } else { + GetOptionsForPlanBAnswer(offer_answer_options, session_options); + } + + // Apply ICE renomination flag. + for (auto& options : session_options->media_description_options) { + options.transport_options.enable_ice_renomination = + configuration_.enable_ice_renomination; + } + + session_options->rtcp_cname = rtcp_cname_; + session_options->crypto_options = GetCryptoOptions(); + session_options->pooled_ice_credentials = + network_thread()->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, + port_allocator_.get())); + + // If datagram transport is in use, add opaque transport parameters. + if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + for (auto& options : session_options->media_description_options) { + absl::optional params = + transport_controller_->GetTransportParameters(options.mid); + if (!params) { + continue; + } + options.transport_options.opaque_parameters = params; + if ((use_datagram_transport_ && + (options.type == cricket::MEDIA_TYPE_AUDIO || + options.type == cricket::MEDIA_TYPE_VIDEO)) || + (use_datagram_transport_for_data_channels_ && + options.type == cricket::MEDIA_TYPE_DATA)) { + options.alt_protocol = params->protocol; + } + } + } +} + +void TgPeerConnection::GetOptionsForPlanBAnswer( + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + // Figure out transceiver directional preferences. + bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO); + bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO); + + // By default, generate sendrecv/recvonly m= sections. The direction is also + // restricted by the direction in the offer. + bool recv_audio = true; + bool recv_video = true; + + // The "offer_to_receive_X" options allow those defaults to be overridden. + if (offer_answer_options.offer_to_receive_audio != + RTCOfferAnswerOptions::kUndefined) { + recv_audio = (offer_answer_options.offer_to_receive_audio > 0); + } + if (offer_answer_options.offer_to_receive_video != + RTCOfferAnswerOptions::kUndefined) { + recv_video = (offer_answer_options.offer_to_receive_video > 0); + } + + absl::optional audio_index; + absl::optional video_index; + absl::optional data_index; + + // Generate m= sections that match those in the offer. + // Note that mediasession.cc will handle intersection our preferred + // direction with the offered direction. + GenerateMediaDescriptionOptions( + remote_description(), + RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), + RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index, + &video_index, &data_index, session_options); + + cricket::MediaDescriptionOptions* audio_media_description_options = + !audio_index ? nullptr + : &session_options->media_description_options[*audio_index]; + cricket::MediaDescriptionOptions* video_media_description_options = + !video_index ? nullptr + : &session_options->media_description_options[*video_index]; + + AddPlanBRtpSenderOptions(GetSendersInternal(), + audio_media_description_options, + video_media_description_options, + offer_answer_options.num_simulcast_layers); +} + +void TgPeerConnection::GetOptionsForUnifiedPlanAnswer( + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial + // Answers) and 5.3.2 (Subsequent Answers). + RTC_DCHECK(remote_description()); + RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer); + for (const ContentInfo& content : + remote_description()->description()->contents()) { + cricket::MediaType media_type = content.media_description()->type(); + if (media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO) { + auto transceiver = GetAssociatedTransceiver(content.name); + RTC_CHECK(transceiver); + session_options->media_description_options.push_back( + GetMediaDescriptionOptionsForTransceiver(transceiver, content.name)); + } + } +} + +void TgPeerConnection::GenerateMediaDescriptionOptions( + const SessionDescriptionInterface* session_desc, + RtpTransceiverDirection audio_direction, + RtpTransceiverDirection video_direction, + absl::optional* audio_index, + absl::optional* video_index, + absl::optional* data_index, + cricket::MediaSessionOptions* session_options) { + for (const cricket::ContentInfo& content : + session_desc->description()->contents()) { + if (IsAudioContent(&content)) { + // If we already have an audio m= section, reject this extra one. + if (*audio_index) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions( + cricket::MEDIA_TYPE_AUDIO, content.name, + RtpTransceiverDirection::kInactive, /*stopped=*/true)); + } else { + bool stopped = (audio_direction == RtpTransceiverDirection::kInactive); + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO, + content.name, audio_direction, + stopped)); + *audio_index = session_options->media_description_options.size() - 1; + } + } else if (IsVideoContent(&content)) { + // If we already have an video m= section, reject this extra one. + if (*video_index) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions( + cricket::MEDIA_TYPE_VIDEO, content.name, + RtpTransceiverDirection::kInactive, /*stopped=*/true)); + } else { + bool stopped = (video_direction == RtpTransceiverDirection::kInactive); + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO, + content.name, video_direction, + stopped)); + *video_index = session_options->media_description_options.size() - 1; + } + } + } +} + +void TgPeerConnection::RemoveSenders(cricket::MediaType media_type) { + UpdateLocalSenders(std::vector(), media_type); + UpdateRemoteSendersList(std::vector(), false, + media_type, nullptr); +} + +void TgPeerConnection::UpdateRemoteSendersList( + const cricket::StreamParamsVec& streams, + bool default_sender_needed, + cricket::MediaType media_type, + StreamCollection* new_streams) { + RTC_DCHECK(!IsUnifiedPlan()); + + std::vector* current_senders = + GetRemoteSenderInfos(media_type); + + // Find removed senders. I.e., senders where the sender id or ssrc don't match + // the new StreamParam. + for (auto sender_it = current_senders->begin(); + sender_it != current_senders->end(); + /* incremented manually */) { + const RtpSenderInfo& info = *sender_it; + const cricket::StreamParams* params = + cricket::GetStreamBySsrc(streams, info.first_ssrc); + std::string params_stream_id; + if (params) { + params_stream_id = + (!params->first_stream_id().empty() ? params->first_stream_id() + : kDefaultStreamId); + } + bool sender_exists = params && params->id == info.sender_id && + params_stream_id == info.stream_id; + // If this is a default track, and we still need it, don't remove it. + if ((info.stream_id == kDefaultStreamId && default_sender_needed) || + sender_exists) { + ++sender_it; + } else { + OnRemoteSenderRemoved(info, media_type); + sender_it = current_senders->erase(sender_it); + } + } + + // Find new and active senders. + for (const cricket::StreamParams& params : streams) { + if (!params.has_ssrcs()) { + // The remote endpoint has streams, but didn't signal ssrcs. For an active + // sender, this means it is coming from a Unified Plan endpoint,so we just + // create a default. + default_sender_needed = true; + break; + } + + // |params.id| is the sender id and the stream id uses the first of + // |params.stream_ids|. The remote description could come from a Unified + // Plan endpoint, with multiple or no stream_ids() signaled. Since this is + // not supported in Plan B, we just take the first here and create the + // default stream ID if none is specified. + const std::string& stream_id = + (!params.first_stream_id().empty() ? params.first_stream_id() + : kDefaultStreamId); + const std::string& sender_id = params.id; + uint32_t ssrc = params.first_ssrc(); + + rtc::scoped_refptr stream = + remote_streams_->find(stream_id); + if (!stream) { + // This is a new MediaStream. Create a new remote MediaStream. + stream = MediaStreamProxy::Create(rtc::Thread::Current(), + MediaStream::Create(stream_id)); + remote_streams_->AddStream(stream); + new_streams->AddStream(stream); + } + + const RtpSenderInfo* sender_info = + FindSenderInfo(*current_senders, stream_id, sender_id); + if (!sender_info) { + current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); + OnRemoteSenderAdded(current_senders->back(), media_type); + } + } + + // Add default sender if necessary. + if (default_sender_needed) { + rtc::scoped_refptr default_stream = + remote_streams_->find(kDefaultStreamId); + if (!default_stream) { + // Create the new default MediaStream. + default_stream = MediaStreamProxy::Create( + rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId)); + remote_streams_->AddStream(default_stream); + new_streams->AddStream(default_stream); + } + std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO) + ? kDefaultAudioSenderId + : kDefaultVideoSenderId; + const RtpSenderInfo* default_sender_info = + FindSenderInfo(*current_senders, kDefaultStreamId, default_sender_id); + if (!default_sender_info) { + current_senders->push_back( + RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0)); + OnRemoteSenderAdded(current_senders->back(), media_type); + } + } +} + +void TgPeerConnection::OnRemoteSenderAdded(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) { + RTC_LOG(LS_INFO) << "Creating " << cricket::MediaTypeToString(media_type) + << " receiver for track_id=" << sender_info.sender_id + << " and stream_id=" << sender_info.stream_id; + + MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + CreateAudioReceiver(stream, sender_info); + } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { + CreateVideoReceiver(stream, sender_info); + } else { + RTC_NOTREACHED() << "Invalid media type"; + } +} + +void TgPeerConnection::OnRemoteSenderRemoved(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) { + RTC_LOG(LS_INFO) << "Removing " << cricket::MediaTypeToString(media_type) + << " receiver for track_id=" << sender_info.sender_id + << " and stream_id=" << sender_info.stream_id; + + MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); + + rtc::scoped_refptr receiver; + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + // When the MediaEngine audio channel is destroyed, the RemoteAudioSource + // will be notified which will end the AudioRtpReceiver::track(). + receiver = RemoveAndStopReceiver(sender_info); + rtc::scoped_refptr audio_track = + stream->FindAudioTrack(sender_info.sender_id); + if (audio_track) { + stream->RemoveTrack(audio_track); + } + } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { + // Stopping or destroying a VideoRtpReceiver will end the + // VideoRtpReceiver::track(). + receiver = RemoveAndStopReceiver(sender_info); + rtc::scoped_refptr video_track = + stream->FindVideoTrack(sender_info.sender_id); + if (video_track) { + // There's no guarantee the track is still available, e.g. the track may + // have been removed from the stream by an application. + stream->RemoveTrack(video_track); + } + } else { + RTC_NOTREACHED() << "Invalid media type"; + } + if (receiver) { + Observer()->OnRemoveTrack(receiver); + } +} + +void TgPeerConnection::UpdateEndedRemoteMediaStreams() { + std::vector> streams_to_remove; + for (size_t i = 0; i < remote_streams_->count(); ++i) { + MediaStreamInterface* stream = remote_streams_->at(i); + if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) { + streams_to_remove.push_back(stream); + } + } + + for (auto& stream : streams_to_remove) { + remote_streams_->RemoveStream(stream); + Observer()->OnRemoveStream(std::move(stream)); + } +} + +void TgPeerConnection::UpdateLocalSenders( + const std::vector& streams, + cricket::MediaType media_type) { + std::vector* current_senders = GetLocalSenderInfos(media_type); + + // Find removed tracks. I.e., tracks where the track id, stream id or ssrc + // don't match the new StreamParam. + for (auto sender_it = current_senders->begin(); + sender_it != current_senders->end(); + /* incremented manually */) { + const RtpSenderInfo& info = *sender_it; + const cricket::StreamParams* params = + cricket::GetStreamBySsrc(streams, info.first_ssrc); + if (!params || params->id != info.sender_id || + params->first_stream_id() != info.stream_id) { + OnLocalSenderRemoved(info, media_type); + sender_it = current_senders->erase(sender_it); + } else { + ++sender_it; + } + } + + // Find new and active senders. + for (const cricket::StreamParams& params : streams) { + // The sync_label is the MediaStream label and the |stream.id| is the + // sender id. + const std::string& stream_id = params.first_stream_id(); + const std::string& sender_id = params.id; + uint32_t ssrc = params.first_ssrc(); + const RtpSenderInfo* sender_info = + FindSenderInfo(*current_senders, stream_id, sender_id); + if (!sender_info) { + current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); + OnLocalSenderAdded(current_senders->back(), media_type); + } + } +} + +void TgPeerConnection::OnLocalSenderAdded(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) { + RTC_DCHECK(!IsUnifiedPlan()); + auto sender = FindSenderById(sender_info.sender_id); + if (!sender) { + RTC_LOG(LS_WARNING) << "An unknown RtpSender with id " + << sender_info.sender_id + << " has been configured in the local description."; + return; + } + + if (sender->media_type() != media_type) { + RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" + " description with an unexpected media type."; + return; + } + + sender->internal()->set_stream_ids({sender_info.stream_id}); + sender->internal()->SetSsrc(sender_info.first_ssrc); +} + +void TgPeerConnection::OnLocalSenderRemoved(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) { + auto sender = FindSenderById(sender_info.sender_id); + if (!sender) { + // This is the normal case. I.e., RemoveStream has been called and the + // SessionDescriptions has been renegotiated. + return; + } + + // A sender has been removed from the SessionDescription but it's still + // associated with the TgPeerConnection. This only occurs if the SDP doesn't + // match with the calls to CreateSender, AddStream and RemoveStream. + if (sender->media_type() != media_type) { + RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" + " description with an unexpected media type."; + return; + } + + sender->internal()->SetSsrc(0); +} + +rtc::scoped_refptr> +TgPeerConnection::GetAudioTransceiver() const { + // This method only works with Plan B SDP, where there is a single + // audio/video transceiver. + RTC_DCHECK(!IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + return transceiver; + } + } + RTC_NOTREACHED(); + return nullptr; +} + +rtc::scoped_refptr> +TgPeerConnection::GetVideoTransceiver() const { + // This method only works with Plan B SDP, where there is a single + // audio/video transceiver. + RTC_DCHECK(!IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { + return transceiver; + } + } + RTC_NOTREACHED(); + return nullptr; +} + +// TODO(bugs.webrtc.org/7600): Remove this when multiple transceivers with +// individual transceiver directions are supported. +bool TgPeerConnection::HasRtpSender(cricket::MediaType type) const { + switch (type) { + case cricket::MEDIA_TYPE_AUDIO: + return !GetAudioTransceiver()->internal()->senders().empty(); + case cricket::MEDIA_TYPE_VIDEO: + return !GetVideoTransceiver()->internal()->senders().empty(); + case cricket::MEDIA_TYPE_DATA: + return false; + } + RTC_NOTREACHED(); + return false; +} + +rtc::scoped_refptr> +TgPeerConnection::FindSenderForTrack(MediaStreamTrackInterface* track) const { + for (const auto& transceiver : transceivers_) { + for (auto sender : transceiver->internal()->senders()) { + if (sender->track() == track) { + return sender; + } + } + } + return nullptr; +} + +rtc::scoped_refptr> +TgPeerConnection::FindSenderById(const std::string& sender_id) const { + for (const auto& transceiver : transceivers_) { + for (auto sender : transceiver->internal()->senders()) { + if (sender->id() == sender_id) { + return sender; + } + } + } + return nullptr; +} + +rtc::scoped_refptr> +TgPeerConnection::FindReceiverById(const std::string& receiver_id) const { + for (const auto& transceiver : transceivers_) { + for (auto receiver : transceiver->internal()->receivers()) { + if (receiver->id() == receiver_id) { + return receiver; + } + } + } + return nullptr; +} + +std::vector* +TgPeerConnection::GetRemoteSenderInfos(cricket::MediaType media_type) { + RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO); + return (media_type == cricket::MEDIA_TYPE_AUDIO) + ? &remote_audio_sender_infos_ + : &remote_video_sender_infos_; +} + +std::vector* TgPeerConnection::GetLocalSenderInfos( + cricket::MediaType media_type) { + RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO); + return (media_type == cricket::MEDIA_TYPE_AUDIO) ? &local_audio_sender_infos_ + : &local_video_sender_infos_; +} + +const TgPeerConnection::RtpSenderInfo* TgPeerConnection::FindSenderInfo( + const std::vector& infos, + const std::string& stream_id, + const std::string sender_id) const { + for (const RtpSenderInfo& sender_info : infos) { + if (sender_info.stream_id == stream_id && + sender_info.sender_id == sender_id) { + return &sender_info; + } + } + return nullptr; +} + +TgPeerConnection::InitializePortAllocatorResult +TgPeerConnection::InitializePortAllocator_n( + const cricket::ServerAddresses& stun_servers, + const std::vector& turn_servers, + const RTCConfiguration& configuration) { + RTC_DCHECK_RUN_ON(network_thread()); + + port_allocator_->Initialize(); + // To handle both internal and externally created port allocator, we will + // enable BUNDLE here. + int port_allocator_flags = port_allocator_->flags(); + port_allocator_flags |= cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_ENABLE_IPV6 | + cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI; + // If the disable-IPv6 flag was specified, we'll not override it + // by experiment. + if (configuration.disable_ipv6) { + port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); + } else if (webrtc::field_trial::FindFullName("WebRTC-IPv6Default") + .find("Disabled") == 0) { + port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); + } + + if (configuration.disable_ipv6_on_wifi) { + port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI); + RTC_LOG(LS_INFO) << "IPv6 candidates on Wi-Fi are disabled."; + } + + if (configuration.tcp_candidate_policy == kTcpCandidatePolicyDisabled) { + port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_TCP; + RTC_LOG(LS_INFO) << "TCP candidates are disabled."; + } + + if (configuration.candidate_network_policy == + kCandidateNetworkPolicyLowCost) { + port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS; + RTC_LOG(LS_INFO) << "Do not gather candidates on high-cost networks"; + } + + if (configuration.disable_link_local_networks) { + port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS; + RTC_LOG(LS_INFO) << "Disable candidates on link-local network interfaces."; + } + + port_allocator_->set_flags(port_allocator_flags); + // No step delay is used while allocating ports. + port_allocator_->set_step_delay(cricket::kMinimumStepDelay); + port_allocator_->SetCandidateFilter( + ConvertIceTransportTypeToCandidateFilter(configuration.type)); + port_allocator_->set_max_ipv6_networks(configuration.max_ipv6_networks); + + auto turn_servers_copy = turn_servers; + for (auto& turn_server : turn_servers_copy) { + turn_server.tls_cert_verifier = tls_cert_verifier_.get(); + } + // Call this last since it may create pooled allocator sessions using the + // properties set above. + port_allocator_->SetConfiguration( + stun_servers, std::move(turn_servers_copy), + configuration.ice_candidate_pool_size, + configuration.GetTurnPortPrunePolicy(), configuration.turn_customizer, + configuration.stun_candidate_keepalive_interval); + + InitializePortAllocatorResult res; + res.enable_ipv6 = port_allocator_flags & cricket::PORTALLOCATOR_ENABLE_IPV6; + return res; +} + +bool TgPeerConnection::ReconfigurePortAllocator_n( + const cricket::ServerAddresses& stun_servers, + const std::vector& turn_servers, + IceTransportsType type, + int candidate_pool_size, + PortPrunePolicy turn_port_prune_policy, + webrtc::TurnCustomizer* turn_customizer, + absl::optional stun_candidate_keepalive_interval, + bool have_local_description) { + port_allocator_->SetCandidateFilter( + ConvertIceTransportTypeToCandidateFilter(type)); + // According to JSEP, after setLocalDescription, changing the candidate pool + // size is not allowed, and changing the set of ICE servers will not result + // in new candidates being gathered. + if (have_local_description) { + port_allocator_->FreezeCandidatePool(); + } + // Add the custom tls turn servers if they exist. + auto turn_servers_copy = turn_servers; + for (auto& turn_server : turn_servers_copy) { + turn_server.tls_cert_verifier = tls_cert_verifier_.get(); + } + // Call this last since it may create pooled allocator sessions using the + // candidate filter set above. + return port_allocator_->SetConfiguration( + stun_servers, std::move(turn_servers_copy), candidate_pool_size, + turn_port_prune_policy, turn_customizer, + stun_candidate_keepalive_interval); +} + +cricket::ChannelManager* TgPeerConnection::channel_manager() const { + return factory_->channel_manager(); +} + +bool TgPeerConnection::StartRtcEventLog_w( + std::unique_ptr output, + int64_t output_period_ms) { + RTC_DCHECK_RUN_ON(worker_thread()); + if (!event_log_) { + return false; + } + return event_log_->StartLogging(std::move(output), output_period_ms); +} + +void TgPeerConnection::StopRtcEventLog_w() { + RTC_DCHECK_RUN_ON(worker_thread()); + if (event_log_) { + event_log_->StopLogging(); + } +} + +cricket::ChannelInterface* TgPeerConnection::GetChannel( + const std::string& content_name) { + for (const auto& transceiver : transceivers_) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel && channel->content_name() == content_name) { + return channel; + } + } + if (rtp_data_channel() && + rtp_data_channel()->content_name() == content_name) { + return rtp_data_channel(); + } + return nullptr; +} + +bool TgPeerConnection::GetSctpSslRole(rtc::SSLRole* role) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!local_description() || !remote_description()) { + RTC_LOG(LS_INFO) + << "Local and Remote descriptions must be applied to get the " + "SSL Role of the SCTP transport."; + return false; + } + + absl::optional dtls_role; + if (sctp_mid_) { + dtls_role = transport_controller_->GetDtlsRole(*sctp_mid_); + if (!dtls_role && is_caller_.has_value()) { + dtls_role = *is_caller_ ? rtc::SSL_SERVER : rtc::SSL_CLIENT; + } + *role = *dtls_role; + return true; + } + return false; +} + +bool TgPeerConnection::GetSslRole(const std::string& content_name, + rtc::SSLRole* role) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!local_description() || !remote_description()) { + RTC_LOG(LS_INFO) + << "Local and Remote descriptions must be applied to get the " + "SSL Role of the session."; + return false; + } + + auto dtls_role = transport_controller_->GetDtlsRole(content_name); + if (dtls_role) { + *role = *dtls_role; + return true; + } + return false; +} + +void TgPeerConnection::SetSessionError(SessionError error, + const std::string& error_desc) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (error != session_error_) { + session_error_ = error; + session_error_desc_ = error_desc; + } +} + +RTCError TgPeerConnection::UpdateSessionState( + SdpType type, + cricket::ContentSource source, + const cricket::SessionDescription* description) { + RTC_DCHECK_RUN_ON(signaling_thread()); + + // 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(session_error() == SessionError::kNone); + + // If this is answer-ish we're ready to let media flow. + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + EnableSending(); + } + + // Update the signaling state according to the specified state machine (see + // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum). + if (type == SdpType::kOffer) { + ChangeSignalingState(source == cricket::CS_LOCAL + ? PeerConnectionInterface::kHaveLocalOffer + : PeerConnectionInterface::kHaveRemoteOffer); + } else if (type == SdpType::kPrAnswer) { + ChangeSignalingState(source == cricket::CS_LOCAL + ? PeerConnectionInterface::kHaveLocalPrAnswer + : PeerConnectionInterface::kHaveRemotePrAnswer); + } else { + RTC_DCHECK(type == SdpType::kAnswer); + ChangeSignalingState(PeerConnectionInterface::kStable); + transceiver_stable_states_by_transceivers_.clear(); + } + + // Update internal objects according to the session description's media + // descriptions. + RTCError error = PushdownMediaDescription(type, source); + if (!error.ok()) { + return error; + } + + return RTCError::OK(); +} + +RTCError TgPeerConnection::PushdownMediaDescription( + SdpType type, + cricket::ContentSource source) { + const SessionDescriptionInterface* sdesc = + (source == cricket::CS_LOCAL ? local_description() + : remote_description()); + RTC_DCHECK(sdesc); + + // Push down the new SDP media section for each audio/video transceiver. + for (const auto& transceiver : transceivers_) { + const ContentInfo* content_info = + FindMediaSectionForTransceiver(transceiver, sdesc); + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (!channel || !content_info || content_info->rejected) { + continue; + } + const MediaContentDescription* content_desc = + content_info->media_description(); + if (!content_desc) { + continue; + } + std::string error; + bool success = (source == cricket::CS_LOCAL) + ? channel->SetLocalContent(content_desc, type, &error) + : channel->SetRemoteContent(content_desc, type, &error); + if (!success) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error); + } + } + + // 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_mid_ && local_description() && remote_description()) { + rtc::scoped_refptr sctp_transport = + transport_controller_->GetSctpTransport(*sctp_mid_); + auto local_sctp_description = cricket::GetFirstSctpDataContentDescription( + local_description()->description()); + auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription( + remote_description()->description()); + if (sctp_transport && local_sctp_description && remote_sctp_description) { + int max_message_size; + // A remote max message size of zero means "any size supported". + // We configure the connection with our own max message size. + if (remote_sctp_description->max_message_size() == 0) { + max_message_size = local_sctp_description->max_message_size(); + } else { + max_message_size = + std::min(local_sctp_description->max_message_size(), + remote_sctp_description->max_message_size()); + } + sctp_transport->Start(local_sctp_description->port(), + remote_sctp_description->port(), max_message_size); + } + } + + return RTCError::OK(); +} + +RTCError TgPeerConnection::PushdownTransportDescription( + cricket::ContentSource source, + SdpType type) { + RTC_DCHECK_RUN_ON(signaling_thread()); + + if (source == cricket::CS_LOCAL) { + const SessionDescriptionInterface* sdesc = local_description(); + RTC_DCHECK(sdesc); + return transport_controller_->SetLocalDescription(type, + sdesc->description()); + } else { + const SessionDescriptionInterface* sdesc = remote_description(); + RTC_DCHECK(sdesc); + return transport_controller_->SetRemoteDescription(type, + sdesc->description()); + } +} + +bool TgPeerConnection::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; +} + +cricket::IceConfig TgPeerConnection::ParseIceConfig( + const PeerConnectionInterface::RTCConfiguration& config) const { + cricket::ContinualGatheringPolicy gathering_policy; + 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 = RTCConfigurationToIceConfigOptionalInt( + 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 = + RTCConfigurationToIceConfigOptionalInt( + 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.surface_ice_candidates_on_ice_transport_type_changed = + config.surface_ice_candidates_on_ice_transport_type_changed; + ice_config.ice_check_interval_strong_connectivity = + config.ice_check_interval_strong_connectivity; + ice_config.ice_check_interval_weak_connectivity = + config.ice_check_interval_weak_connectivity; + ice_config.ice_check_min_interval = config.ice_check_min_interval; + ice_config.ice_unwritable_timeout = config.ice_unwritable_timeout; + ice_config.ice_unwritable_min_checks = config.ice_unwritable_min_checks; + ice_config.ice_inactive_timeout = config.ice_inactive_timeout; + ice_config.stun_keepalive_interval = config.stun_candidate_keepalive_interval; + ice_config.regather_all_networks_interval_range = + config.ice_regather_interval_range; + ice_config.network_preference = config.network_preference; + return ice_config; +} + +absl::optional TgPeerConnection::sctp_transport_name() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (sctp_mid_ && transport_controller_) { + auto dtls_transport = transport_controller_->GetDtlsTransport(*sctp_mid_); + if (dtls_transport) { + return dtls_transport->transport_name(); + } + return absl::optional(); + } + return absl::optional(); +} + +cricket::CandidateStatsList TgPeerConnection::GetPooledCandidateStats() const { + cricket::CandidateStatsList candidate_states_list; + network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&cricket::PortAllocator::GetCandidateStatsFromPooledSessions, + port_allocator_.get(), &candidate_states_list)); + return candidate_states_list; +} + +std::map TgPeerConnection::GetTransportNamesByMid() + const { + RTC_DCHECK_RUN_ON(signaling_thread()); + std::map transport_names_by_mid; + for (const auto& transceiver : transceivers_) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel) { + transport_names_by_mid[channel->content_name()] = + channel->transport_name(); + } + } + return transport_names_by_mid; +} + +std::map +TgPeerConnection::GetTransportStatsByNames( + const std::set& transport_names) { + if (!network_thread()->IsCurrent()) { + return network_thread() + ->Invoke>( + RTC_FROM_HERE, + [&] { return GetTransportStatsByNames(transport_names); }); + } + RTC_DCHECK_RUN_ON(network_thread()); + std::map transport_stats_by_name; + for (const std::string& transport_name : transport_names) { + cricket::TransportStats transport_stats; + bool success = + transport_controller_->GetStats(transport_name, &transport_stats); + if (success) { + transport_stats_by_name[transport_name] = std::move(transport_stats); + } else { + RTC_LOG(LS_ERROR) << "Failed to get transport stats for transport_name=" + << transport_name; + } + } + return transport_stats_by_name; +} + +bool TgPeerConnection::GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + if (!certificate) { + return false; + } + *certificate = transport_controller_->GetLocalCertificate(transport_name); + return *certificate != nullptr; +} + +std::unique_ptr TgPeerConnection::GetRemoteSSLCertChain( + const std::string& transport_name) { + return transport_controller_->GetRemoteSSLCertChain(transport_name); +} + +bool TgPeerConnection::IceRestartPending(const std::string& content_name) const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_ice_restarts_.find(content_name) != + pending_ice_restarts_.end(); +} + +bool TgPeerConnection::NeedsIceRestart(const std::string& content_name) const { + return transport_controller_->NeedsIceRestart(content_name); +} + +void TgPeerConnection::OnCertificateReady( + const rtc::scoped_refptr& certificate) { + transport_controller_->SetLocalCertificate(certificate); +} + +void TgPeerConnection::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) { + SetSessionError(SessionError::kTransport, + rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); +} + +void TgPeerConnection::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: + RTC_LOG(LS_INFO) << "Changing to ICE connected state because " + "all transports are writable."; + SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); + NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); + break; + case cricket::kIceConnectionCompleted: + RTC_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); + NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); + ReportTransportStats(); + break; + default: + RTC_NOTREACHED(); + } +} + +void TgPeerConnection::OnTransportControllerCandidatesGathered( + const std::string& transport_name, + const cricket::Candidates& candidates) { + int sdp_mline_index; + if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { + RTC_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 TgPeerConnection::OnTransportControllerCandidateError( + const cricket::IceCandidateErrorEvent& event) { + OnIceCandidateError(event.address, event.port, event.url, event.error_code, + event.error_text); +} + +void TgPeerConnection::OnTransportControllerCandidatesRemoved( + const std::vector& candidates) { + // Sanity check. + for (const cricket::Candidate& candidate : candidates) { + if (candidate.transport_name().empty()) { + RTC_LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " + "empty content name in candidate " + << candidate.ToString(); + return; + } + } + + if (local_description()) { + mutable_local_description()->RemoveCandidates(candidates); + } + OnIceCandidatesRemoved(candidates); +} + +void TgPeerConnection::OnTransportControllerCandidateChanged( + const cricket::CandidatePairChangeEvent& event) { + OnSelectedCandidatePairChanged(event); +} + +void TgPeerConnection::OnTransportControllerDtlsHandshakeError( + rtc::SSLHandshakeError error) { + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.TgPeerConnection.DtlsHandshakeError", static_cast(error), + static_cast(rtc::SSLHandshakeError::MAX_VALUE)); +} + +void TgPeerConnection::EnableSending() { + for (const auto& transceiver : transceivers_) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel && !channel->enabled()) { + channel->Enable(true); + } + } +} + +// Returns the media index for a local ice candidate given the content name. +bool TgPeerConnection::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 TgPeerConnection::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) { + RTC_LOG(LS_INFO) + << "UseCandidatesInSessionDescription: Not ready to use " + "candidate."; + } + continue; + } + ret = UseCandidate(candidate); + if (!ret) { + break; + } + } + } + return ret; +} + +bool TgPeerConnection::UseCandidate(const IceCandidateInterface* candidate) { + RTCErrorOr result = + FindContentInfo(remote_description(), candidate); + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "UseCandidate: Invalid candidate. " + << result.error().message(); + return false; + } + std::vector candidates; + candidates.push_back(candidate->candidate()); + // Invoking BaseSession method to handle remote candidates. + RTCError error = transport_controller_->AddRemoteCandidates( + result.value()->name, candidates); + if (error.ok()) { + ReportRemoteIceCandidateAdded(candidate->candidate()); + // 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 { + RTC_LOG(LS_WARNING) << error.message(); + } + return true; +} + +RTCErrorOr TgPeerConnection::FindContentInfo( + const SessionDescriptionInterface* description, + const IceCandidateInterface* candidate) { + if (candidate->sdp_mline_index() >= 0) { + size_t mediacontent_index = + static_cast(candidate->sdp_mline_index()); + size_t content_size = description->description()->contents().size(); + if (mediacontent_index < content_size) { + return &description->description()->contents()[mediacontent_index]; + } else { + return RTCError(RTCErrorType::INVALID_RANGE, + "Media line index (" + + rtc::ToString(candidate->sdp_mline_index()) + + ") out of range (number of mlines: " + + rtc::ToString(content_size) + ")."); + } + } else if (!candidate->sdp_mid().empty()) { + auto& contents = description->description()->contents(); + auto it = absl::c_find_if( + contents, [candidate](const cricket::ContentInfo& content_info) { + return content_info.mid() == candidate->sdp_mid(); + }); + if (it == contents.end()) { + return RTCError( + RTCErrorType::INVALID_PARAMETER, + "Mid " + candidate->sdp_mid() + + " specified but no media section with that mid found."); + } else { + return &*it; + } + } + + return RTCError(RTCErrorType::INVALID_PARAMETER, + "Neither sdp_mline_index nor sdp_mid specified."); +} + +void TgPeerConnection::RemoveUnusedChannels(const SessionDescription* desc) { + // 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) { + DestroyTransceiverChannel(GetVideoTransceiver()); + } + + const cricket::ContentInfo* audio_info = cricket::GetFirstAudioContent(desc); + if (!audio_info || audio_info->rejected) { + DestroyTransceiverChannel(GetAudioTransceiver()); + } +} + +RTCErrorOr TgPeerConnection::GetEarlyBundleGroup( + const SessionDescription& desc) const { + const cricket::ContentGroup* bundle_group = nullptr; + if (configuration_.bundle_policy == + PeerConnectionInterface::kBundlePolicyMaxBundle) { + bundle_group = desc.GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + if (!bundle_group) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max-bundle configured but session description " + "has no BUNDLE group"); + } + } + return bundle_group; +} + +RTCError TgPeerConnection::CreateChannels(const SessionDescription& desc) { + // Creating the media channels. Transports should already have been created + // at this point. + const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc); + if (voice && !voice->rejected && + !GetAudioTransceiver()->internal()->channel()) { + cricket::VoiceChannel* voice_channel = CreateVoiceChannel(voice->name); + if (!voice_channel) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Failed to create voice channel."); + } + GetAudioTransceiver()->internal()->SetChannel(voice_channel); + } + + const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc); + if (video && !video->rejected && + !GetVideoTransceiver()->internal()->channel()) { + cricket::VideoChannel* video_channel = CreateVideoChannel(video->name); + if (!video_channel) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Failed to create video channel."); + } + GetVideoTransceiver()->internal()->SetChannel(video_channel); + } + + return RTCError::OK(); +} + +// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. +cricket::VoiceChannel* TgPeerConnection::CreateVoiceChannel( + const std::string& mid) { + RtpTransportInternal* rtp_transport = GetRtpTransport(mid); + MediaTransportConfig media_transport_config = + transport_controller_->GetMediaTransportConfig(mid); + + cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel( + call_ptr_, configuration_.media_config, rtp_transport, + media_transport_config, signaling_thread(), mid, SrtpRequired(), + GetCryptoOptions(), &ssrc_generator_, audio_options_); + if (!voice_channel) { + return nullptr; + } + voice_channel->SignalDtlsSrtpSetupFailure.connect( + this, &TgPeerConnection::OnDtlsSrtpSetupFailure); + voice_channel->SignalSentPacket.connect(this, + &TgPeerConnection::OnSentPacket_w); + voice_channel->SetRtpTransport(rtp_transport); + + return voice_channel; +} + +// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. +cricket::VideoChannel* TgPeerConnection::CreateVideoChannel( + const std::string& mid) { + RtpTransportInternal* rtp_transport = GetRtpTransport(mid); + MediaTransportConfig media_transport_config = + transport_controller_->GetMediaTransportConfig(mid); + + cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel( + call_ptr_, configuration_.media_config, rtp_transport, + media_transport_config, signaling_thread(), mid, SrtpRequired(), + GetCryptoOptions(), &ssrc_generator_, video_options_, + video_bitrate_allocator_factory_.get()); + if (!video_channel) { + return nullptr; + } + video_channel->SignalDtlsSrtpSetupFailure.connect( + this, &TgPeerConnection::OnDtlsSrtpSetupFailure); + video_channel->SignalSentPacket.connect(this, + &TgPeerConnection::OnSentPacket_w); + video_channel->SetRtpTransport(rtp_transport); + + return video_channel; +} + +Call::Stats TgPeerConnection::GetCallStats() { + if (!worker_thread()->IsCurrent()) { + return worker_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::GetCallStats, this)); + } + RTC_DCHECK_RUN_ON(worker_thread()); + if (call_) { + return call_->GetStats(); + } else { + return Call::Stats(); + } +} + +// Returns false if bundle is enabled and rtcp_mux is disabled. +bool TgPeerConnection::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 == MediaProtocolType::kRtp) { + if (!HasRtcpMuxEnabled(content)) + return false; + } + } + // RTCP-MUX is enabled in all the contents. + return true; +} + +bool TgPeerConnection::HasRtcpMuxEnabled(const cricket::ContentInfo* content) { + return content->media_description()->rtcp_mux(); +} + +static RTCError ValidateMids(const cricket::SessionDescription& description) { + std::set mids; + for (const cricket::ContentInfo& content : description.contents()) { + if (content.name.empty()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "A media section is missing a MID attribute."); + } + if (!mids.insert(content.name).second) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Duplicate a=mid value '" + content.name + "'."); + } + } + return RTCError::OK(); +} + +RTCError TgPeerConnection::ValidateSessionDescription( + const SessionDescriptionInterface* sdesc, + cricket::ContentSource source) { + if (session_error() != SessionError::kNone) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); + } + + if (!sdesc || !sdesc->description()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); + } + + SdpType type = sdesc->GetType(); + if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) || + (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_STATE, + "Called in wrong state: " + GetSignalingStateString(signaling_state())); + } + + RTCError error = ValidateMids(*sdesc->description()); + if (!error.ok()) { + return error; + } + + // Verify crypto settings. + std::string crypto_error; + if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || + dtls_enabled_) { + RTCError crypto_error = VerifyCrypto(sdesc->description(), dtls_enabled_); + if (!crypto_error.ok()) { + return crypto_error; + } + } + + // Verify ice-ufrag and ice-pwd. + if (!VerifyIceUfragPwdPresent(sdesc->description())) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + kSdpWithoutIceUfragPwd); + } + + if (!ValidateBundleSettings(sdesc->description())) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + kBundleWithoutRtcpMux); + } + + // 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 (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + // With an answer we want to compare the new answer session description with + // the offer's session description from the current negotiation. + const cricket::SessionDescription* offer_desc = + (source == cricket::CS_LOCAL) ? remote_description()->description() + : local_description()->description(); + if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) || + !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(), + type)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + kMlineMismatchInAnswer); + } + } else { + // The re-offers should respect the order of m= sections in current + // description. See RFC3264 Section 8 paragraph 4 for more details. + // With a re-offer, either the current local or current remote descriptions + // could be the most up to date, so we would like to check against both of + // them if they exist. It could be the case that one of them has a 0 port + // for a media section, but the other does not. This is important to check + // against in the case that we are recycling an m= section. + const cricket::SessionDescription* current_desc = nullptr; + const cricket::SessionDescription* secondary_current_desc = nullptr; + if (local_description()) { + current_desc = local_description()->description(); + if (remote_description()) { + secondary_current_desc = remote_description()->description(); + } + } else if (remote_description()) { + current_desc = remote_description()->description(); + } + if (current_desc && + !MediaSectionsInSameOrder(*current_desc, secondary_current_desc, + *sdesc->description(), type)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + kMlineMismatchInSubsequentOffer); + } + } + + if (IsUnifiedPlan()) { + // Ensure that each audio and video media section has at most one + // "StreamParams". This will return an error if receiving a session + // description from a "Plan B" endpoint which adds multiple tracks of the + // same type. With Unified Plan, there can only be at most one track per + // media section. + for (const ContentInfo& content : sdesc->description()->contents()) { + const MediaContentDescription& desc = *content.media_description(); + if ((desc.type() == cricket::MEDIA_TYPE_AUDIO || + desc.type() == cricket::MEDIA_TYPE_VIDEO) && + desc.streams().size() > 1u) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Media section has more than one track specified " + "with a=ssrc lines which is not supported with " + "Unified Plan."); + } + } + } + + return RTCError::OK(); +} + +bool TgPeerConnection::ExpectSetLocalDescription(SdpType type) { + PeerConnectionInterface::SignalingState state = signaling_state(); + if (type == SdpType::kOffer) { + return (state == PeerConnectionInterface::kStable) || + (state == PeerConnectionInterface::kHaveLocalOffer); + } else { + RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); + return (state == PeerConnectionInterface::kHaveRemoteOffer) || + (state == PeerConnectionInterface::kHaveLocalPrAnswer); + } +} + +bool TgPeerConnection::ExpectSetRemoteDescription(SdpType type) { + PeerConnectionInterface::SignalingState state = signaling_state(); + if (type == SdpType::kOffer) { + return (state == PeerConnectionInterface::kStable) || + (state == PeerConnectionInterface::kHaveRemoteOffer); + } else { + RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); + return (state == PeerConnectionInterface::kHaveLocalOffer) || + (state == PeerConnectionInterface::kHaveRemotePrAnswer); + } +} + +const char* TgPeerConnection::SessionErrorToString(SessionError error) const { + switch (error) { + case SessionError::kNone: + return "ERROR_NONE"; + case SessionError::kContent: + return "ERROR_CONTENT"; + case SessionError::kTransport: + return "ERROR_TRANSPORT"; + } + RTC_NOTREACHED(); + return ""; +} + +std::string TgPeerConnection::GetSessionErrorMsg() { + rtc::StringBuilder desc; + desc << kSessionError << SessionErrorToString(session_error()) << ". "; + desc << kSessionErrorDesc << session_error_desc() << "."; + return desc.Release(); +} + +void TgPeerConnection::ReportSdpFormatReceived( + const SessionDescriptionInterface& remote_offer) { + int num_audio_mlines = 0; + int num_video_mlines = 0; + int num_audio_tracks = 0; + int num_video_tracks = 0; + for (const ContentInfo& content : remote_offer.description()->contents()) { + cricket::MediaType media_type = content.media_description()->type(); + int num_tracks = std::max( + 1, static_cast(content.media_description()->streams().size())); + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + num_audio_mlines += 1; + num_audio_tracks += num_tracks; + } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { + num_video_mlines += 1; + num_video_tracks += num_tracks; + } + } + SdpFormatReceived format = kSdpFormatReceivedNoTracks; + if (num_audio_mlines > 1 || num_video_mlines > 1) { + format = kSdpFormatReceivedComplexUnifiedPlan; + } else if (num_audio_tracks > 1 || num_video_tracks > 1) { + format = kSdpFormatReceivedComplexPlanB; + } else if (num_audio_tracks > 0 || num_video_tracks > 0) { + format = kSdpFormatReceivedSimple; + } + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpFormatReceived", format, + kSdpFormatReceivedMax); +} + +void TgPeerConnection::ReportIceCandidateCollected( + const cricket::Candidate& candidate) { + NoteUsageEvent(UsageEvent::CANDIDATE_COLLECTED); + if (candidate.address().IsPrivateIP()) { + NoteUsageEvent(UsageEvent::PRIVATE_CANDIDATE_COLLECTED); + } + if (candidate.address().IsUnresolvedIP()) { + NoteUsageEvent(UsageEvent::MDNS_CANDIDATE_COLLECTED); + } + if (candidate.address().family() == AF_INET6) { + NoteUsageEvent(UsageEvent::IPV6_CANDIDATE_COLLECTED); + } +} + +void TgPeerConnection::ReportRemoteIceCandidateAdded( + const cricket::Candidate& candidate) { + NoteUsageEvent(UsageEvent::REMOTE_CANDIDATE_ADDED); + if (candidate.address().IsPrivateIP()) { + NoteUsageEvent(UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED); + } + if (candidate.address().IsUnresolvedIP()) { + NoteUsageEvent(UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED); + } + if (candidate.address().family() == AF_INET6) { + NoteUsageEvent(UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED); + } +} + +void TgPeerConnection::NoteUsageEvent(UsageEvent event) { + RTC_DCHECK_RUN_ON(signaling_thread()); + usage_event_accumulator_ |= static_cast(event); +} + +void TgPeerConnection::ReportUsagePattern() const { + RTC_DLOG(LS_INFO) << "Usage signature is " << usage_event_accumulator_; + RTC_HISTOGRAM_ENUMERATION_SPARSE("WebRTC.TgPeerConnection.UsagePattern", + usage_event_accumulator_, + static_cast(UsageEvent::MAX_VALUE)); + const int bad_bits = + static_cast(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED) | + static_cast(UsageEvent::CANDIDATE_COLLECTED); + const int good_bits = + static_cast(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED) | + static_cast(UsageEvent::REMOTE_CANDIDATE_ADDED) | + static_cast(UsageEvent::ICE_STATE_CONNECTED); + if ((usage_event_accumulator_ & bad_bits) == bad_bits && + (usage_event_accumulator_ & good_bits) == 0) { + // If called after close(), we can't report, because observer may have + // been deallocated, and therefore pointer is null. Write to log instead. + if (observer_) { + Observer()->OnInterestingUsage(usage_event_accumulator_); + } else { + RTC_LOG(LS_INFO) << "Interesting usage signature " + << usage_event_accumulator_ + << " observed after observer shutdown"; + } + } +} + +void TgPeerConnection::ReportNegotiatedSdpSemantics( + const SessionDescriptionInterface& answer) { + SdpSemanticNegotiated semantics_negotiated; + switch (answer.description()->msid_signaling()) { + case 0: + semantics_negotiated = kSdpSemanticNegotiatedNone; + break; + case cricket::kMsidSignalingMediaSection: + semantics_negotiated = kSdpSemanticNegotiatedUnifiedPlan; + break; + case cricket::kMsidSignalingSsrcAttribute: + semantics_negotiated = kSdpSemanticNegotiatedPlanB; + break; + case cricket::kMsidSignalingMediaSection | + cricket::kMsidSignalingSsrcAttribute: + semantics_negotiated = kSdpSemanticNegotiatedMixed; + break; + default: + RTC_NOTREACHED(); + } + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpSemanticNegotiated", + semantics_negotiated, kSdpSemanticNegotiatedMax); +} + +// 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 TgPeerConnection::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; + } + + RTCErrorOr result = + FindContentInfo(current_remote_desc, candidate); + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. " + << result.error().message(); + + *valid = false; + return false; + } + + std::string transport_name = GetTransportName(result.value()->name); + return !transport_name.empty(); +} + +bool TgPeerConnection::SrtpRequired() const { + return !use_datagram_transport_ && + (dtls_enabled_ || + webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED); +} + +void TgPeerConnection::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 TgPeerConnection::ReportTransportStats() { + std::map> + media_types_by_transport_name; + for (const auto& transceiver : transceivers_) { + if (transceiver->internal()->channel()) { + const std::string& transport_name = + transceiver->internal()->channel()->transport_name(); + media_types_by_transport_name[transport_name].insert( + transceiver->media_type()); + } + } + if (rtp_data_channel()) { + media_types_by_transport_name[rtp_data_channel()->transport_name()].insert( + cricket::MEDIA_TYPE_DATA); + } + + absl::optional transport_name = sctp_transport_name(); + if (transport_name) { + media_types_by_transport_name[*transport_name].insert( + cricket::MEDIA_TYPE_DATA); + } + + for (const auto& entry : media_types_by_transport_name) { + const std::string& transport_name = entry.first; + const std::set media_types = entry.second; + cricket::TransportStats stats; + if (transport_controller_->GetStats(transport_name, &stats)) { + ReportBestConnectionState(stats); + ReportNegotiatedCiphers(stats, media_types); + } + } +} +// Walk through the ConnectionInfos to gather best connection usage +// for IPv4 and IPv6. +void TgPeerConnection::ReportBestConnectionState( + const cricket::TransportStats& stats) { + for (const cricket::TransportChannelStats& channel_stats : + stats.channel_stats) { + for (const cricket::ConnectionInfo& connection_info : + channel_stats.ice_transport_stats.connection_infos) { + if (!connection_info.best_connection) { + continue; + } + + const cricket::Candidate& local = connection_info.local_candidate; + const cricket::Candidate& remote = connection_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)) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_TCP", + GetIceCandidatePairCounter(local, remote), + kIceCandidatePairMax); + } else if (local.protocol() == cricket::UDP_PROTOCOL_NAME) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_UDP", + GetIceCandidatePairCounter(local, remote), + kIceCandidatePairMax); + } else { + RTC_CHECK(0); + } + + // Increment the counter for IP type. + if (local.address().family() == AF_INET) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", + kBestConnections_IPv4, + kPeerConnectionAddressFamilyCounter_Max); + } else if (local.address().family() == AF_INET6) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", + kBestConnections_IPv6, + kPeerConnectionAddressFamilyCounter_Max); + } else { + RTC_CHECK(!local.address().hostname().empty() && + local.address().IsUnresolvedIP()); + } + + return; + } + } +} + +void TgPeerConnection::ReportNegotiatedCiphers( + const cricket::TransportStats& stats, + const std::set& media_types) { + 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; + } + + if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE) { + for (cricket::MediaType media_type : media_types) { + switch (media_type) { + case cricket::MEDIA_TYPE_AUDIO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SrtpCryptoSuite.Audio", srtp_crypto_suite, + rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_VIDEO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SrtpCryptoSuite.Video", srtp_crypto_suite, + rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_DATA: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SrtpCryptoSuite.Data", srtp_crypto_suite, + rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); + break; + default: + RTC_NOTREACHED(); + continue; + } + } + } + + if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL) { + for (cricket::MediaType media_type : media_types) { + switch (media_type) { + case cricket::MEDIA_TYPE_AUDIO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SslCipherSuite.Audio", ssl_cipher_suite, + rtc::SSL_CIPHER_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_VIDEO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SslCipherSuite.Video", ssl_cipher_suite, + rtc::SSL_CIPHER_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_DATA: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SslCipherSuite.Data", ssl_cipher_suite, + rtc::SSL_CIPHER_SUITE_MAX_VALUE); + break; + default: + RTC_NOTREACHED(); + continue; + } + } + } +} + +void TgPeerConnection::OnSentPacket_w(const rtc::SentPacket& sent_packet) { + RTC_DCHECK_RUN_ON(worker_thread()); + RTC_DCHECK(call_); + call_->OnSentPacket(sent_packet); +} + +const std::string TgPeerConnection::GetTransportName( + const std::string& content_name) { + cricket::ChannelInterface* channel = GetChannel(content_name); + if (channel) { + return channel->transport_name(); + } + // Return an empty string if failed to retrieve the transport name. + return ""; +} + +void TgPeerConnection::DestroyTransceiverChannel( + rtc::scoped_refptr> + transceiver) { + RTC_DCHECK(transceiver); + + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel) { + transceiver->internal()->SetChannel(nullptr); + DestroyChannelInterface(channel); + } +} + +void TgPeerConnection::DestroyChannelInterface( + cricket::ChannelInterface* channel) { + RTC_DCHECK(channel); + switch (channel->media_type()) { + case cricket::MEDIA_TYPE_AUDIO: + channel_manager()->DestroyVoiceChannel( + static_cast(channel)); + break; + case cricket::MEDIA_TYPE_VIDEO: + channel_manager()->DestroyVideoChannel( + static_cast(channel)); + break; + case cricket::MEDIA_TYPE_DATA: + channel_manager()->DestroyRtpDataChannel( + static_cast(channel)); + break; + default: + RTC_NOTREACHED() << "Unknown media type: " << channel->media_type(); + break; + } +} + +bool TgPeerConnection::OnTransportChanged( + const std::string& mid, + RtpTransportInternal* rtp_transport, + rtc::scoped_refptr dtls_transport, + DataChannelTransportInterface* data_channel_transport) { + RTC_DCHECK_RUN_ON(network_thread()); + bool ret = true; + auto base_channel = GetChannel(mid); + if (base_channel) { + ret = base_channel->SetRtpTransport(rtp_transport); + } + return ret; +} + +void TgPeerConnection::OnSetStreams() { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (IsUnifiedPlan()) + UpdateNegotiationNeeded(); +} + +PeerConnectionObserver* TgPeerConnection::Observer() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(observer_); + return observer_; +} + +CryptoOptions TgPeerConnection::GetCryptoOptions() { + // TODO(bugs.webrtc.org/9891) - Remove PeerConnectionFactory::CryptoOptions + // after it has been removed. + return configuration_.crypto_options.has_value() + ? *configuration_.crypto_options + : factory_->options().crypto_options; +} + +void TgPeerConnection::ClearStatsCache() { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (stats_collector_) { + stats_collector_->ClearCachedStatsReport(); + } +} + +void TgPeerConnection::RequestUsagePatternReportForTesting() { + signaling_thread()->Post(RTC_FROM_HERE, this, MSG_REPORT_USAGE_PATTERN, + nullptr); +} + +void TgPeerConnection::UpdateNegotiationNeeded() { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!IsUnifiedPlan()) { + Observer()->OnRenegotiationNeeded(); + return; + } + + // If connection's [[IsClosed]] slot is true, abort these steps. + if (IsClosed()) + return; + + // If connection's signaling state is not "stable", abort these steps. + if (signaling_state() != kStable) + return; + + // NOTE + // The negotiation-needed flag will be updated once the state transitions to + // "stable", as part of the steps for setting an RTCSessionDescription. + + // If the result of checking if negotiation is needed is false, clear the + // negotiation-needed flag by setting connection's [[NegotiationNeeded]] slot + // to false, and abort these steps. + bool is_negotiation_needed = CheckIfNegotiationIsNeeded(); + if (!is_negotiation_needed) { + is_negotiation_needed_ = false; + return; + } + + // If connection's [[NegotiationNeeded]] slot is already true, abort these + // steps. + if (is_negotiation_needed_) + return; + + // Set connection's [[NegotiationNeeded]] slot to true. + is_negotiation_needed_ = true; + + // Queue a task that runs the following steps: + // If connection's [[IsClosed]] slot is true, abort these steps. + // If connection's [[NegotiationNeeded]] slot is false, abort these steps. + // Fire an event named negotiationneeded at connection. + Observer()->OnRenegotiationNeeded(); +} + +bool TgPeerConnection::CheckIfNegotiationIsNeeded() { + RTC_DCHECK_RUN_ON(signaling_thread()); + // 1. If any implementation-specific negotiation is required, as described at + // the start of this section, return true. + + // 2. If connection's [[RestartIce]] internal slot is true, return true. + if (local_ice_credentials_to_replace_->HasIceCredentials()) { + return true; + } + + // 3. Let description be connection.[[CurrentLocalDescription]]. + const SessionDescriptionInterface* description = current_local_description(); + if (!description) + return true; + + // 5. For each transceiver in connection's set of transceivers, perform the + // following checks: + for (const auto& transceiver : transceivers_) { + const ContentInfo* current_local_msection = + FindTransceiverMSection(transceiver.get(), description); + + const ContentInfo* current_remote_msection = FindTransceiverMSection( + transceiver.get(), current_remote_description()); + + // 5.3 If transceiver is stopped and is associated with an m= section, + // but the associated m= section is not yet rejected in + // connection.[[CurrentLocalDescription]] or + // connection.[[CurrentRemoteDescription]], return true. + if (transceiver->stopped()) { + if (current_local_msection && !current_local_msection->rejected && + ((current_remote_msection && !current_remote_msection->rejected) || + !current_remote_msection)) { + return true; + } + continue; + } + + // 5.1 If transceiver isn't stopped and isn't yet associated with an m= + // section in description, return true. + if (!current_local_msection) + return true; + + const MediaContentDescription* current_local_media_description = + current_local_msection->media_description(); + // 5.2 If transceiver isn't stopped and is associated with an m= section + // in description then perform the following checks: + + // 5.2.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the + // associated m= section in description either doesn't contain a single + // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this + // m= section, or the MSID values themselves, differ from what is in + // transceiver.sender.[[AssociatedMediaStreamIds]], return true. + if (RtpTransceiverDirectionHasSend(transceiver->direction())) { + if (current_local_media_description->streams().size() == 0) + return true; + + std::vector msection_msids; + for (const auto& stream : current_local_media_description->streams()) { + for (const std::string& msid : stream.stream_ids()) + msection_msids.push_back(msid); + } + + std::vector transceiver_msids = + transceiver->sender()->stream_ids(); + if (msection_msids.size() != transceiver_msids.size()) + return true; + + absl::c_sort(transceiver_msids); + absl::c_sort(msection_msids); + if (transceiver_msids != msection_msids) + return true; + } + + // 5.2.2 If description is of type "offer", and the direction of the + // associated m= section in neither connection.[[CurrentLocalDescription]] + // nor connection.[[CurrentRemoteDescription]] matches + // transceiver.[[Direction]], return true. + if (description->GetType() == SdpType::kOffer) { + if (!current_remote_description()) + return true; + + if (!current_remote_msection) + return true; + + RtpTransceiverDirection current_local_direction = + current_local_media_description->direction(); + RtpTransceiverDirection current_remote_direction = + current_remote_msection->media_description()->direction(); + if (transceiver->direction() != current_local_direction && + transceiver->direction() != + RtpTransceiverDirectionReversed(current_remote_direction)) { + return true; + } + } + + // 5.2.3 If description is of type "answer", and the direction of the + // associated m= section in the description does not match + // transceiver.[[Direction]] intersected with the offered direction (as + // described in [JSEP] (section 5.3.1.)), return true. + if (description->GetType() == SdpType::kAnswer) { + if (!remote_description()) + return true; + + const ContentInfo* offered_remote_msection = + FindTransceiverMSection(transceiver.get(), remote_description()); + + RtpTransceiverDirection offered_direction = + offered_remote_msection + ? offered_remote_msection->media_description()->direction() + : RtpTransceiverDirection::kInactive; + + if (current_local_media_description->direction() != + (RtpTransceiverDirectionIntersection( + transceiver->direction(), + RtpTransceiverDirectionReversed(offered_direction)))) { + return true; + } + } + } + + // If all the preceding checks were performed and true was not returned, + // nothing remains to be negotiated; return false. + return false; +} + +RTCError TgPeerConnection::Rollback(SdpType sdp_type) { + auto state = signaling_state(); + if (state != PeerConnectionInterface::kHaveLocalOffer && + state != PeerConnectionInterface::kHaveRemoteOffer) { + return RTCError(RTCErrorType::INVALID_STATE, + "Called in wrong signalingState: " + + GetSignalingStateString(signaling_state())); + } + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(IsUnifiedPlan()); + std::vector mids; + std::vector> all_added_streams; + std::vector> all_removed_streams; + std::vector> removed_receivers; + + for (auto&& transceivers_stable_state_pair : + transceiver_stable_states_by_transceivers_) { + auto transceiver = transceivers_stable_state_pair.first; + auto state = transceivers_stable_state_pair.second; + + if (state.remote_stream_ids()) { + std::vector> added_streams; + std::vector> removed_streams; + SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), + state.remote_stream_ids().value(), + &added_streams, &removed_streams); + all_added_streams.insert(all_added_streams.end(), added_streams.begin(), + added_streams.end()); + all_removed_streams.insert(all_removed_streams.end(), + removed_streams.begin(), + removed_streams.end()); + if (!state.has_m_section() && !state.newly_created()) { + continue; + } + } + + RTC_DCHECK(transceiver->internal()->mid().has_value()); + std::string mid = transceiver->internal()->mid().value(); + mids.push_back(mid); + DestroyTransceiverChannel(transceiver); + + if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer && + transceiver->receiver()) { + removed_receivers.push_back(transceiver->receiver()); + } + if (state.newly_created()) { + if (transceiver->internal()->reused_for_addtrack()) { + transceiver->internal()->set_created_by_addtrack(true); + } else { + int remaining_transceiver_count = 0; + for (auto&& t : transceivers_) { + if (t != transceiver) { + transceivers_[remaining_transceiver_count++] = t; + } + } + transceivers_.resize(remaining_transceiver_count); + } + } + transceiver->internal()->sender_internal()->set_transport(nullptr); + transceiver->internal()->receiver_internal()->set_transport(nullptr); + transceiver->internal()->set_mid(state.mid()); + transceiver->internal()->set_mline_index(state.mline_index()); + } + transport_controller_->RollbackTransportForMids(mids); + transceiver_stable_states_by_transceivers_.clear(); + pending_local_description_.reset(); + pending_remote_description_.reset(); + ChangeSignalingState(PeerConnectionInterface::kStable); + + // Once all processing has finished, fire off callbacks. + for (const auto& receiver : removed_receivers) { + Observer()->OnRemoveTrack(receiver); + } + for (const auto& stream : all_added_streams) { + Observer()->OnAddStream(stream); + } + for (const auto& stream : all_removed_streams) { + Observer()->OnRemoveStream(stream); + } + + // The assumption is that in case of implicit rollback UpdateNegotiationNeeded + // gets called in SetRemoteDescription. + if (sdp_type == SdpType::kRollback) { + UpdateNegotiationNeeded(); + if (is_negotiation_needed_) { + Observer()->OnRenegotiationNeeded(); + } + } + return RTCError::OK(); +} + +rtc::scoped_refptr TgPeerConnection::CreateDataChannel(const std::string& label, const DataChannelInit* config) { + return nullptr; +} + +RTCError TgPeerConnection::UpdateDataChannel(cricket::ContentSource source, const cricket::ContentInfo& content, const cricket::ContentGroup* bundle_group) { + return RTCError::OK(); +} + +} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h new file mode 100644 index 0000000000..0b4b0a1c3d --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h @@ -0,0 +1,1377 @@ +/* + * 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 TG_PC_PEER_CONNECTION_H_ +#define TG_PC_PEER_CONNECTION_H_ + +#include +#include +#include +#include +#include +#include + +#include "api/peer_connection_interface.h" +#include "api/transport/data_channel_transport_interface.h" +#include "api/turn_customizer.h" +#include "pc/data_channel_controller.h" +#include "pc/ice_server_parsing.h" +#include "pc/peer_connection_factory.h" +#include "pc/peer_connection_internal.h" +#include "pc/rtc_stats_collector.h" +#include "pc/rtp_sender.h" +#include "pc/rtp_transceiver.h" +#include "pc/sctp_transport.h" +#include "pc/stats_collector.h" +#include "pc/stream_collection.h" +#include "pc/webrtc_session_description_factory.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/operations_chain.h" +#include "rtc_base/race_checker.h" +#include "rtc_base/unique_id_generator.h" +#include "rtc_base/weak_ptr.h" + +#include "tg_jsep_transport_controller.h" +#include "tg_peer_connection_factory.h" + +namespace webrtc { + +class MediaStreamObserver; +class VideoRtpReceiver; +class RtcEventLog; + +// TgPeerConnection is the implementation of the TgPeerConnection 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 TgPeerConnection : public PeerConnectionInternal, + public TgJsepTransportController::Observer, + public RtpSenderBase::SetStreamsObserver, + public rtc::MessageHandler, + public sigslot::has_slots<> { + public: + // A bit in the usage pattern is registered when its defining event occurs at + // least once. + enum class UsageEvent : int { + TURN_SERVER_ADDED = 0x01, + STUN_SERVER_ADDED = 0x02, + DATA_ADDED = 0x04, + AUDIO_ADDED = 0x08, + VIDEO_ADDED = 0x10, + // |SetLocalDescription| returns successfully. + SET_LOCAL_DESCRIPTION_SUCCEEDED = 0x20, + // |SetRemoteDescription| returns successfully. + SET_REMOTE_DESCRIPTION_SUCCEEDED = 0x40, + // A local candidate (with type host, server-reflexive, or relay) is + // collected. + CANDIDATE_COLLECTED = 0x80, + // A remote candidate is successfully added via |AddIceCandidate|. + ADD_ICE_CANDIDATE_SUCCEEDED = 0x100, + ICE_STATE_CONNECTED = 0x200, + CLOSE_CALLED = 0x400, + // A local candidate with private IP is collected. + PRIVATE_CANDIDATE_COLLECTED = 0x800, + // A remote candidate with private IP is added, either via AddiceCandidate + // or from the remote description. + REMOTE_PRIVATE_CANDIDATE_ADDED = 0x1000, + // A local mDNS candidate is collected. + MDNS_CANDIDATE_COLLECTED = 0x2000, + // A remote mDNS candidate is added, either via AddIceCandidate or from the + // remote description. + REMOTE_MDNS_CANDIDATE_ADDED = 0x4000, + // A local candidate with IPv6 address is collected. + IPV6_CANDIDATE_COLLECTED = 0x8000, + // A remote candidate with IPv6 address is added, either via AddIceCandidate + // or from the remote description. + REMOTE_IPV6_CANDIDATE_ADDED = 0x10000, + // A remote candidate (with type host, server-reflexive, or relay) is + // successfully added, either via AddIceCandidate or from the remote + // description. + REMOTE_CANDIDATE_ADDED = 0x20000, + // An explicit host-host candidate pair is selected, i.e. both the local and + // the remote candidates have the host type. This does not include candidate + // pairs formed with equivalent prflx remote candidates, e.g. a host-prflx + // pair where the prflx candidate has the same base as a host candidate of + // the remote peer. + DIRECT_CONNECTION_SELECTED = 0x40000, + MAX_VALUE = 0x80000, + }; + + explicit TgPeerConnection(TgPeerConnectionFactory* factory, + std::unique_ptr event_log, + std::unique_ptr call); + + bool Initialize( + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies); + + rtc::scoped_refptr local_streams() override; + rtc::scoped_refptr remote_streams() override; + bool AddStream(MediaStreamInterface* local_stream) override; + void RemoveStream(MediaStreamInterface* local_stream) override; + + RTCErrorOr> AddTrack( + rtc::scoped_refptr track, + const std::vector& stream_ids) override; + bool RemoveTrack(RtpSenderInterface* sender) override; + RTCError RemoveTrackNew( + rtc::scoped_refptr sender) override; + + RTCErrorOr> AddTransceiver( + rtc::scoped_refptr track) override; + RTCErrorOr> AddTransceiver( + rtc::scoped_refptr track, + const RtpTransceiverInit& init) override; + RTCErrorOr> AddTransceiver( + cricket::MediaType media_type) override; + RTCErrorOr> AddTransceiver( + cricket::MediaType media_type, + const RtpTransceiverInit& init) override; + + // 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 + // transitioning to kIceConnectionCompleted. + // Note that this will be removed once we implement RTCDtlsTransport which + // has standardized method for getting this information. + // See https://www.w3.org/TR/webrtc/#rtcdtlstransport-interface + std::unique_ptr GetRemoteAudioSSLCertificate(); + + // Version of the above method that returns the full certificate chain. + std::unique_ptr GetRemoteAudioSSLCertChain(); + + rtc::scoped_refptr CreateSender( + const std::string& kind, + const std::string& stream_id) override; + + std::vector> GetSenders() + const override; + std::vector> GetReceivers() + const override; + std::vector> GetTransceivers() + const override; + + rtc::scoped_refptr CreateDataChannel( + const std::string& label, + const DataChannelInit* config) override; + // WARNING: LEGACY. See peerconnectioninterface.h + bool GetStats(StatsObserver* observer, + webrtc::MediaStreamTrackInterface* track, + StatsOutputLevel level) override; + // Spec-complaint GetStats(). See peerconnectioninterface.h + void GetStats(RTCStatsCollectorCallback* callback) override; + void GetStats( + rtc::scoped_refptr selector, + rtc::scoped_refptr callback) override; + void GetStats( + rtc::scoped_refptr selector, + rtc::scoped_refptr callback) override; + void ClearStatsCache() override; + + SignalingState signaling_state() override; + + IceConnectionState ice_connection_state() override; + IceConnectionState standardized_ice_connection_state() override; + PeerConnectionState peer_connection_state() override; + IceGatheringState ice_gathering_state() override; + + const SessionDescriptionInterface* local_description() const override; + const SessionDescriptionInterface* remote_description() const override; + const SessionDescriptionInterface* current_local_description() const override; + const SessionDescriptionInterface* current_remote_description() + const override; + const SessionDescriptionInterface* pending_local_description() const override; + const SessionDescriptionInterface* pending_remote_description() + const override; + + void RestartIce() override; + + // JSEP01 + void CreateOffer(CreateSessionDescriptionObserver* observer, + const RTCOfferAnswerOptions& options) override; + void CreateAnswer(CreateSessionDescriptionObserver* observer, + const RTCOfferAnswerOptions& options) override; + void SetLocalDescription(SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc) override; + void SetLocalDescription(SetSessionDescriptionObserver* observer) override; + void SetRemoteDescription(SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc) override; + void SetRemoteDescription( + std::unique_ptr desc, + rtc::scoped_refptr observer) + override; + PeerConnectionInterface::RTCConfiguration GetConfiguration() override; + RTCError SetConfiguration( + const PeerConnectionInterface::RTCConfiguration& configuration) override; + bool AddIceCandidate(const IceCandidateInterface* candidate) override; + void AddIceCandidate(std::unique_ptr candidate, + std::function callback) override; + bool RemoveIceCandidates( + const std::vector& candidates) override; + + RTCError SetBitrate(const BitrateSettings& bitrate) override; + + void SetAudioPlayout(bool playout) override; + void SetAudioRecording(bool recording) override; + + rtc::scoped_refptr LookupDtlsTransportByMid( + const std::string& mid) override; + rtc::scoped_refptr LookupDtlsTransportByMidInternal( + const std::string& mid); + + rtc::scoped_refptr GetSctpTransport() const override; + + bool StartRtcEventLog(std::unique_ptr output, + int64_t output_period_ms) override; + bool StartRtcEventLog(std::unique_ptr output) override; + void StopRtcEventLog() override; + + void Close() override; + + // PeerConnectionInternal implementation. + rtc::Thread* network_thread() const final { + return factory_->network_thread(); + } + rtc::Thread* worker_thread() const final { return factory_->worker_thread(); } + rtc::Thread* signaling_thread() const final { + return factory_->signaling_thread(); + } + + std::string session_id() const override { + RTC_DCHECK_RUN_ON(signaling_thread()); + return session_id_; + } + + bool initial_offerer() const override { + RTC_DCHECK_RUN_ON(signaling_thread()); + return transport_controller_ && transport_controller_->initial_offerer(); + } + + std::vector< + rtc::scoped_refptr>> + GetTransceiversInternal() const override { + RTC_DCHECK_RUN_ON(signaling_thread()); + return transceivers_; + } + + sigslot::signal1& SignalDataChannelCreated() override { + RTC_DCHECK_RUN_ON(signaling_thread()); + return SignalDataChannelCreated_; + } + + cricket::RtpDataChannel* rtp_data_channel() const override { + return nullptr; + } + + std::vector> sctp_data_channels() + const override { + RTC_DCHECK_RUN_ON(signaling_thread()); + return std::vector>(); + } + + absl::optional sctp_content_name() const override { + RTC_DCHECK_RUN_ON(signaling_thread()); + return sctp_mid_; + } + + absl::optional sctp_transport_name() const override; + + cricket::CandidateStatsList GetPooledCandidateStats() const override; + std::map GetTransportNamesByMid() const override; + std::map GetTransportStatsByNames( + const std::set& transport_names) override; + Call::Stats GetCallStats() override; + + bool GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate) override; + std::unique_ptr GetRemoteSSLCertChain( + const std::string& transport_name) override; + bool IceRestartPending(const std::string& content_name) const override; + bool NeedsIceRestart(const std::string& content_name) const override; + bool GetSslRole(const std::string& content_name, rtc::SSLRole* role) override; + + // Functions needed by DataChannelController + void NoteDataAddedEvent() { NoteUsageEvent(UsageEvent::DATA_ADDED); } + // Returns the observer. Will crash on CHECK if the observer is removed. + PeerConnectionObserver* Observer() const; + bool IsClosed() const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return signaling_state_ == PeerConnectionInterface::kClosed; + } + // Get current SSL role used by SCTP's underlying transport. + bool GetSctpSslRole(rtc::SSLRole* role); + // Handler for the "channel closed" signal + void OnSctpDataChannelClosed(DataChannel* channel); + + // Functions made public for testing. + void ReturnHistogramVeryQuicklyForTesting() { + RTC_DCHECK_RUN_ON(signaling_thread()); + return_histogram_very_quickly_ = true; + } + void RequestUsagePatternReportForTesting(); + + protected: + ~TgPeerConnection() override; + + private: + class ImplicitCreateSessionDescriptionObserver; + friend class ImplicitCreateSessionDescriptionObserver; + class SetRemoteDescriptionObserverAdapter; + friend class SetRemoteDescriptionObserverAdapter; + + sigslot::signal1 SignalDataChannelCreated_ RTC_GUARDED_BY(signaling_thread()); + + // Represents the [[LocalIceCredentialsToReplace]] internal slot in the spec. + // It makes the next CreateOffer() produce new ICE credentials even if + // RTCOfferAnswerOptions::ice_restart is false. + // https://w3c.github.io/webrtc-pc/#dfn-localufragstoreplace + // TODO(hbos): When TgJsepTransportController/TgJsepTransport supports rollback, + // move this type of logic to TgJsepTransportController/TgJsepTransport. + class LocalIceCredentialsToReplace; + + struct RtpSenderInfo { + RtpSenderInfo() : first_ssrc(0) {} + RtpSenderInfo(const std::string& stream_id, + const std::string sender_id, + uint32_t ssrc) + : stream_id(stream_id), sender_id(sender_id), first_ssrc(ssrc) {} + bool operator==(const RtpSenderInfo& other) { + return this->stream_id == other.stream_id && + this->sender_id == other.sender_id && + this->first_ssrc == other.first_ssrc; + } + std::string stream_id; + std::string sender_id; + // An RtpSender can have many SSRCs. The first one is used as a sort of ID + // for communicating with the lower layers. + uint32_t first_ssrc; + }; + + // Field-trial based configuration for datagram transport. + struct DatagramTransportConfig { + explicit DatagramTransportConfig(const std::string& field_trial) + : enabled("enabled", true), default_value("default_value", false) { + ParseFieldTrial({&enabled, &default_value}, field_trial); + } + + // Whether datagram transport support is enabled at all. Defaults to true, + // allowing datagram transport to be used if (a) the application provides a + // factory for it and (b) the configuration specifies its use. This flag + // provides a kill-switch to force-disable datagram transport across all + // applications, without code changes. + FieldTrialFlag enabled; + + // Whether the datagram transport is enabled or disabled by default. + // Defaults to false, meaning that applications must configure use of + // datagram transport through RTCConfiguration. If set to true, + // applications will use the datagram transport by default (but may still + // explicitly configure themselves not to use it through RTCConfiguration). + FieldTrialFlag default_value; + }; + + // Field-trial based configuration for datagram transport data channels. + struct DatagramTransportDataChannelConfig { + explicit DatagramTransportDataChannelConfig(const std::string& field_trial) + : enabled("enabled", true), + default_value("default_value", false), + receive_only("receive_only", false) { + ParseFieldTrial({&enabled, &default_value, &receive_only}, field_trial); + } + + // Whether datagram transport data channel support is enabled at all. + // Defaults to true, allowing datagram transport to be used if (a) the + // application provides a factory for it and (b) the configuration specifies + // its use. This flag provides a kill-switch to force-disable datagram + // transport across all applications, without code changes. + FieldTrialFlag enabled; + + // Whether the datagram transport data channels are enabled or disabled by + // default. Defaults to false, meaning that applications must configure use + // of datagram transport through RTCConfiguration. If set to true, + // applications will use the datagram transport by default (but may still + // explicitly configure themselves not to use it through RTCConfiguration). + FieldTrialFlag default_value; + + // Whether the datagram transport is enabled in receive-only mode. If true, + // and if the datagram transport is enabled, it will only be used when + // receiving incoming calls, not when placing outgoing calls. + FieldTrialFlag receive_only; + }; + + // Captures partial state to be used for rollback. Applicable only in + // Unified Plan. + class TransceiverStableState { + public: + TransceiverStableState() {} + void set_newly_created(); + void SetMSectionIfUnset(absl::optional mid, + absl::optional mline_index); + void SetRemoteStreamIdsIfUnset(const std::vector& ids); + absl::optional mid() const { return mid_; } + absl::optional mline_index() const { return mline_index_; } + absl::optional> remote_stream_ids() const { + return remote_stream_ids_; + } + bool has_m_section() const { return has_m_section_; } + bool newly_created() const { return newly_created_; } + + private: + absl::optional mid_; + absl::optional mline_index_; + absl::optional> remote_stream_ids_; + // Indicates that mid value from stable state has been captured and + // that rollback has to restore the transceiver. Also protects against + // subsequent overwrites. + bool has_m_section_ = false; + // Indicates that the transceiver was created as part of applying a + // description to track potential need for removing transceiver during + // rollback. + bool newly_created_ = false; + }; + + // Implements MessageHandler. + void OnMessage(rtc::Message* msg) override; + + // Plan B helpers for getting the voice/video media channels for the single + // audio/video transceiver, if it exists. + cricket::VoiceMediaChannel* voice_media_channel() const + RTC_RUN_ON(signaling_thread()); + cricket::VideoMediaChannel* video_media_channel() const + RTC_RUN_ON(signaling_thread()); + + std::vector>> + GetSendersInternal() const RTC_RUN_ON(signaling_thread()); + std::vector< + rtc::scoped_refptr>> + GetReceiversInternal() const RTC_RUN_ON(signaling_thread()); + + rtc::scoped_refptr> + GetAudioTransceiver() const RTC_RUN_ON(signaling_thread()); + rtc::scoped_refptr> + GetVideoTransceiver() const RTC_RUN_ON(signaling_thread()); + + rtc::scoped_refptr> + GetFirstAudioTransceiver() const RTC_RUN_ON(signaling_thread()); + + // Implementation of the offer/answer exchange operations. These are chained + // onto the |operations_chain_| when the public CreateOffer(), CreateAnswer(), + // SetLocalDescription() and SetRemoteDescription() methods are invoked. + void DoCreateOffer( + const RTCOfferAnswerOptions& options, + rtc::scoped_refptr observer); + void DoCreateAnswer( + const RTCOfferAnswerOptions& options, + rtc::scoped_refptr observer); + void DoSetLocalDescription( + std::unique_ptr desc, + rtc::scoped_refptr observer); + void DoSetRemoteDescription( + std::unique_ptr desc, + rtc::scoped_refptr observer); + + void CreateAudioReceiver(MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) + RTC_RUN_ON(signaling_thread()); + + void CreateVideoReceiver(MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) + RTC_RUN_ON(signaling_thread()); + rtc::scoped_refptr RemoveAndStopReceiver( + const RtpSenderInfo& remote_sender_info) RTC_RUN_ON(signaling_thread()); + + // May be called either by AddStream/RemoveStream, or when a track is + // added/removed from a stream previously added via AddStream. + void AddAudioTrack(AudioTrackInterface* track, MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + void RemoveAudioTrack(AudioTrackInterface* track, + MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + void AddVideoTrack(VideoTrackInterface* track, MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + void RemoveVideoTrack(VideoTrackInterface* track, + MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + + // AddTrack implementation when Unified Plan is specified. + RTCErrorOr> AddTrackUnifiedPlan( + rtc::scoped_refptr track, + const std::vector& stream_ids) + RTC_RUN_ON(signaling_thread()); + // AddTrack implementation when Plan B is specified. + RTCErrorOr> AddTrackPlanB( + rtc::scoped_refptr track, + const std::vector& stream_ids) + RTC_RUN_ON(signaling_thread()); + + // Returns the first RtpTransceiver suitable for a newly added track, if such + // transceiver is available. + rtc::scoped_refptr> + FindFirstTransceiverForAddedTrack( + rtc::scoped_refptr track) + RTC_RUN_ON(signaling_thread()); + + rtc::scoped_refptr> + FindTransceiverBySender(rtc::scoped_refptr sender) + RTC_RUN_ON(signaling_thread()); + + // Internal implementation for AddTransceiver family of methods. If + // |fire_callback| is set, fires OnRenegotiationNeeded callback if successful. + RTCErrorOr> AddTransceiver( + cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init, + bool fire_callback = true) RTC_RUN_ON(signaling_thread()); + + rtc::scoped_refptr> + CreateSender(cricket::MediaType media_type, + const std::string& id, + rtc::scoped_refptr track, + const std::vector& stream_ids, + const std::vector& send_encodings); + + rtc::scoped_refptr> + CreateReceiver(cricket::MediaType media_type, const std::string& receiver_id); + + // Create a new RtpTransceiver of the given type and add it to the list of + // transceivers. + rtc::scoped_refptr> + CreateAndAddTransceiver( + rtc::scoped_refptr> sender, + rtc::scoped_refptr> + receiver) RTC_RUN_ON(signaling_thread()); + + void SetIceConnectionState(IceConnectionState new_state) + RTC_RUN_ON(signaling_thread()); + void SetStandardizedIceConnectionState( + PeerConnectionInterface::IceConnectionState new_state) + RTC_RUN_ON(signaling_thread()); + void SetConnectionState( + PeerConnectionInterface::PeerConnectionState new_state) + RTC_RUN_ON(signaling_thread()); + + // Called any time the IceGatheringState changes. + void OnIceGatheringChange(IceGatheringState new_state) + RTC_RUN_ON(signaling_thread()); + // New ICE candidate has been gathered. + void OnIceCandidate(std::unique_ptr candidate) + RTC_RUN_ON(signaling_thread()); + // Gathering of an ICE candidate failed. + void OnIceCandidateError(const std::string& address, + int port, + const std::string& url, + int error_code, + const std::string& error_text) + RTC_RUN_ON(signaling_thread()); + // Some local ICE candidates have been removed. + void OnIceCandidatesRemoved(const std::vector& candidates) + RTC_RUN_ON(signaling_thread()); + + void OnSelectedCandidatePairChanged( + const cricket::CandidatePairChangeEvent& event) + RTC_RUN_ON(signaling_thread()); + + // Update the state, signaling if necessary. + void ChangeSignalingState(SignalingState signaling_state) + RTC_RUN_ON(signaling_thread()); + + // Signals from MediaStreamObserver. + void OnAudioTrackAdded(AudioTrackInterface* track, + MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + void OnAudioTrackRemoved(AudioTrackInterface* track, + MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + void OnVideoTrackAdded(VideoTrackInterface* track, + MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + void OnVideoTrackRemoved(VideoTrackInterface* track, + MediaStreamInterface* stream) + RTC_RUN_ON(signaling_thread()); + + void PostSetSessionDescriptionSuccess( + SetSessionDescriptionObserver* observer); + void PostSetSessionDescriptionFailure(SetSessionDescriptionObserver* observer, + RTCError&& error); + void PostCreateSessionDescriptionFailure( + CreateSessionDescriptionObserver* observer, + RTCError error); + + // Synchronous implementations of SetLocalDescription/SetRemoteDescription + // that return an RTCError instead of invoking a callback. + RTCError ApplyLocalDescription( + std::unique_ptr desc); + RTCError ApplyRemoteDescription( + std::unique_ptr desc); + + // Updates the local RtpTransceivers according to the JSEP rules. Called as + // part of setting the local/remote description. + RTCError UpdateTransceiversAndDataChannels( + cricket::ContentSource source, + const SessionDescriptionInterface& new_session, + const SessionDescriptionInterface* old_local_description, + const SessionDescriptionInterface* old_remote_description) + RTC_RUN_ON(signaling_thread()); + + // Either creates or destroys the transceiver's BaseChannel according to the + // given media section. + RTCError UpdateTransceiverChannel( + rtc::scoped_refptr> + transceiver, + const cricket::ContentInfo& content, + const cricket::ContentGroup* bundle_group) RTC_RUN_ON(signaling_thread()); + + // Either creates or destroys the local data channel according to the given + // media section. + RTCError UpdateDataChannel(cricket::ContentSource source, + const cricket::ContentInfo& content, + const cricket::ContentGroup* bundle_group) + RTC_RUN_ON(signaling_thread()); + + // Associate the given transceiver according to the JSEP rules. + RTCErrorOr< + rtc::scoped_refptr>> + AssociateTransceiver(cricket::ContentSource source, + SdpType type, + size_t mline_index, + const cricket::ContentInfo& content, + const cricket::ContentInfo* old_local_content, + const cricket::ContentInfo* old_remote_content) + RTC_RUN_ON(signaling_thread()); + + // Returns the RtpTransceiver, if found, that is associated to the given MID. + rtc::scoped_refptr> + GetAssociatedTransceiver(const std::string& mid) const + RTC_RUN_ON(signaling_thread()); + + // Returns the RtpTransceiver, if found, that was assigned to the given mline + // index in CreateOffer. + rtc::scoped_refptr> + GetTransceiverByMLineIndex(size_t mline_index) const + RTC_RUN_ON(signaling_thread()); + + // Returns an RtpTransciever, if available, that can be used to receive the + // given media type according to JSEP rules. + rtc::scoped_refptr> + FindAvailableTransceiverToReceive(cricket::MediaType media_type) const + RTC_RUN_ON(signaling_thread()); + + // Returns the media section in the given session description that is + // associated with the RtpTransceiver. Returns null if none found or this + // RtpTransceiver is not associated. Logic varies depending on the + // SdpSemantics specified in the configuration. + const cricket::ContentInfo* FindMediaSectionForTransceiver( + rtc::scoped_refptr> + transceiver, + const SessionDescriptionInterface* sdesc) const + RTC_RUN_ON(signaling_thread()); + + // Runs the algorithm **set the associated remote streams** specified in + // https://w3c.github.io/webrtc-pc/#set-associated-remote-streams. + void SetAssociatedRemoteStreams( + rtc::scoped_refptr receiver, + const std::vector& stream_ids, + std::vector>* added_streams, + std::vector>* removed_streams) + RTC_RUN_ON(signaling_thread()); + + // Runs the algorithm **process the removal of a remote track** specified in + // the WebRTC specification. + // This method will update the following lists: + // |remove_list| is the list of transceivers for which the receiving track is + // being removed. + // |removed_streams| is the list of streams which no longer have a receiving + // track so should be removed. + // https://w3c.github.io/webrtc-pc/#process-remote-track-removal + void ProcessRemovalOfRemoteTrack( + rtc::scoped_refptr> + transceiver, + std::vector>* remove_list, + std::vector>* removed_streams) + RTC_RUN_ON(signaling_thread()); + + void RemoveRemoteStreamsIfEmpty( + const std::vector>& + remote_streams, + std::vector>* removed_streams) + RTC_RUN_ON(signaling_thread()); + + void OnNegotiationNeeded(); + + // Returns a MediaSessionOptions struct with options decided by |options|, + // the local MediaStreams and DataChannels. + void GetOptionsForOffer(const PeerConnectionInterface::RTCOfferAnswerOptions& + offer_answer_options, + cricket::MediaSessionOptions* session_options) + RTC_RUN_ON(signaling_thread()); + void GetOptionsForPlanBOffer( + const PeerConnectionInterface::RTCOfferAnswerOptions& + offer_answer_options, + cricket::MediaSessionOptions* session_options) + RTC_RUN_ON(signaling_thread()); + void GetOptionsForUnifiedPlanOffer( + const PeerConnectionInterface::RTCOfferAnswerOptions& + offer_answer_options, + cricket::MediaSessionOptions* session_options) + RTC_RUN_ON(signaling_thread()); + + RTCError HandleLegacyOfferOptions(const RTCOfferAnswerOptions& options) + RTC_RUN_ON(signaling_thread()); + void RemoveRecvDirectionFromReceivingTransceiversOfType( + cricket::MediaType media_type) RTC_RUN_ON(signaling_thread()); + void AddUpToOneReceivingTransceiverOfType(cricket::MediaType media_type); + std::vector< + rtc::scoped_refptr>> + GetReceivingTransceiversOfType(cricket::MediaType media_type) + RTC_RUN_ON(signaling_thread()); + + // Returns a MediaSessionOptions struct with options decided by + // |constraints|, the local MediaStreams and DataChannels. + void GetOptionsForAnswer(const RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) + RTC_RUN_ON(signaling_thread()); + void GetOptionsForPlanBAnswer( + const PeerConnectionInterface::RTCOfferAnswerOptions& + offer_answer_options, + cricket::MediaSessionOptions* session_options) + RTC_RUN_ON(signaling_thread()); + void GetOptionsForUnifiedPlanAnswer( + const PeerConnectionInterface::RTCOfferAnswerOptions& + offer_answer_options, + cricket::MediaSessionOptions* session_options) + RTC_RUN_ON(signaling_thread()); + + // Generates MediaDescriptionOptions for the |session_opts| based on existing + // local description or remote description. + void GenerateMediaDescriptionOptions( + const SessionDescriptionInterface* session_desc, + RtpTransceiverDirection audio_direction, + RtpTransceiverDirection video_direction, + absl::optional* audio_index, + absl::optional* video_index, + absl::optional* data_index, + cricket::MediaSessionOptions* session_options) + RTC_RUN_ON(signaling_thread()); + + // Remove all local and remote senders of type |media_type|. + // Called when a media type is rejected (m-line set to port 0). + void RemoveSenders(cricket::MediaType media_type) + RTC_RUN_ON(signaling_thread()); + + // Makes sure a MediaStreamTrack is created for each StreamParam in |streams|, + // and existing MediaStreamTracks are removed if there is no corresponding + // StreamParam. If |default_track_needed| is true, a default MediaStreamTrack + // is created if it doesn't exist; if false, it's removed if it exists. + // |media_type| is the type of the |streams| and can be either audio or video. + // If a new MediaStream is created it is added to |new_streams|. + void UpdateRemoteSendersList( + const std::vector& streams, + bool default_track_needed, + cricket::MediaType media_type, + StreamCollection* new_streams) RTC_RUN_ON(signaling_thread()); + + // Triggered when a remote sender has been seen for the first time in a remote + // session description. It creates a remote MediaStreamTrackInterface + // implementation and triggers CreateAudioReceiver or CreateVideoReceiver. + void OnRemoteSenderAdded(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) + RTC_RUN_ON(signaling_thread()); + + // Triggered when a remote sender has been removed from a remote session + // description. It removes the remote sender with id |sender_id| from a remote + // MediaStream and triggers DestroyAudioReceiver or DestroyVideoReceiver. + void OnRemoteSenderRemoved(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) + RTC_RUN_ON(signaling_thread()); + + // Finds remote MediaStreams without any tracks and removes them from + // |remote_streams_| and notifies the observer that the MediaStreams no longer + // exist. + void UpdateEndedRemoteMediaStreams() RTC_RUN_ON(signaling_thread()); + + // Loops through the vector of |streams| and finds added and removed + // StreamParams since last time this method was called. + // For each new or removed StreamParam, OnLocalSenderSeen or + // OnLocalSenderRemoved is invoked. + void UpdateLocalSenders(const std::vector& streams, + cricket::MediaType media_type) + RTC_RUN_ON(signaling_thread()); + + // Triggered when a local sender has been seen for the first time in a local + // session description. + // This method triggers CreateAudioSender or CreateVideoSender if the rtp + // streams in the local SessionDescription can be mapped to a MediaStreamTrack + // in a MediaStream in |local_streams_| + void OnLocalSenderAdded(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) + RTC_RUN_ON(signaling_thread()); + + // Triggered when a local sender has been removed from a local session + // description. + // This method triggers DestroyAudioSender or DestroyVideoSender if a stream + // has been removed from the local SessionDescription and the stream can be + // mapped to a MediaStreamTrack in a MediaStream in |local_streams_|. + void OnLocalSenderRemoved(const RtpSenderInfo& sender_info, + cricket::MediaType media_type) + RTC_RUN_ON(signaling_thread()); + + // Returns true if the TgPeerConnection is configured to use Unified Plan + // semantics for creating offers/answers and setting local/remote + // descriptions. If this is true the RtpTransceiver API will also be available + // to the user. If this is false, Plan B semantics are assumed. + // TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once + // sufficient time has passed. + bool IsUnifiedPlan() const RTC_RUN_ON(signaling_thread()) { + return configuration_.sdp_semantics == SdpSemantics::kUnifiedPlan; + } + + // The offer/answer machinery assumes the media section MID is present and + // unique. To support legacy end points that do not supply a=mid lines, this + // method will modify the session description to add MIDs generated according + // to the SDP semantics. + void FillInMissingRemoteMids(cricket::SessionDescription* remote_description) + RTC_RUN_ON(signaling_thread()); + + // Is there an RtpSender of the given type? + bool HasRtpSender(cricket::MediaType type) const + RTC_RUN_ON(signaling_thread()); + + // Return the RtpSender with the given track attached. + rtc::scoped_refptr> + FindSenderForTrack(MediaStreamTrackInterface* track) const + RTC_RUN_ON(signaling_thread()); + + // Return the RtpSender with the given id, or null if none exists. + rtc::scoped_refptr> + FindSenderById(const std::string& sender_id) const + RTC_RUN_ON(signaling_thread()); + + // Return the RtpReceiver with the given id, or null if none exists. + rtc::scoped_refptr> + FindReceiverById(const std::string& receiver_id) const + RTC_RUN_ON(signaling_thread()); + + std::vector* GetRemoteSenderInfos( + cricket::MediaType media_type); + std::vector* GetLocalSenderInfos( + cricket::MediaType media_type); + const RtpSenderInfo* FindSenderInfo(const std::vector& infos, + const std::string& stream_id, + const std::string sender_id) const; + + // Returns the specified SCTP DataChannel in sctp_data_channels_, + // or nullptr if not found. + DataChannel* FindDataChannelBySid(int sid) const + RTC_RUN_ON(signaling_thread()); + + // Called when first configuring the port allocator. + struct InitializePortAllocatorResult { + bool enable_ipv6; + }; + InitializePortAllocatorResult InitializePortAllocator_n( + const cricket::ServerAddresses& stun_servers, + const std::vector& turn_servers, + const RTCConfiguration& configuration); + // Called when SetConfiguration is called to apply the supported subset + // of the configuration on the network thread. + bool ReconfigurePortAllocator_n( + const cricket::ServerAddresses& stun_servers, + const std::vector& turn_servers, + IceTransportsType type, + int candidate_pool_size, + PortPrunePolicy turn_port_prune_policy, + webrtc::TurnCustomizer* turn_customizer, + absl::optional stun_candidate_keepalive_interval, + bool have_local_description); + + // Starts output of an RTC event log to the given output object. + // This function should only be called from the worker thread. + bool StartRtcEventLog_w(std::unique_ptr output, + int64_t output_period_ms); + + // Stops recording an RTC event log. + // This function should only be called from the worker thread. + void StopRtcEventLog_w(); + + // Ensures the configuration doesn't have any parameters with invalid values, + // or values that conflict with other parameters. + // + // Returns RTCError::OK() if there are no issues. + RTCError ValidateConfiguration(const RTCConfiguration& config) const; + + cricket::ChannelManager* channel_manager() const; + + enum class SessionError { + kNone, // No error. + kContent, // Error in BaseChannel SetLocalContent/SetRemoteContent. + kTransport, // Error from the underlying transport. + }; + + // Returns the last error in the session. See the enum above for details. + SessionError session_error() const RTC_RUN_ON(signaling_thread()) { + return session_error_; + } + const std::string& session_error_desc() const { return session_error_desc_; } + + cricket::ChannelInterface* GetChannel(const std::string& content_name); + + cricket::IceConfig ParseIceConfig( + const PeerConnectionInterface::RTCConfiguration& config) 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); + + // Non-const versions of local_description()/remote_description(), for use + // internally. + SessionDescriptionInterface* mutable_local_description() + RTC_RUN_ON(signaling_thread()) { + return pending_local_description_ ? pending_local_description_.get() + : current_local_description_.get(); + } + SessionDescriptionInterface* mutable_remote_description() + RTC_RUN_ON(signaling_thread()) { + return pending_remote_description_ ? pending_remote_description_.get() + : current_remote_description_.get(); + } + + // Updates the error state, signaling if necessary. + void SetSessionError(SessionError error, const std::string& error_desc); + + RTCError UpdateSessionState(SdpType type, + cricket::ContentSource source, + const cricket::SessionDescription* description); + // Push the media parts of the local or remote session description + // down to all of the channels. + RTCError PushdownMediaDescription(SdpType type, cricket::ContentSource source) + RTC_RUN_ON(signaling_thread()); + + RTCError PushdownTransportDescription(cricket::ContentSource source, + SdpType type); + + // 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); + + // Enables media channels to allow sending of media. + // This enables media to flow on all configured audio/video channels and the + // RtpDataChannel. + void EnableSending() RTC_RUN_ON(signaling_thread()); + + // Destroys all BaseChannels and destroys the SCTP data channel, if present. + void DestroyAllChannels() RTC_RUN_ON(signaling_thread()); + + // 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) + RTC_RUN_ON(signaling_thread()); + // Uses all remote candidates in |remote_desc| in this session. + bool UseCandidatesInSessionDescription( + const SessionDescriptionInterface* remote_desc) + RTC_RUN_ON(signaling_thread()); + // Uses |candidate| in this session. + bool UseCandidate(const IceCandidateInterface* candidate) + RTC_RUN_ON(signaling_thread()); + RTCErrorOr FindContentInfo( + const SessionDescriptionInterface* description, + const IceCandidateInterface* candidate) RTC_RUN_ON(signaling_thread()); + // 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) + RTC_RUN_ON(signaling_thread()); + + // 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. + RTCError CreateChannels(const cricket::SessionDescription& desc) + RTC_RUN_ON(signaling_thread()); + + // If the BUNDLE policy is max-bundle, then we know for sure that all + // transports will be bundled from the start. This method returns the BUNDLE + // group if that's the case, or null if BUNDLE will be negotiated later. An + // error is returned if max-bundle is specified but the session description + // does not have a BUNDLE group. + RTCErrorOr GetEarlyBundleGroup( + const cricket::SessionDescription& desc) const + RTC_RUN_ON(signaling_thread()); + + // Helper methods to create media channels. + cricket::VoiceChannel* CreateVoiceChannel(const std::string& mid) + RTC_RUN_ON(signaling_thread()); + cricket::VideoChannel* CreateVideoChannel(const std::string& mid) + RTC_RUN_ON(signaling_thread()); + bool CreateDataChannel(const std::string& mid) RTC_RUN_ON(signaling_thread()); + + bool SetupDataChannelTransport_n(const std::string& mid) + RTC_RUN_ON(network_thread()); + void TeardownDataChannelTransport_n() RTC_RUN_ON(network_thread()); + + bool ValidateBundleSettings(const cricket::SessionDescription* desc); + bool HasRtcpMuxEnabled(const cricket::ContentInfo* content); + // Below methods are helper methods which verifies SDP. + RTCError ValidateSessionDescription(const SessionDescriptionInterface* sdesc, + cricket::ContentSource source) + RTC_RUN_ON(signaling_thread()); + + // Check if a call to SetLocalDescription is acceptable with a session + // description of the given type. + bool ExpectSetLocalDescription(SdpType type); + // Check if a call to SetRemoteDescription is acceptable with a session + // description of the given type. + bool ExpectSetRemoteDescription(SdpType type); + // Verifies a=setup attribute as per RFC 5763. + bool ValidateDtlsSetupAttribute(const cricket::SessionDescription* desc, + SdpType type); + + // 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) RTC_RUN_ON(signaling_thread()); + + // Returns true if SRTP (either using DTLS-SRTP or SDES) is required by + // this session. + bool SrtpRequired() const RTC_RUN_ON(signaling_thread()); + + // TgJsepTransportController signal handlers. + void OnTransportControllerConnectionState(cricket::IceConnectionState state) + RTC_RUN_ON(signaling_thread()); + void OnTransportControllerGatheringState(cricket::IceGatheringState state) + RTC_RUN_ON(signaling_thread()); + void OnTransportControllerCandidatesGathered( + const std::string& transport_name, + const std::vector& candidates) + RTC_RUN_ON(signaling_thread()); + void OnTransportControllerCandidateError( + const cricket::IceCandidateErrorEvent& event) + RTC_RUN_ON(signaling_thread()); + void OnTransportControllerCandidatesRemoved( + const std::vector& candidates) + RTC_RUN_ON(signaling_thread()); + void OnTransportControllerCandidateChanged( + const cricket::CandidatePairChangeEvent& event) + RTC_RUN_ON(signaling_thread()); + void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error); + + const char* SessionErrorToString(SessionError error) const; + std::string GetSessionErrorMsg() RTC_RUN_ON(signaling_thread()); + + // Report the UMA metric SdpFormatReceived for the given remote offer. + void ReportSdpFormatReceived(const SessionDescriptionInterface& remote_offer); + + // Report inferred negotiated SDP semantics from a local/remote answer to the + // UMA observer. + void ReportNegotiatedSdpSemantics(const SessionDescriptionInterface& answer); + + // Invoked when TransportController connection completion is signaled. + // Reports stats for all transports in use. + void ReportTransportStats() RTC_RUN_ON(signaling_thread()); + + // Gather the usage of IPv4/IPv6 as best connection. + void ReportBestConnectionState(const cricket::TransportStats& stats); + + void ReportNegotiatedCiphers(const cricket::TransportStats& stats, + const std::set& media_types) + RTC_RUN_ON(signaling_thread()); + void ReportIceCandidateCollected(const cricket::Candidate& candidate) + RTC_RUN_ON(signaling_thread()); + void ReportRemoteIceCandidateAdded(const cricket::Candidate& candidate) + RTC_RUN_ON(signaling_thread()); + + void NoteUsageEvent(UsageEvent event); + void ReportUsagePattern() const RTC_RUN_ON(signaling_thread()); + + void OnSentPacket_w(const rtc::SentPacket& sent_packet); + + const std::string GetTransportName(const std::string& content_name) + RTC_RUN_ON(signaling_thread()); + + // Functions for dealing with transports. + // Note that cricket code uses the term "channel" for what other code + // refers to as "transport". + + // Destroys and clears the BaseChannel associated with the given transceiver, + // if such channel is set. + void DestroyTransceiverChannel( + rtc::scoped_refptr> + transceiver); + + // Destroys the given ChannelInterface. + // The channel cannot be accessed after this method is called. + void DestroyChannelInterface(cricket::ChannelInterface* channel); + + // TgJsepTransportController::Observer override. + // + // Called by |transport_controller_| when processing transport information + // from a session description, and the mapping from m= sections to transports + // changed (as a result of BUNDLE negotiation, or m= sections being + // rejected). + bool OnTransportChanged( + const std::string& mid, + RtpTransportInternal* rtp_transport, + rtc::scoped_refptr dtls_transport, + DataChannelTransportInterface* data_channel_transport) override; + + // RtpSenderBase::SetStreamsObserver override. + void OnSetStreams() override; + + // Returns the CryptoOptions for this TgPeerConnection. This will always + // return the RTCConfiguration.crypto_options if set and will only default + // back to the PeerConnectionFactory settings if nothing was set. + CryptoOptions GetCryptoOptions() RTC_RUN_ON(signaling_thread()); + + // Returns rtp transport, result can not be nullptr. + RtpTransportInternal* GetRtpTransport(const std::string& mid) + RTC_RUN_ON(signaling_thread()) { + auto rtp_transport = transport_controller_->GetRtpTransport(mid); + RTC_DCHECK(rtp_transport); + return rtp_transport; + } + + void UpdateNegotiationNeeded(); + bool CheckIfNegotiationIsNeeded(); + + // | sdp_type | is the type of the SDP that caused the rollback. + RTCError Rollback(SdpType sdp_type); + + // Storing the factory as a scoped reference pointer ensures that the memory + // in the PeerConnectionFactoryImpl remains available as long as the + // TgPeerConnection is running. It is passed to TgPeerConnection as a raw pointer. + // However, since the reference counting is done in the + // PeerConnectionFactoryInterface all instances created using the raw pointer + // will refer to the same reference count. + const rtc::scoped_refptr factory_; + PeerConnectionObserver* observer_ RTC_GUARDED_BY(signaling_thread()) = + nullptr; + + // The EventLog needs to outlive |call_| (and any other object that uses it). + std::unique_ptr event_log_ RTC_GUARDED_BY(worker_thread()); + + // Points to the same thing as `event_log_`. Since it's const, we may read the + // pointer (but not touch the object) from any thread. + RtcEventLog* const event_log_ptr_ RTC_PT_GUARDED_BY(worker_thread()); + + // The operations chain is used by the offer/answer exchange methods to ensure + // they are executed in the right order. For example, if + // SetRemoteDescription() is invoked while CreateOffer() is still pending, the + // SRD operation will not start until CreateOffer() has completed. See + // https://w3c.github.io/webrtc-pc/#dfn-operations-chain. + rtc::scoped_refptr operations_chain_ + RTC_GUARDED_BY(signaling_thread()); + + SignalingState signaling_state_ RTC_GUARDED_BY(signaling_thread()) = kStable; + IceConnectionState ice_connection_state_ RTC_GUARDED_BY(signaling_thread()) = + kIceConnectionNew; + PeerConnectionInterface::IceConnectionState standardized_ice_connection_state_ + RTC_GUARDED_BY(signaling_thread()) = kIceConnectionNew; + PeerConnectionInterface::PeerConnectionState connection_state_ + RTC_GUARDED_BY(signaling_thread()) = PeerConnectionState::kNew; + + IceGatheringState ice_gathering_state_ RTC_GUARDED_BY(signaling_thread()) = + kIceGatheringNew; + PeerConnectionInterface::RTCConfiguration configuration_ + RTC_GUARDED_BY(signaling_thread()); + + // Field-trial based configuration for datagram transport. + const DatagramTransportConfig datagram_transport_config_; + + // Field-trial based configuration for datagram transport data channels. + const DatagramTransportDataChannelConfig + datagram_transport_data_channel_config_; + + // Final, resolved value for whether datagram transport is in use. + bool use_datagram_transport_ RTC_GUARDED_BY(signaling_thread()) = false; + + // Equivalent of |use_datagram_transport_|, but for its use with data + // channels. + bool use_datagram_transport_for_data_channels_ + RTC_GUARDED_BY(signaling_thread()) = false; + + // Resolved value of whether to use data channels only for incoming calls. + bool use_datagram_transport_for_data_channels_receive_only_ + RTC_GUARDED_BY(signaling_thread()) = false; + + // TODO(zstein): |async_resolver_factory_| can currently be nullptr if it + // is not injected. It should be required once chromium supplies it. + std::unique_ptr async_resolver_factory_ + RTC_GUARDED_BY(signaling_thread()); + std::unique_ptr + port_allocator_; // TODO(bugs.webrtc.org/9987): Accessed on both + // signaling and network thread. + std::unique_ptr + ice_transport_factory_; // TODO(bugs.webrtc.org/9987): Accessed on the + // signaling thread but the underlying raw + // pointer is given to + // |jsep_transport_controller_| and used on the + // network thread. + std::unique_ptr + tls_cert_verifier_; // TODO(bugs.webrtc.org/9987): Accessed on both + // signaling and network thread. + + // One TgPeerConnection has only one RTCP CNAME. + // https://tools.ietf.org/html/draft-ietf-rtcweb-rtp-usage-26#section-4.9 + const std::string rtcp_cname_; + + // Streams added via AddStream. + const rtc::scoped_refptr local_streams_ + RTC_GUARDED_BY(signaling_thread()); + // Streams created as a result of SetRemoteDescription. + const rtc::scoped_refptr remote_streams_ + RTC_GUARDED_BY(signaling_thread()); + + std::vector> stream_observers_ + RTC_GUARDED_BY(signaling_thread()); + + // These lists store sender info seen in local/remote descriptions. + std::vector remote_audio_sender_infos_ + RTC_GUARDED_BY(signaling_thread()); + std::vector remote_video_sender_infos_ + RTC_GUARDED_BY(signaling_thread()); + std::vector local_audio_sender_infos_ + RTC_GUARDED_BY(signaling_thread()); + std::vector local_video_sender_infos_ + RTC_GUARDED_BY(signaling_thread()); + + bool remote_peer_supports_msid_ RTC_GUARDED_BY(signaling_thread()) = false; + + // The unique_ptr belongs to the worker thread, but the Call object manages + // its own thread safety. + std::unique_ptr call_ RTC_GUARDED_BY(worker_thread()); + + rtc::AsyncInvoker rtcp_invoker_ RTC_GUARDED_BY(network_thread()); + + // Points to the same thing as `call_`. Since it's const, we may read the + // pointer from any thread. + Call* const call_ptr_; + + std::unique_ptr stats_ + RTC_GUARDED_BY(signaling_thread()); // A pointer is passed to senders_ + rtc::scoped_refptr stats_collector_ + RTC_GUARDED_BY(signaling_thread()); + // Holds changes made to transceivers during applying descriptors for + // potential rollback. Gets cleared once signaling state goes to stable. + std::map>, + TransceiverStableState> + transceiver_stable_states_by_transceivers_; + // Holds remote stream ids for transceivers from stable state. + std::map>, + std::vector> + remote_stream_ids_by_transceivers_; + std::vector< + rtc::scoped_refptr>> + transceivers_; // TODO(bugs.webrtc.org/9987): Accessed on both signaling + // and network thread. + + // In Unified Plan, if we encounter remote SDP that does not contain an a=msid + // line we create and use a stream with a random ID for our receivers. This is + // to support legacy endpoints that do not support the a=msid attribute (as + // opposed to streamless tracks with "a=msid:-"). + rtc::scoped_refptr missing_msid_default_stream_ + RTC_GUARDED_BY(signaling_thread()); + // MIDs will be generated using this generator which will keep track of + // all the MIDs that have been seen over the life of the TgPeerConnection. + rtc::UniqueStringGenerator mid_generator_ RTC_GUARDED_BY(signaling_thread()); + + SessionError session_error_ RTC_GUARDED_BY(signaling_thread()) = + SessionError::kNone; + std::string session_error_desc_ RTC_GUARDED_BY(signaling_thread()); + + std::string session_id_ RTC_GUARDED_BY(signaling_thread()); + + std::unique_ptr + transport_controller_; // TODO(bugs.webrtc.org/9987): Accessed on both + // signaling and network thread. + std::unique_ptr + sctp_factory_; // TODO(bugs.webrtc.org/9987): Accessed on both + // signaling and network thread. + + // |sctp_mid_| is the content name (MID) in SDP. + // Note: this is used as the data channel MID by both SCTP and data channel + // transports. It is set when either transport is initialized and unset when + // both transports are deleted. + absl::optional + sctp_mid_; // TODO(bugs.webrtc.org/9987): Accessed on both signaling + // and network thread. + + // Whether this peer is the caller. Set when the local description is applied. + absl::optional is_caller_ RTC_GUARDED_BY(signaling_thread()); + + + + std::unique_ptr current_local_description_ + RTC_GUARDED_BY(signaling_thread()); + std::unique_ptr pending_local_description_ + RTC_GUARDED_BY(signaling_thread()); + std::unique_ptr current_remote_description_ + RTC_GUARDED_BY(signaling_thread()); + std::unique_ptr pending_remote_description_ + RTC_GUARDED_BY(signaling_thread()); + bool dtls_enabled_ RTC_GUARDED_BY(signaling_thread()) = false; + + // List of content names for which the remote side triggered an ICE restart. + std::set pending_ice_restarts_ + RTC_GUARDED_BY(signaling_thread()); + + std::unique_ptr webrtc_session_desc_factory_ + RTC_GUARDED_BY(signaling_thread()); + + // Member variables for caching global options. + cricket::AudioOptions audio_options_ RTC_GUARDED_BY(signaling_thread()); + cricket::VideoOptions video_options_ RTC_GUARDED_BY(signaling_thread()); + + int usage_event_accumulator_ RTC_GUARDED_BY(signaling_thread()) = 0; + bool return_histogram_very_quickly_ RTC_GUARDED_BY(signaling_thread()) = + false; + + // This object should be used to generate any SSRC that is not explicitly + // specified by the user (or by the remote party). + // The generator is not used directly, instead it is passed on to the + // channel manager and the session description factory. + rtc::UniqueRandomIdGenerator ssrc_generator_ + RTC_GUARDED_BY(signaling_thread()); + + // A video bitrate allocator factory. + // This can injected using the PeerConnectionDependencies, + // or else the CreateBuiltinVideoBitrateAllocatorFactory() will be called. + // Note that one can still choose to override this in a MediaEngine + // if one wants too. + std::unique_ptr + video_bitrate_allocator_factory_; + + std::unique_ptr + local_ice_credentials_to_replace_ RTC_GUARDED_BY(signaling_thread()); + bool is_negotiation_needed_ RTC_GUARDED_BY(signaling_thread()) = false; + + rtc::WeakPtrFactory weak_ptr_factory_ + RTC_GUARDED_BY(signaling_thread()); +}; + +} // namespace webrtc + +#endif // PC_PEER_CONNECTION_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp new file mode 100644 index 0000000000..7cf9478b66 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp @@ -0,0 +1,406 @@ +/* + * Copyright 2004 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 "tg_peer_connection_factory.h" + +#include +#include +#include + +#include "api/fec_controller.h" +#include "api/media_stream_proxy.h" +#include "api/media_stream_track_proxy.h" +#include "api/network_state_predictor.h" +#include "api/peer_connection_factory_proxy.h" +#include "api/peer_connection_proxy.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/transport/field_trial_based_config.h" +#include "api/transport/media/media_transport_interface.h" +#include "api/turn_customizer.h" +#include "api/units/data_rate.h" +#include "api/video_track_source_proxy.h" +#include "media/sctp/sctp_transport.h" +#include "p2p/base/basic_packet_socket_factory.h" +#include "p2p/base/default_ice_transport_factory.h" +#include "p2p/client/basic_port_allocator.h" +#include "pc/audio_track.h" +#include "pc/local_audio_source.h" +#include "pc/media_stream.h" +#include "pc/peer_connection.h" +#include "pc/rtp_parameters_conversion.h" +#include "pc/video_track.h" +#include "rtc_base/bind.h" +#include "rtc_base/checks.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/experiments/field_trial_units.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/system/file_wrapper.h" + +#include "tg_rtp_data_engine.h" +#include "tg_peer_connection.h" + +namespace webrtc { + +rtc::scoped_refptr +CreateModularPeerConnectionFactory( + PeerConnectionFactoryDependencies dependencies) { + rtc::scoped_refptr pc_factory( + new rtc::RefCountedObject( + std::move(dependencies))); + // Call Initialize synchronously but make sure it is executed on + // |signaling_thread|. + MethodCall call( + pc_factory.get(), &TgPeerConnectionFactory::Initialize); + bool result = call.Marshal(RTC_FROM_HERE, pc_factory->signaling_thread()); + + if (!result) { + return nullptr; + } + return PeerConnectionFactoryProxy::Create(pc_factory->signaling_thread(), + pc_factory); +} + +TgPeerConnectionFactory::TgPeerConnectionFactory( + PeerConnectionFactoryDependencies dependencies) + : wraps_current_thread_(false), + network_thread_(dependencies.network_thread), + worker_thread_(dependencies.worker_thread), + signaling_thread_(dependencies.signaling_thread), + task_queue_factory_(std::move(dependencies.task_queue_factory)), + media_engine_(std::move(dependencies.media_engine)), + call_factory_(std::move(dependencies.call_factory)), + event_log_factory_(std::move(dependencies.event_log_factory)), + fec_controller_factory_(std::move(dependencies.fec_controller_factory)), + network_state_predictor_factory_( + std::move(dependencies.network_state_predictor_factory)), + injected_network_controller_factory_( + std::move(dependencies.network_controller_factory)), + media_transport_factory_(std::move(dependencies.media_transport_factory)), + neteq_factory_(std::move(dependencies.neteq_factory)), + trials_(dependencies.trials ? std::move(dependencies.trials) + : std::make_unique()) { + if (!network_thread_) { + owned_network_thread_ = rtc::Thread::CreateWithSocketServer(); + owned_network_thread_->SetName("pc_network_thread", nullptr); + owned_network_thread_->Start(); + network_thread_ = owned_network_thread_.get(); + } + + if (!worker_thread_) { + owned_worker_thread_ = rtc::Thread::Create(); + owned_worker_thread_->SetName("pc_worker_thread", nullptr); + owned_worker_thread_->Start(); + worker_thread_ = owned_worker_thread_.get(); + } + + if (!signaling_thread_) { + signaling_thread_ = rtc::Thread::Current(); + if (!signaling_thread_) { + // If this thread isn't already wrapped by an rtc::Thread, create a + // wrapper and own it in this class. + signaling_thread_ = rtc::ThreadManager::Instance()->WrapCurrentThread(); + wraps_current_thread_ = true; + } + } + + options_.disable_encryption = true; +} + +TgPeerConnectionFactory::~TgPeerConnectionFactory() { + RTC_DCHECK(signaling_thread_->IsCurrent()); + channel_manager_.reset(nullptr); + + // Make sure |worker_thread_| and |signaling_thread_| outlive + // |default_socket_factory_| and |default_network_manager_|. + default_socket_factory_ = nullptr; + default_network_manager_ = nullptr; + + if (wraps_current_thread_) + rtc::ThreadManager::Instance()->UnwrapCurrentThread(); +} + +bool TgPeerConnectionFactory::Initialize() { + RTC_DCHECK(signaling_thread_->IsCurrent()); + rtc::InitRandom(rtc::Time32()); + + default_network_manager_.reset(new rtc::BasicNetworkManager()); + if (!default_network_manager_) { + return false; + } + + default_socket_factory_.reset( + new rtc::BasicPacketSocketFactory(network_thread_)); + if (!default_socket_factory_) { + return false; + } + + channel_manager_ = std::make_unique( + std::move(media_engine_), std::make_unique(), + worker_thread_, network_thread_); + + channel_manager_->SetVideoRtxEnabled(true); + if (!channel_manager_->Init()) { + return false; + } + + return true; +} + +void TgPeerConnectionFactory::SetOptions(const Options& options) { + options_ = options; +} + +RtpCapabilities TgPeerConnectionFactory::GetRtpSenderCapabilities( + cricket::MediaType kind) const { + RTC_DCHECK_RUN_ON(signaling_thread_); + switch (kind) { + case cricket::MEDIA_TYPE_AUDIO: { + cricket::AudioCodecs cricket_codecs; + cricket::RtpHeaderExtensions cricket_extensions; + channel_manager_->GetSupportedAudioSendCodecs(&cricket_codecs); + channel_manager_->GetSupportedAudioRtpHeaderExtensions( + &cricket_extensions); + return ToRtpCapabilities(cricket_codecs, cricket_extensions); + } + case cricket::MEDIA_TYPE_VIDEO: { + cricket::VideoCodecs cricket_codecs; + cricket::RtpHeaderExtensions cricket_extensions; + channel_manager_->GetSupportedVideoCodecs(&cricket_codecs); + channel_manager_->GetSupportedVideoRtpHeaderExtensions( + &cricket_extensions); + return ToRtpCapabilities(cricket_codecs, cricket_extensions); + } + case cricket::MEDIA_TYPE_DATA: + return RtpCapabilities(); + } + // Not reached; avoids compile warning. + FATAL(); +} + +RtpCapabilities TgPeerConnectionFactory::GetRtpReceiverCapabilities( + cricket::MediaType kind) const { + RTC_DCHECK_RUN_ON(signaling_thread_); + switch (kind) { + case cricket::MEDIA_TYPE_AUDIO: { + cricket::AudioCodecs cricket_codecs; + cricket::RtpHeaderExtensions cricket_extensions; + channel_manager_->GetSupportedAudioReceiveCodecs(&cricket_codecs); + channel_manager_->GetSupportedAudioRtpHeaderExtensions( + &cricket_extensions); + return ToRtpCapabilities(cricket_codecs, cricket_extensions); + } + case cricket::MEDIA_TYPE_VIDEO: { + cricket::VideoCodecs cricket_codecs; + cricket::RtpHeaderExtensions cricket_extensions; + channel_manager_->GetSupportedVideoCodecs(&cricket_codecs); + channel_manager_->GetSupportedVideoRtpHeaderExtensions( + &cricket_extensions); + return ToRtpCapabilities(cricket_codecs, cricket_extensions); + } + case cricket::MEDIA_TYPE_DATA: + return RtpCapabilities(); + } + // Not reached; avoids compile warning. + FATAL(); +} + +rtc::scoped_refptr +TgPeerConnectionFactory::CreateAudioSource(const cricket::AudioOptions& options) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + rtc::scoped_refptr source( + LocalAudioSource::Create(&options)); + return source; +} + +bool TgPeerConnectionFactory::StartAecDump(FILE* file, int64_t max_size_bytes) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + return channel_manager_->StartAecDump(FileWrapper(file), max_size_bytes); +} + +void TgPeerConnectionFactory::StopAecDump() { + RTC_DCHECK(signaling_thread_->IsCurrent()); + channel_manager_->StopAecDump(); +} + +rtc::scoped_refptr +TgPeerConnectionFactory::CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + std::unique_ptr allocator, + std::unique_ptr cert_generator, + PeerConnectionObserver* observer) { + // Convert the legacy API into the new dependency structure. + PeerConnectionDependencies dependencies(observer); + dependencies.allocator = std::move(allocator); + dependencies.cert_generator = std::move(cert_generator); + // Pass that into the new API. + return CreatePeerConnection(configuration, std::move(dependencies)); +} + +rtc::scoped_refptr +TgPeerConnectionFactory::CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + RTC_DCHECK(!(dependencies.allocator && dependencies.packet_socket_factory)) + << "You can't set both allocator and packet_socket_factory; " + "the former is going away (see bugs.webrtc.org/7447"; + + // Set internal defaults if optional dependencies are not set. + if (!dependencies.cert_generator) { + dependencies.cert_generator = + std::make_unique(signaling_thread_, + network_thread_); + } + if (!dependencies.allocator) { + rtc::PacketSocketFactory* packet_socket_factory; + if (dependencies.packet_socket_factory) + packet_socket_factory = dependencies.packet_socket_factory.get(); + else + packet_socket_factory = default_socket_factory_.get(); + + network_thread_->Invoke(RTC_FROM_HERE, [this, &configuration, + &dependencies, + &packet_socket_factory]() { + dependencies.allocator = std::make_unique( + default_network_manager_.get(), packet_socket_factory, + configuration.turn_customizer); + }); + } + + if (!dependencies.ice_transport_factory) { + dependencies.ice_transport_factory = + std::make_unique(); + } + + // TODO(zstein): Once chromium injects its own AsyncResolverFactory, set + // |dependencies.async_resolver_factory| to a new + // |rtc::BasicAsyncResolverFactory| if no factory is provided. + + network_thread_->Invoke( + RTC_FROM_HERE, + rtc::Bind(&cricket::PortAllocator::SetNetworkIgnoreMask, + dependencies.allocator.get(), options_.network_ignore_mask)); + + std::unique_ptr event_log = + worker_thread_->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnectionFactory::CreateRtcEventLog_w, this)); + + std::unique_ptr call = worker_thread_->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnectionFactory::CreateCall_w, this, event_log.get())); + + rtc::scoped_refptr pc( + new rtc::RefCountedObject(this, std::move(event_log), + std::move(call))); + ActionsBeforeInitializeForTesting(pc); + if (!pc->Initialize(configuration, std::move(dependencies))) { + return nullptr; + } + return PeerConnectionProxy::Create(signaling_thread(), pc); +} + +rtc::scoped_refptr +TgPeerConnectionFactory::CreateLocalMediaStream(const std::string& stream_id) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + return MediaStreamProxy::Create(signaling_thread_, + MediaStream::Create(stream_id)); +} + +rtc::scoped_refptr TgPeerConnectionFactory::CreateVideoTrack( + const std::string& id, + VideoTrackSourceInterface* source) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + rtc::scoped_refptr track( + VideoTrack::Create(id, source, worker_thread_)); + return VideoTrackProxy::Create(signaling_thread_, worker_thread_, track); +} + +rtc::scoped_refptr TgPeerConnectionFactory::CreateAudioTrack( + const std::string& id, + AudioSourceInterface* source) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + rtc::scoped_refptr track(AudioTrack::Create(id, source)); + return AudioTrackProxy::Create(signaling_thread_, track); +} + +std::unique_ptr +TgPeerConnectionFactory::CreateSctpTransportInternalFactory() { +#ifdef HAVE_SCTP + return std::make_unique(network_thread()); +#else + return nullptr; +#endif +} + +cricket::ChannelManager* TgPeerConnectionFactory::channel_manager() { + return channel_manager_.get(); +} + +std::unique_ptr TgPeerConnectionFactory::CreateRtcEventLog_w() { + RTC_DCHECK_RUN_ON(worker_thread_); + + auto encoding_type = RtcEventLog::EncodingType::Legacy; + if (IsTrialEnabled("WebRTC-RtcEventLogNewFormat")) + encoding_type = RtcEventLog::EncodingType::NewFormat; + return event_log_factory_ + ? event_log_factory_->CreateRtcEventLog(encoding_type) + : std::make_unique(); +} + +std::unique_ptr TgPeerConnectionFactory::CreateCall_w( + RtcEventLog* event_log) { + RTC_DCHECK_RUN_ON(worker_thread_); + + webrtc::Call::Config call_config(event_log); + if (!channel_manager_->media_engine() || !call_factory_) { + return nullptr; + } + call_config.audio_state = + channel_manager_->media_engine()->voice().GetAudioState(); + + FieldTrialParameter min_bandwidth("min", DataRate::kbps(30)); + FieldTrialParameter start_bandwidth("start", DataRate::kbps(300)); + FieldTrialParameter max_bandwidth("max", DataRate::kbps(2000)); + ParseFieldTrial({&min_bandwidth, &start_bandwidth, &max_bandwidth}, + trials_->Lookup("WebRTC-PcFactoryDefaultBitrates")); + + call_config.bitrate_config.min_bitrate_bps = + rtc::saturated_cast(min_bandwidth->bps()); + call_config.bitrate_config.start_bitrate_bps = + rtc::saturated_cast(start_bandwidth->bps()); + call_config.bitrate_config.max_bitrate_bps = + rtc::saturated_cast(max_bandwidth->bps()); + + call_config.fec_controller_factory = fec_controller_factory_.get(); + call_config.task_queue_factory = task_queue_factory_.get(); + call_config.network_state_predictor_factory = + network_state_predictor_factory_.get(); + call_config.neteq_factory = neteq_factory_.get(); + + if (IsTrialEnabled("WebRTC-Bwe-InjectedCongestionController")) { + RTC_LOG(LS_INFO) << "Using injected network controller factory"; + call_config.network_controller_factory = + injected_network_controller_factory_.get(); + } else { + RTC_LOG(LS_INFO) << "Using default network controller factory"; + } + + call_config.trials = trials_.get(); + + return std::unique_ptr(call_factory_->CreateCall(call_config)); +} + +bool TgPeerConnectionFactory::IsTrialEnabled(absl::string_view key) const { + RTC_DCHECK(trials_); + return trials_->Lookup(key).find("Enabled") == 0; +} + +} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h new file mode 100644 index 0000000000..a9765e1002 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h @@ -0,0 +1,138 @@ + +/* + * Copyright 2011 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 TG_PC_PEER_CONNECTION_FACTORY_H_ +#define TG_PC_PEER_CONNECTION_FACTORY_H_ + +#include +#include + +#include "api/media_stream_interface.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "api/transport/media/media_transport_interface.h" +#include "media/sctp/sctp_transport_internal.h" +#include "pc/channel_manager.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/thread.h" + +namespace rtc { +class BasicNetworkManager; +class BasicPacketSocketFactory; +} // namespace rtc + +namespace webrtc { + +class RtcEventLog; + +class TgPeerConnectionFactory : public PeerConnectionFactoryInterface { + public: + void SetOptions(const Options& options) override; + + rtc::scoped_refptr CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + std::unique_ptr allocator, + std::unique_ptr cert_generator, + PeerConnectionObserver* observer) override; + + rtc::scoped_refptr CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies) override; + + bool Initialize(); + + RtpCapabilities GetRtpSenderCapabilities( + cricket::MediaType kind) const override; + + RtpCapabilities GetRtpReceiverCapabilities( + cricket::MediaType kind) const override; + + rtc::scoped_refptr CreateLocalMediaStream( + const std::string& stream_id) override; + + rtc::scoped_refptr CreateAudioSource( + const cricket::AudioOptions& options) override; + + rtc::scoped_refptr CreateVideoTrack( + const std::string& id, + VideoTrackSourceInterface* video_source) override; + + rtc::scoped_refptr CreateAudioTrack( + const std::string& id, + AudioSourceInterface* audio_source) override; + + bool StartAecDump(FILE* file, int64_t max_size_bytes) override; + void StopAecDump() override; + + virtual std::unique_ptr + CreateSctpTransportInternalFactory(); + + virtual cricket::ChannelManager* channel_manager(); + + rtc::Thread* signaling_thread() { + // This method can be called on a different thread when the factory is + // created in CreatePeerConnectionFactory(). + return signaling_thread_; + } + rtc::Thread* worker_thread() { return worker_thread_; } + rtc::Thread* network_thread() { return network_thread_; } + + const Options& options() const { return options_; } + + MediaTransportFactory* media_transport_factory() { + return media_transport_factory_.get(); + } + + protected: + // This structure allows simple management of all new dependencies being added + // to the PeerConnectionFactory. + explicit TgPeerConnectionFactory( + PeerConnectionFactoryDependencies dependencies); + + // Hook to let testing framework insert actions between + // "new RTCPeerConnection" and "pc.Initialize" + virtual void ActionsBeforeInitializeForTesting(PeerConnectionInterface*) {} + + virtual ~TgPeerConnectionFactory(); + + private: + bool IsTrialEnabled(absl::string_view key) const; + + std::unique_ptr CreateRtcEventLog_w(); + std::unique_ptr CreateCall_w(RtcEventLog* event_log); + + bool wraps_current_thread_; + rtc::Thread* network_thread_; + rtc::Thread* worker_thread_; + rtc::Thread* signaling_thread_; + std::unique_ptr owned_network_thread_; + std::unique_ptr owned_worker_thread_; + const std::unique_ptr task_queue_factory_; + Options options_; + std::unique_ptr channel_manager_; + std::unique_ptr default_network_manager_; + std::unique_ptr default_socket_factory_; + std::unique_ptr media_engine_; + std::unique_ptr call_factory_; + std::unique_ptr event_log_factory_; + std::unique_ptr fec_controller_factory_; + std::unique_ptr + network_state_predictor_factory_; + std::unique_ptr + injected_network_controller_factory_; + std::unique_ptr media_transport_factory_; + std::unique_ptr neteq_factory_; + const std::unique_ptr trials_; +}; + +} // namespace webrtc + +#endif // PC_PEER_CONNECTION_FACTORY_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp new file mode 100644 index 0000000000..9c95b1913f --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 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 "tg_rtp_data_engine.h" + +#include + +#include "absl/strings/match.h" +#include "media/base/codec.h" +#include "media/base/media_constants.h" +#include "media/base/rtp_utils.h" +#include "media/base/stream_params.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/data_rate_limiter.h" +#include "rtc_base/helpers.h" +#include "rtc_base/logging.h" +#include "rtc_base/sanitizer.h" + +namespace cricket { + +// We want to avoid IP fragmentation. +static const size_t kDataMaxRtpPacketLen = 1200U; +// We reserve space after the RTP header for future wiggle room. +static const unsigned char kReservedSpace[] = {0x00, 0x00, 0x00, 0x00}; + +// Amount of overhead SRTP may take. We need to leave room in the +// buffer for it, otherwise SRTP will fail later. If SRTP ever uses +// more than this, we need to increase this number. +static const size_t kMaxSrtpHmacOverhead = 16; + +TgRtpDataEngine::TgRtpDataEngine() { + data_codecs_.push_back( + DataCodec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName)); +} + +DataMediaChannel* TgRtpDataEngine::CreateChannel(const MediaConfig& config) { + return new TgRtpDataMediaChannel(config); +} + +static const DataCodec* FindCodecByName(const std::vector& codecs, + const std::string& name) { + for (const DataCodec& codec : codecs) { + if (absl::EqualsIgnoreCase(name, codec.name)) + return &codec; + } + return nullptr; +} + +TgRtpDataMediaChannel::TgRtpDataMediaChannel(const MediaConfig& config) + : DataMediaChannel(config) { + Construct(); + SetPreferredDscp(rtc::DSCP_AF41); +} + +void TgRtpDataMediaChannel::Construct() { + sending_ = false; + receiving_ = false; + send_limiter_.reset(new rtc::DataRateLimiter(kDataMaxBandwidth / 8, 1.0)); +} + +TgRtpDataMediaChannel::~TgRtpDataMediaChannel() { + std::map::const_iterator iter; + for (iter = rtp_clock_by_send_ssrc_.begin(); + iter != rtp_clock_by_send_ssrc_.end(); ++iter) { + delete iter->second; + } +} + +void RTC_NO_SANITIZE("float-cast-overflow") // bugs.webrtc.org/8204 + RtpClock::Tick(double now, int* seq_num, uint32_t* timestamp) { + *seq_num = ++last_seq_num_; + *timestamp = timestamp_offset_ + static_cast(now * clockrate_); + // UBSan: 5.92374e+10 is outside the range of representable values of type + // 'unsigned int' +} + +const DataCodec* FindUnknownCodec(const std::vector& codecs) { + DataCodec data_codec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName); + std::vector::const_iterator iter; + for (iter = codecs.begin(); iter != codecs.end(); ++iter) { + if (!iter->Matches(data_codec)) { + return &(*iter); + } + } + return NULL; +} + +const DataCodec* FindKnownCodec(const std::vector& codecs) { + DataCodec data_codec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName); + std::vector::const_iterator iter; + for (iter = codecs.begin(); iter != codecs.end(); ++iter) { + if (iter->Matches(data_codec)) { + return &(*iter); + } + } + return NULL; +} + +bool TgRtpDataMediaChannel::SetRecvCodecs(const std::vector& codecs) { + const DataCodec* unknown_codec = FindUnknownCodec(codecs); + if (unknown_codec) { + RTC_LOG(LS_WARNING) << "Failed to SetRecvCodecs because of unknown codec: " + << unknown_codec->ToString(); + return false; + } + + recv_codecs_ = codecs; + return true; +} + +bool TgRtpDataMediaChannel::SetSendCodecs(const std::vector& codecs) { + const DataCodec* known_codec = FindKnownCodec(codecs); + if (!known_codec) { + RTC_LOG(LS_WARNING) + << "Failed to SetSendCodecs because there is no known codec."; + return false; + } + + send_codecs_ = codecs; + return true; +} + +bool TgRtpDataMediaChannel::SetSendParameters(const DataSendParameters& params) { + return (SetSendCodecs(params.codecs) && + SetMaxSendBandwidth(params.max_bandwidth_bps)); +} + +bool TgRtpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) { + return SetRecvCodecs(params.codecs); +} + +bool TgRtpDataMediaChannel::AddSendStream(const StreamParams& stream) { + if (!stream.has_ssrcs()) { + return false; + } + + if (GetStreamBySsrc(send_streams_, stream.first_ssrc())) { + RTC_LOG(LS_WARNING) << "Not adding data send stream '" << stream.id + << "' with ssrc=" << stream.first_ssrc() + << " because stream already exists."; + return false; + } + + send_streams_.push_back(stream); + // TODO(pthatcher): This should be per-stream, not per-ssrc. + // And we should probably allow more than one per stream. + rtp_clock_by_send_ssrc_[stream.first_ssrc()] = + new RtpClock(kDataCodecClockrate, rtc::CreateRandomNonZeroId(), + rtc::CreateRandomNonZeroId()); + + RTC_LOG(LS_INFO) << "Added data send stream '" << stream.id + << "' with ssrc=" << stream.first_ssrc(); + return true; +} + +bool TgRtpDataMediaChannel::RemoveSendStream(uint32_t ssrc) { + if (!GetStreamBySsrc(send_streams_, ssrc)) { + return false; + } + + RemoveStreamBySsrc(&send_streams_, ssrc); + delete rtp_clock_by_send_ssrc_[ssrc]; + rtp_clock_by_send_ssrc_.erase(ssrc); + return true; +} + +bool TgRtpDataMediaChannel::AddRecvStream(const StreamParams& stream) { + if (!stream.has_ssrcs()) { + return false; + } + + if (GetStreamBySsrc(recv_streams_, stream.first_ssrc())) { + RTC_LOG(LS_WARNING) << "Not adding data recv stream '" << stream.id + << "' with ssrc=" << stream.first_ssrc() + << " because stream already exists."; + return false; + } + + recv_streams_.push_back(stream); + RTC_LOG(LS_INFO) << "Added data recv stream '" << stream.id + << "' with ssrc=" << stream.first_ssrc(); + return true; +} + +bool TgRtpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) { + RemoveStreamBySsrc(&recv_streams_, ssrc); + return true; +} + +// Not implemented. +void TgRtpDataMediaChannel::ResetUnsignaledRecvStream() {} + +void TgRtpDataMediaChannel::OnPacketReceived(rtc::CopyOnWriteBuffer packet, + int64_t /* packet_time_us */) { + RtpHeader header; + if (!GetRtpHeader(packet.cdata(), packet.size(), &header)) { + return; + } + + size_t header_length; + if (!GetRtpHeaderLen(packet.cdata(), packet.size(), &header_length)) { + return; + } + const char* data = + packet.cdata() + header_length + sizeof(kReservedSpace); + size_t data_len = packet.size() - header_length - sizeof(kReservedSpace); + + if (!receiving_) { + RTC_LOG(LS_WARNING) << "Not receiving packet " << header.ssrc << ":" + << header.seq_num << " before SetReceive(true) called."; + return; + } + + if (!FindCodecById(recv_codecs_, header.payload_type)) { + return; + } + + if (!GetStreamBySsrc(recv_streams_, header.ssrc)) { + RTC_LOG(LS_WARNING) << "Received packet for unknown ssrc: " << header.ssrc; + return; + } + + // Uncomment this for easy debugging. + // const auto* found_stream = GetStreamBySsrc(recv_streams_, header.ssrc); + // RTC_LOG(LS_INFO) << "Received packet" + // << " groupid=" << found_stream.groupid + // << ", ssrc=" << header.ssrc + // << ", seqnum=" << header.seq_num + // << ", timestamp=" << header.timestamp + // << ", len=" << data_len; + + ReceiveDataParams params; + params.ssrc = header.ssrc; + params.seq_num = header.seq_num; + params.timestamp = header.timestamp; + SignalDataReceived(params, data, data_len); +} + +bool TgRtpDataMediaChannel::SetMaxSendBandwidth(int bps) { + if (bps <= 0) { + bps = kDataMaxBandwidth; + } + send_limiter_.reset(new rtc::DataRateLimiter(bps / 8, 1.0)); + RTC_LOG(LS_INFO) << "TgRtpDataMediaChannel::SetSendBandwidth to " << bps + << "bps."; + return true; +} + +bool TgRtpDataMediaChannel::SendData(const SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + SendDataResult* result) { + if (result) { + // If we return true, we'll set this to SDR_SUCCESS. + *result = SDR_ERROR; + } + if (!sending_) { + RTC_LOG(LS_WARNING) << "Not sending packet with ssrc=" << params.ssrc + << " len=" << payload.size() + << " before SetSend(true)."; + return false; + } + + if (params.type != cricket::DMT_TEXT) { + RTC_LOG(LS_WARNING) + << "Not sending data because binary type is unsupported."; + return false; + } + + const StreamParams* found_stream = + GetStreamBySsrc(send_streams_, params.ssrc); + if (!found_stream) { + RTC_LOG(LS_WARNING) << "Not sending data because ssrc is unknown: " + << params.ssrc; + return false; + } + + const DataCodec* found_codec = + FindCodecByName(send_codecs_, kGoogleRtpDataCodecName); + if (!found_codec) { + RTC_LOG(LS_WARNING) << "Not sending data because codec is unknown: " + << kGoogleRtpDataCodecName; + return false; + } + + size_t packet_len = (kMinRtpPacketLen + sizeof(kReservedSpace) + + payload.size() + kMaxSrtpHmacOverhead); + if (packet_len > kDataMaxRtpPacketLen) { + return false; + } + + double now = + rtc::TimeMicros() / static_cast(rtc::kNumMicrosecsPerSec); + + if (!send_limiter_->CanUse(packet_len, now)) { + RTC_LOG(LS_VERBOSE) << "Dropped data packet of len=" << packet_len + << "; already sent " << send_limiter_->used_in_period() + << "/" << send_limiter_->max_per_period(); + return false; + } + + RtpHeader header; + header.payload_type = found_codec->id; + header.ssrc = params.ssrc; + rtp_clock_by_send_ssrc_[header.ssrc]->Tick(now, &header.seq_num, + &header.timestamp); + + rtc::CopyOnWriteBuffer packet(kMinRtpPacketLen, packet_len); + if (!SetRtpHeader(packet.data(), packet.size(), header)) { + return false; + } + packet.AppendData(kReservedSpace); + packet.AppendData(payload); + + RTC_LOG(LS_VERBOSE) << "Sent RTP data packet: " + << " stream=" << found_stream->id + << " ssrc=" << header.ssrc + << ", seqnum=" << header.seq_num + << ", timestamp=" << header.timestamp + << ", len=" << payload.size(); + + rtc::PacketOptions options; + options.info_signaled_after_sent.packet_type = rtc::PacketType::kData; + MediaChannel::SendPacket(&packet, options); + send_limiter_->Use(packet_len, now); + if (result) { + *result = SDR_SUCCESS; + } + return true; +} + +} // namespace cricket diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h new file mode 100644 index 0000000000..ed2a9f3dc3 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 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 TG_MEDIA_BASE_RTP_DATA_ENGINE_H_ +#define TG_MEDIA_BASE_RTP_DATA_ENGINE_H_ + +#include +#include +#include +#include + +#include "media/base/codec.h" +#include "media/base/media_channel.h" +#include "media/base/media_constants.h" +#include "media/base/media_engine.h" + +namespace rtc { +class DataRateLimiter; +} + +namespace cricket { + +class TgRtpDataEngine : public DataEngineInterface { + public: + TgRtpDataEngine(); + + virtual DataMediaChannel* CreateChannel(const MediaConfig& config); + + virtual const std::vector& data_codecs() { return data_codecs_; } + + private: + std::vector data_codecs_; +}; + +// Keep track of sequence number and timestamp of an RTP stream. The +// sequence number starts with a "random" value and increments. The +// timestamp starts with a "random" value and increases monotonically +// according to the clockrate. +class RtpClock { + public: + RtpClock(int clockrate, uint16_t first_seq_num, uint32_t timestamp_offset) + : clockrate_(clockrate), + last_seq_num_(first_seq_num), + timestamp_offset_(timestamp_offset) {} + + // Given the current time (in number of seconds which must be + // monotonically increasing), Return the next sequence number and + // timestamp. + void Tick(double now, int* seq_num, uint32_t* timestamp); + + private: + int clockrate_; + uint16_t last_seq_num_; + uint32_t timestamp_offset_; +}; + +class TgRtpDataMediaChannel : public DataMediaChannel { + public: + explicit TgRtpDataMediaChannel(const MediaConfig& config); + virtual ~TgRtpDataMediaChannel(); + + virtual bool SetSendParameters(const DataSendParameters& params); + virtual bool SetRecvParameters(const DataRecvParameters& params); + virtual bool AddSendStream(const StreamParams& sp); + virtual bool RemoveSendStream(uint32_t ssrc); + virtual bool AddRecvStream(const StreamParams& sp); + virtual bool RemoveRecvStream(uint32_t ssrc); + virtual void ResetUnsignaledRecvStream(); + virtual bool SetSend(bool send) { + sending_ = send; + return true; + } + virtual bool SetReceive(bool receive) { + receiving_ = receive; + return true; + } + virtual void OnPacketReceived(rtc::CopyOnWriteBuffer packet, + int64_t packet_time_us); + virtual void OnReadyToSend(bool ready) {} + virtual bool SendData(const SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + SendDataResult* result); + + private: + void Construct(); + bool SetMaxSendBandwidth(int bps); + bool SetSendCodecs(const std::vector& codecs); + bool SetRecvCodecs(const std::vector& codecs); + + bool sending_; + bool receiving_; + std::vector send_codecs_; + std::vector recv_codecs_; + std::vector send_streams_; + std::vector recv_streams_; + std::map rtp_clock_by_send_ssrc_; + std::unique_ptr send_limiter_; +}; + +} // namespace cricket + +#endif // MEDIA_BASE_RTP_DATA_ENGINE_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp new file mode 100644 index 0000000000..7c1e75cfb8 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp @@ -0,0 +1,292 @@ +/* + * Copyright 2017 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 "tg_rtp_transport.h" + +#include + +#include +#include + +#include "api/rtp_headers.h" +#include "api/rtp_parameters.h" +#include "media/base/rtp_utils.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/logging.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/trace_event.h" + +namespace webrtc { + +void TgRtpTransport::SetRtcpMuxEnabled(bool enable) { + rtcp_mux_enabled_ = enable; + MaybeSignalReadyToSend(); +} + +const std::string& TgRtpTransport::transport_name() const { + return rtp_packet_transport_->transport_name(); +} + +int TgRtpTransport::SetRtpOption(rtc::Socket::Option opt, int value) { + return rtp_packet_transport_->SetOption(opt, value); +} + +int TgRtpTransport::SetRtcpOption(rtc::Socket::Option opt, int value) { + if (rtcp_packet_transport_) { + return rtcp_packet_transport_->SetOption(opt, value); + } + return -1; +} + +void TgRtpTransport::SetRtpPacketTransport( + rtc::PacketTransportInternal* new_packet_transport) { + if (new_packet_transport == rtp_packet_transport_) { + return; + } + if (rtp_packet_transport_) { + rtp_packet_transport_->SignalReadyToSend.disconnect(this); + rtp_packet_transport_->SignalReadPacket.disconnect(this); + rtp_packet_transport_->SignalNetworkRouteChanged.disconnect(this); + rtp_packet_transport_->SignalWritableState.disconnect(this); + rtp_packet_transport_->SignalSentPacket.disconnect(this); + // Reset the network route of the old transport. + SignalNetworkRouteChanged(absl::optional()); + } + if (new_packet_transport) { + new_packet_transport->SignalReadyToSend.connect( + this, &TgRtpTransport::OnReadyToSend); + new_packet_transport->SignalReadPacket.connect(this, + &TgRtpTransport::OnReadPacket); + new_packet_transport->SignalNetworkRouteChanged.connect( + this, &TgRtpTransport::OnNetworkRouteChanged); + new_packet_transport->SignalWritableState.connect( + this, &TgRtpTransport::OnWritableState); + new_packet_transport->SignalSentPacket.connect(this, + &TgRtpTransport::OnSentPacket); + // Set the network route for the new transport. + SignalNetworkRouteChanged(new_packet_transport->network_route()); + } + + rtp_packet_transport_ = new_packet_transport; + // Assumes the transport is ready to send if it is writable. If we are wrong, + // ready to send will be updated the next time we try to send. + SetReadyToSend(false, + rtp_packet_transport_ && rtp_packet_transport_->writable()); +} + +void TgRtpTransport::SetRtcpPacketTransport( + rtc::PacketTransportInternal* new_packet_transport) { + if (new_packet_transport == rtcp_packet_transport_) { + return; + } + if (rtcp_packet_transport_) { + rtcp_packet_transport_->SignalReadyToSend.disconnect(this); + rtcp_packet_transport_->SignalReadPacket.disconnect(this); + rtcp_packet_transport_->SignalNetworkRouteChanged.disconnect(this); + rtcp_packet_transport_->SignalWritableState.disconnect(this); + rtcp_packet_transport_->SignalSentPacket.disconnect(this); + // Reset the network route of the old transport. + SignalNetworkRouteChanged(absl::optional()); + } + if (new_packet_transport) { + new_packet_transport->SignalReadyToSend.connect( + this, &TgRtpTransport::OnReadyToSend); + new_packet_transport->SignalReadPacket.connect(this, + &TgRtpTransport::OnReadPacket); + new_packet_transport->SignalNetworkRouteChanged.connect( + this, &TgRtpTransport::OnNetworkRouteChanged); + new_packet_transport->SignalWritableState.connect( + this, &TgRtpTransport::OnWritableState); + new_packet_transport->SignalSentPacket.connect(this, + &TgRtpTransport::OnSentPacket); + // Set the network route for the new transport. + SignalNetworkRouteChanged(new_packet_transport->network_route()); + } + rtcp_packet_transport_ = new_packet_transport; + + // Assumes the transport is ready to send if it is writable. If we are wrong, + // ready to send will be updated the next time we try to send. + SetReadyToSend(true, + rtcp_packet_transport_ && rtcp_packet_transport_->writable()); +} + +bool TgRtpTransport::IsWritable(bool rtcp) const { + rtc::PacketTransportInternal* transport = rtcp && !rtcp_mux_enabled_ + ? rtcp_packet_transport_ + : rtp_packet_transport_; + return transport && transport->writable(); +} + +bool TgRtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags) { + return SendPacket(false, packet, options, flags); +} + +bool TgRtpTransport::SendRtcpPacket(rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags) { + return SendPacket(true, packet, options, flags); +} + +bool TgRtpTransport::SendPacket(bool rtcp, + rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags) { + rtc::PacketTransportInternal* transport = rtcp && !rtcp_mux_enabled_ + ? rtcp_packet_transport_ + : rtp_packet_transport_; + int ret = transport->SendPacket(packet->cdata(), packet->size(), + options, flags); + if (ret != static_cast(packet->size())) { + if (transport->GetError() == ENOTCONN) { + RTC_LOG(LS_WARNING) << "Got ENOTCONN from transport."; + SetReadyToSend(rtcp, false); + } + return false; + } + return true; +} + +void TgRtpTransport::UpdateRtpHeaderExtensionMap( + const cricket::RtpHeaderExtensions& header_extensions) { + header_extension_map_ = RtpHeaderExtensionMap(header_extensions); +} + +bool TgRtpTransport::RegisterRtpDemuxerSink(const RtpDemuxerCriteria& criteria, + RtpPacketSinkInterface* sink) { + rtp_demuxer_.RemoveSink(sink); + if (!rtp_demuxer_.AddSink(criteria, sink)) { + RTC_LOG(LS_ERROR) << "Failed to register the sink for RTP demuxer."; + return false; + } + return true; +} + +bool TgRtpTransport::UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) { + if (!rtp_demuxer_.RemoveSink(sink)) { + RTC_LOG(LS_ERROR) << "Failed to unregister the sink for RTP demuxer."; + return false; + } + return true; +} + +void TgRtpTransport::DemuxPacket(rtc::CopyOnWriteBuffer packet, + int64_t packet_time_us) { + webrtc::RtpPacketReceived parsed_packet(&header_extension_map_); + if (!parsed_packet.Parse(std::move(packet))) { + RTC_LOG(LS_ERROR) + << "Failed to parse the incoming RTP packet before demuxing. Drop it."; + return; + } + + if (packet_time_us != -1) { + parsed_packet.set_arrival_time_ms((packet_time_us + 500) / 1000); + } + if (!rtp_demuxer_.OnRtpPacket(parsed_packet)) { + RTC_LOG(LS_WARNING) << "Failed to demux RTP packet: " + << RtpDemuxer::DescribePacket(parsed_packet); + } +} + +bool TgRtpTransport::IsTransportWritable() { + auto rtcp_packet_transport = + rtcp_mux_enabled_ ? nullptr : rtcp_packet_transport_; + return rtp_packet_transport_ && rtp_packet_transport_->writable() && + (!rtcp_packet_transport || rtcp_packet_transport->writable()); +} + +void TgRtpTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) { + SetReadyToSend(transport == rtcp_packet_transport_, true); +} + +void TgRtpTransport::OnNetworkRouteChanged( + absl::optional network_route) { + SignalNetworkRouteChanged(network_route); +} + +void TgRtpTransport::OnWritableState( + rtc::PacketTransportInternal* packet_transport) { + RTC_DCHECK(packet_transport == rtp_packet_transport_ || + packet_transport == rtcp_packet_transport_); + SignalWritableState(IsTransportWritable()); +} + +void TgRtpTransport::OnSentPacket(rtc::PacketTransportInternal* packet_transport, + const rtc::SentPacket& sent_packet) { + RTC_DCHECK(packet_transport == rtp_packet_transport_ || + packet_transport == rtcp_packet_transport_); + SignalSentPacket(sent_packet); +} + +void TgRtpTransport::OnRtpPacketReceived(rtc::CopyOnWriteBuffer packet, + int64_t packet_time_us) { + DemuxPacket(packet, packet_time_us); +} + +void TgRtpTransport::OnRtcpPacketReceived(rtc::CopyOnWriteBuffer packet, + int64_t packet_time_us) { + SignalRtcpPacketReceived(&packet, packet_time_us); +} + +void TgRtpTransport::OnReadPacket(rtc::PacketTransportInternal* transport, + const char* data, + size_t len, + const int64_t& packet_time_us, + int flags) { + TRACE_EVENT0("webrtc", "TgRtpTransport::OnReadPacket"); + + // When using RTCP multiplexing we might get RTCP packets on the RTP + // transport. We check the RTP payload type to determine if it is RTCP. + auto array_view = rtc::MakeArrayView(data, len); + cricket::RtpPacketType packet_type = cricket::InferRtpPacketType(array_view); + // Filter out the packet that is neither RTP nor RTCP. + if (packet_type == cricket::RtpPacketType::kUnknown) { + return; + } + + // Protect ourselves against crazy data. + if (!cricket::IsValidRtpPacketSize(packet_type, len)) { + RTC_LOG(LS_ERROR) << "Dropping incoming " + << cricket::RtpPacketTypeToString(packet_type) + << " packet: wrong size=" << len; + return; + } + + rtc::CopyOnWriteBuffer packet(data, len); + if (packet_type == cricket::RtpPacketType::kRtcp) { + OnRtcpPacketReceived(std::move(packet), packet_time_us); + } else { + OnRtpPacketReceived(std::move(packet), packet_time_us); + } +} + +void TgRtpTransport::SetReadyToSend(bool rtcp, bool ready) { + if (rtcp) { + rtcp_ready_to_send_ = ready; + } else { + rtp_ready_to_send_ = ready; + } + + MaybeSignalReadyToSend(); +} + +void TgRtpTransport::MaybeSignalReadyToSend() { + bool ready_to_send = + rtp_ready_to_send_ && (rtcp_ready_to_send_ || rtcp_mux_enabled_); + if (ready_to_send != ready_to_send_) { + ready_to_send_ = ready_to_send; + SignalReadyToSend(ready_to_send); + } +} + +} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h new file mode 100644 index 0000000000..591be9890a --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h @@ -0,0 +1,133 @@ +/* + * Copyright 2017 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 TG_PC_RTP_TRANSPORT_H_ +#define TG_PC_RTP_TRANSPORT_H_ + +#include + +#include "call/rtp_demuxer.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "pc/rtp_transport_internal.h" +#include "rtc_base/third_party/sigslot/sigslot.h" + +namespace rtc { + +class CopyOnWriteBuffer; +struct PacketOptions; +class PacketTransportInternal; + +} // namespace rtc + +namespace webrtc { + +class TgRtpTransport : public RtpTransportInternal { + public: + TgRtpTransport(const TgRtpTransport&) = delete; + TgRtpTransport& operator=(const TgRtpTransport&) = delete; + + explicit TgRtpTransport(bool rtcp_mux_enabled) + : rtcp_mux_enabled_(rtcp_mux_enabled) {} + + bool rtcp_mux_enabled() const override { return rtcp_mux_enabled_; } + void SetRtcpMuxEnabled(bool enable) override; + + const std::string& transport_name() const override; + + int SetRtpOption(rtc::Socket::Option opt, int value) override; + int SetRtcpOption(rtc::Socket::Option opt, int value) override; + + rtc::PacketTransportInternal* rtp_packet_transport() const { + return rtp_packet_transport_; + } + void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp); + + rtc::PacketTransportInternal* rtcp_packet_transport() const { + return rtcp_packet_transport_; + } + void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp); + + bool IsReadyToSend() const override { return ready_to_send_; } + + bool IsWritable(bool rtcp) const override; + + bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags) override; + + bool SendRtcpPacket(rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags) override; + + bool IsSrtpActive() const override { return false; } + + void UpdateRtpHeaderExtensionMap( + const cricket::RtpHeaderExtensions& header_extensions) override; + + bool RegisterRtpDemuxerSink(const RtpDemuxerCriteria& criteria, + RtpPacketSinkInterface* sink) override; + + bool UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) override; + + protected: + // These methods will be used in the subclasses. + void DemuxPacket(rtc::CopyOnWriteBuffer packet, int64_t packet_time_us); + + bool SendPacket(bool rtcp, + rtc::CopyOnWriteBuffer* packet, + const rtc::PacketOptions& options, + int flags); + + // Overridden by SrtpTransport. + virtual void OnNetworkRouteChanged( + absl::optional network_route); + virtual void OnRtpPacketReceived(rtc::CopyOnWriteBuffer packet, + int64_t packet_time_us); + virtual void OnRtcpPacketReceived(rtc::CopyOnWriteBuffer packet, + int64_t packet_time_us); + // Overridden by SrtpTransport and DtlsSrtpTransport. + virtual void OnWritableState(rtc::PacketTransportInternal* packet_transport); + + private: + void OnReadyToSend(rtc::PacketTransportInternal* transport); + void OnSentPacket(rtc::PacketTransportInternal* packet_transport, + const rtc::SentPacket& sent_packet); + void OnReadPacket(rtc::PacketTransportInternal* transport, + const char* data, + size_t len, + const int64_t& packet_time_us, + int flags); + + // Updates "ready to send" for an individual channel and fires + // SignalReadyToSend. + void SetReadyToSend(bool rtcp, bool ready); + + void MaybeSignalReadyToSend(); + + bool IsTransportWritable(); + + bool rtcp_mux_enabled_; + + rtc::PacketTransportInternal* rtp_packet_transport_ = nullptr; + rtc::PacketTransportInternal* rtcp_packet_transport_ = nullptr; + + bool ready_to_send_ = false; + bool rtp_ready_to_send_ = false; + bool rtcp_ready_to_send_ = false; + + RtpDemuxer rtp_demuxer_; + + // Used for identifying the MID for RtpDemuxer. + RtpHeaderExtensionMap header_extension_map_; +}; + +} // namespace webrtc + +#endif // PC_RTP_TRANSPORT_H_ From ee0c7896d823ef1ed0ea0ba4c42247df2d12e0d2 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Mon, 1 Jun 2020 17:36:02 +0400 Subject: [PATCH 2/8] Merge --- submodules/TelegramVoip/BUILD | 2 +- .../Sources/OngoingCallContext.swift | 46 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/submodules/TelegramVoip/BUILD b/submodules/TelegramVoip/BUILD index 64159752a3..f7a03155f3 100644 --- a/submodules/TelegramVoip/BUILD +++ b/submodules/TelegramVoip/BUILD @@ -13,7 +13,7 @@ swift_library( "//submodules/Postbox:Postbox", "//submodules/TelegramUIPreferences:TelegramUIPreferences", "//submodules/TgVoip:TgVoip", - #"//submodules/TgVoipWebrtcCustom:TgVoipWebrtcCustom", + "//submodules/TgVoipWebrtcCustom:TgVoipWebrtcCustom", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index f5adcb14eb..1dbdf317c4 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -8,7 +8,7 @@ import TelegramUIPreferences import TgVoip //import TgVoipWebrtc -//import TgVoipWebrtcCustom +import TgVoipWebrtcCustom private func callConnectionDescription(_ connection: CallSessionConnection) -> OngoingCallConnectionDescription { return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) @@ -18,9 +18,9 @@ private func callConnectionDescription(_ connection: CallSessionConnection) -> O return OngoingCallConnectionDescriptionWebrtc(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) }*/ -/*private func callConnectionDescriptionWebrtcCustom(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtcCustom { +private func callConnectionDescriptionWebrtcCustom(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtcCustom { return OngoingCallConnectionDescriptionWebrtcCustom(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) -}*/ +} private let callLogsLimit = 20 @@ -85,11 +85,11 @@ private let setupLogs: Bool = { Logger.shared.log("TGVOIP", value) } })*/ - /*OngoingCallThreadLocalContextWebrtcCustom.setupLoggingFunction({ value in + OngoingCallThreadLocalContextWebrtcCustom.setupLoggingFunction({ value in if let value = value { Logger.shared.log("TGVOIP", value) } - })*/ + }) return true }() @@ -100,7 +100,7 @@ public enum OngoingCallContextState { case failed } -private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue/*, OngoingCallThreadLocalContextQueueWebrtc,*/ /*OngoingCallThreadLocalContextQueueWebrtcCustom*/ { +private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue/*, OngoingCallThreadLocalContextQueueWebrtc*/ , OngoingCallThreadLocalContextQueueWebrtcCustom { private let queue: Queue init(queue: Queue) { @@ -164,7 +164,7 @@ private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetwor } }*/ -/*private func ongoingNetworkTypeForTypeWebrtcCustom(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtcCustom { +private func ongoingNetworkTypeForTypeWebrtcCustom(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtcCustom { switch type { case .none: return .wifi @@ -182,7 +182,7 @@ private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetwor return .cellularLte } } -}*/ +} private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCallDataSaving { switch type { @@ -210,7 +210,7 @@ private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCal } }*/ -/*private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtcCustom { +private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtcCustom { switch type { case .never: return .never @@ -221,7 +221,7 @@ private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCal default: return .never } -}*/ +} private protocol OngoingCallThreadLocalContextProtocol: class { func nativeSetNetworkType(_ type: NetworkType) @@ -292,7 +292,7 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol { } }*/ -/*extension OngoingCallThreadLocalContextWebrtcCustom: OngoingCallThreadLocalContextProtocol { +extension OngoingCallThreadLocalContextWebrtcCustom: OngoingCallThreadLocalContextProtocol { func nativeSetNetworkType(_ type: NetworkType) { self.setNetworkType(ongoingNetworkTypeForTypeWebrtcCustom(type)) } @@ -316,7 +316,7 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol { func nativeGetDerivedState() -> Data { return self.getDerivedState() } -}*/ +} private extension OngoingCallContextState { init(_ state: OngoingCallState) { @@ -352,7 +352,7 @@ private extension OngoingCallContextState { } }*/ -/*private extension OngoingCallContextState { +private extension OngoingCallContextState { init(_ state: OngoingCallStateWebrtcCustom) { switch state { case .initializing: @@ -367,7 +367,7 @@ private extension OngoingCallContextState { self = .failed } } -}*/ +} public final class OngoingCallContext { public let internalId: CallSessionInternalId @@ -400,9 +400,9 @@ public final class OngoingCallContext { public static func versions(includeExperimental: Bool) -> [String] { var result: [String] = [OngoingCallThreadLocalContext.version()] - /*if includeExperimental { + if includeExperimental { result.append(OngoingCallThreadLocalContextWebrtcCustom.version()) - }*/ + } return result } @@ -425,7 +425,7 @@ public final class OngoingCallContext { |> take(1) |> deliverOn(queue)).start(next: { [weak self] _ in if let strongSelf = self { - /*if version == OngoingCallThreadLocalContextWebrtcCustom.version() { + if version == OngoingCallThreadLocalContextWebrtcCustom.version() { var voipProxyServer: VoipProxyServerWebrtcCustom? if let proxyServer = proxyServer { switch proxyServer.connection { @@ -453,7 +453,7 @@ public final class OngoingCallContext { context.nativeSetNetworkType(networkType) } }) - }*//* else if version == OngoingCallThreadLocalContextWebrtc.version() { + }/* else if version == OngoingCallThreadLocalContextWebrtc.version() { var voipProxyServer: VoipProxyServerWebrtc? if let proxyServer = proxyServer { switch proxyServer.connection { @@ -479,7 +479,7 @@ public final class OngoingCallContext { context.nativeSetNetworkType(networkType) } }) - }*/ do { + }*/ else { var voipProxyServer: VoipProxyServer? if let proxyServer = proxyServer { switch proxyServer.connection { @@ -512,9 +512,9 @@ public final class OngoingCallContext { self.signalingDataDisposable = (callSessionManager.callSignalingData(internalId: internalId) |> deliverOn(self.queue)).start(next: { [weak self] data in self?.withContext { context in - /*if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { + if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { context.receiveSignaling(data) - }*/ + } } }) } @@ -584,9 +584,9 @@ public final class OngoingCallContext { public func getVideoView(completion: @escaping (UIView?) -> Void) { self.withContext { context in - /*if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { + if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { context.getRemoteCameraView(completion) - }*/ + } completion(nil) } } From 5f9565853d614e9d5419b1b9c4d67e36ea5dbdcc Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 2 Jun 2020 11:01:57 +0400 Subject: [PATCH 3/8] Temp --- .../Sources/RtcConnection.mm | 4 +- .../Sources/tg_peer_connection.cpp | 11478 ++++++++-------- .../Sources/tg_peer_connection.h | 213 +- .../Sources/tg_peer_connection_factory.cpp | 28 +- .../Sources/tg_peer_connection_factory.h | 33 +- .../Sources/tg_rtp_sender.cpp | 625 + .../Sources/tg_rtp_sender.h | 344 + .../tg_webrtc_session_description_factory.cpp | 501 + .../tg_webrtc_session_description_factory.h | 167 + 9 files changed, 7309 insertions(+), 6084 deletions(-) create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h diff --git a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm index b279ccde2b..0532482146 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm +++ b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm @@ -183,7 +183,7 @@ public: std::unique_ptr _networkThread; std::unique_ptr _workerThread; std::unique_ptr _signalingThread; - rtc::scoped_refptr _nativeFactory; + rtc::scoped_refptr _nativeFactory; std::unique_ptr _observer; rtc::scoped_refptr _peerConnection; @@ -255,7 +255,7 @@ public: if (!result) { return nil; } - _nativeFactory = webrtc::PeerConnectionFactoryProxy::Create(pc_factory->signaling_thread(), pc_factory); + _nativeFactory = pc_factory; webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp index bad3bcd2af..c10344fe7a 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp @@ -65,6 +65,8 @@ #include "tg_jsep_transport.h" #include "tg_jsep_transport_controller.h" +#include "tg_rtp_sender.h" + using cricket::ContentInfo; using cricket::ContentInfos; using cricket::MediaContentDescription; @@ -87,27 +89,27 @@ namespace webrtc { // Error messages const char kBundleWithoutRtcpMux[] = - "rtcp-mux must be enabled when BUNDLE " - "is enabled."; +"rtcp-mux must be enabled when BUNDLE " +"is enabled."; 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."; +"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."; +"The order of m-lines in subsequent offer doesn't match order from " +"previous offer/answer."; const char kSdpWithoutDtlsFingerprint[] = - "Called with SDP without DTLS fingerprint."; +"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."; +"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."; +"Couldn't set up DTLS-SRTP on RTP channel."; const char kDtlsSrtpSetupFailureRtcp[] = - "Couldn't set up DTLS-SRTP on RTCP channel."; +"Couldn't set up DTLS-SRTP on RTCP channel."; namespace { @@ -116,15 +118,15 @@ namespace { const char kDatagramTransportFieldTrial[] = "WebRTC-DatagramTransport"; // Controls datagram transport data channel support. const char kDatagramTransportDataChannelFieldTrial[] = - "WebRTC-DatagramTransportDataChannels"; +"WebRTC-DatagramTransportDataChannels"; // UMA metric names. const char kSimulcastVersionApplyLocalDescription[] = - "WebRTC.PeerConnection.Simulcast.ApplyLocalDescription"; +"WebRTC.PeerConnection.Simulcast.ApplyLocalDescription"; const char kSimulcastVersionApplyRemoteDescription[] = - "WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription"; +"WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription"; const char kSimulcastNumberOfEncodings[] = - "WebRTC.PeerConnection.Simulcast.NumberOfSendEncodings"; +"WebRTC.PeerConnection.Simulcast.NumberOfSendEncodings"; const char kSimulcastDisabled[] = "WebRTC.PeerConnection.Simulcast.Disabled"; static const char kDefaultStreamId[] = "default"; @@ -135,224 +137,178 @@ static const char kDefaultVideoSenderId[] = "defaultv0"; static const int kRtcpCnameLength = 16; enum { - MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0, - MSG_SET_SESSIONDESCRIPTION_FAILED, - MSG_CREATE_SESSIONDESCRIPTION_FAILED, - MSG_GETSTATS, - MSG_REPORT_USAGE_PATTERN, + MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0, + MSG_SET_SESSIONDESCRIPTION_FAILED, + MSG_CREATE_SESSIONDESCRIPTION_FAILED, + MSG_REPORT_USAGE_PATTERN, }; static const int REPORT_USAGE_PATTERN_DELAY_MS = 60000; struct SetSessionDescriptionMsg : public rtc::MessageData { - explicit SetSessionDescriptionMsg( - webrtc::SetSessionDescriptionObserver* observer) - : observer(observer) {} - - rtc::scoped_refptr observer; - RTCError error; + explicit SetSessionDescriptionMsg( + webrtc::SetSessionDescriptionObserver* observer) + : observer(observer) {} + + rtc::scoped_refptr observer; + RTCError error; }; struct CreateSessionDescriptionMsg : public rtc::MessageData { - explicit CreateSessionDescriptionMsg( - webrtc::CreateSessionDescriptionObserver* observer) - : observer(observer) {} - - rtc::scoped_refptr observer; - RTCError error; + explicit CreateSessionDescriptionMsg( + webrtc::CreateSessionDescriptionObserver* observer) + : observer(observer) {} + + rtc::scoped_refptr observer; + RTCError error; }; struct GetStatsMsg : public rtc::MessageData { - GetStatsMsg(webrtc::StatsObserver* observer, - webrtc::MediaStreamTrackInterface* track) - : observer(observer), track(track) {} - rtc::scoped_refptr observer; - rtc::scoped_refptr track; + GetStatsMsg(webrtc::StatsObserver* observer, + webrtc::MediaStreamTrackInterface* track) + : observer(observer), track(track) {} + rtc::scoped_refptr observer; + rtc::scoped_refptr track; }; // Check if we can send |new_stream| on a PeerConnection. bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams, webrtc::MediaStreamInterface* new_stream) { - if (!new_stream || !current_streams) { - return false; - } - if (current_streams->find(new_stream->id()) != nullptr) { - RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id() - << " is already added."; - return false; - } - return true; + if (!new_stream || !current_streams) { + return false; + } + if (current_streams->find(new_stream->id()) != nullptr) { + RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id() + << " is already added."; + return false; + } + return true; } // If the direction is "recvonly" or "inactive", treat the description // as containing no streams. // See: https://code.google.com/p/webrtc/issues/detail?id=5054 std::vector GetActiveStreams( - const cricket::MediaContentDescription* desc) { - return RtpTransceiverDirectionHasSend(desc->direction()) - ? desc->streams() - : std::vector(); + const cricket::MediaContentDescription* desc) { + return RtpTransceiverDirectionHasSend(desc->direction()) + ? desc->streams() + : std::vector(); } bool IsValidOfferToReceiveMedia(int value) { - typedef PeerConnectionInterface::RTCOfferAnswerOptions Options; - return (value >= Options::kUndefined) && - (value <= Options::kMaxOfferToReceiveMedia); -} - -// Add options to |[audio/video]_media_description_options| from |senders|. -void AddPlanBRtpSenderOptions( - const std::vector>>& senders, - cricket::MediaDescriptionOptions* audio_media_description_options, - cricket::MediaDescriptionOptions* video_media_description_options, - int num_sim_layers) { - for (const auto& sender : senders) { - if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { - if (audio_media_description_options) { - audio_media_description_options->AddAudioSender( - sender->id(), sender->internal()->stream_ids()); - } - } else { - RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO); - if (video_media_description_options) { - video_media_description_options->AddVideoSender( - sender->id(), sender->internal()->stream_ids(), {}, - SimulcastLayerList(), num_sim_layers); - } - } - } -} - -// Add options to |session_options| from |rtp_data_channels|. -void AddRtpDataChannelOptions( - const std::map>& - rtp_data_channels, - cricket::MediaDescriptionOptions* data_media_description_options) { - if (!data_media_description_options) { - return; - } - // Check for data channels. - for (const auto& kv : rtp_data_channels) { - const DataChannel* channel = kv.second; - if (channel->state() == DataChannel::kConnecting || - channel->state() == DataChannel::kOpen) { - // Legacy RTP data channels are signaled with the track/stream ID set to - // the data channel's label. - data_media_description_options->AddRtpDataChannel(channel->label(), - channel->label()); - } - } + typedef PeerConnectionInterface::RTCOfferAnswerOptions Options; + return (value >= Options::kUndefined) && + (value <= Options::kMaxOfferToReceiveMedia); } uint32_t ConvertIceTransportTypeToCandidateFilter( - PeerConnectionInterface::IceTransportsType type) { - switch (type) { - case PeerConnectionInterface::kNone: - return cricket::CF_NONE; - case PeerConnectionInterface::kRelay: - return cricket::CF_RELAY; - case PeerConnectionInterface::kNoHost: - return (cricket::CF_ALL & ~cricket::CF_HOST); - case PeerConnectionInterface::kAll: - return cricket::CF_ALL; - default: - RTC_NOTREACHED(); - } - return cricket::CF_NONE; + PeerConnectionInterface::IceTransportsType type) { + switch (type) { + case PeerConnectionInterface::kNone: + return cricket::CF_NONE; + case PeerConnectionInterface::kRelay: + return cricket::CF_RELAY; + case PeerConnectionInterface::kNoHost: + return (cricket::CF_ALL & ~cricket::CF_HOST); + case PeerConnectionInterface::kAll: + return cricket::CF_ALL; + default: + RTC_NOTREACHED(); + } + return cricket::CF_NONE; } std::string GetSignalingStateString( - PeerConnectionInterface::SignalingState state) { - switch (state) { - case PeerConnectionInterface::kStable: - return "kStable"; - case PeerConnectionInterface::kHaveLocalOffer: - return "kHaveLocalOffer"; - case PeerConnectionInterface::kHaveLocalPrAnswer: - return "kHavePrAnswer"; - case PeerConnectionInterface::kHaveRemoteOffer: - return "kHaveRemoteOffer"; - case PeerConnectionInterface::kHaveRemotePrAnswer: - return "kHaveRemotePrAnswer"; - case PeerConnectionInterface::kClosed: - return "kClosed"; - } - RTC_NOTREACHED(); - return ""; + PeerConnectionInterface::SignalingState state) { + switch (state) { + case PeerConnectionInterface::kStable: + return "kStable"; + case PeerConnectionInterface::kHaveLocalOffer: + return "kHaveLocalOffer"; + case PeerConnectionInterface::kHaveLocalPrAnswer: + return "kHavePrAnswer"; + case PeerConnectionInterface::kHaveRemoteOffer: + return "kHaveRemoteOffer"; + case PeerConnectionInterface::kHaveRemotePrAnswer: + return "kHaveRemotePrAnswer"; + case PeerConnectionInterface::kClosed: + return "kClosed"; + } + RTC_NOTREACHED(); + return ""; } 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_hostname = + 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_hostname = !local.address().hostname().empty() && local.address().IsUnresolvedIP(); - bool remote_hostname = !remote.address().hostname().empty() && - remote.address().IsUnresolvedIP(); - bool local_private = IPIsPrivate(local.address().ipaddr()); - bool remote_private = IPIsPrivate(remote.address().ipaddr()); - if (local_hostname) { - if (remote_hostname) { - return kIceCandidatePairHostNameHostName; - } else if (remote_private) { - return kIceCandidatePairHostNameHostPrivate; - } else { - return kIceCandidatePairHostNameHostPublic; - } - } else if (local_private) { - if (remote_hostname) { - return kIceCandidatePairHostPrivateHostName; - } else if (remote_private) { - return kIceCandidatePairHostPrivateHostPrivate; - } else { - return kIceCandidatePairHostPrivateHostPublic; - } - } else { - if (remote_hostname) { - return kIceCandidatePairHostPublicHostName; - } else if (remote_private) { - return kIceCandidatePairHostPublicHostPrivate; - } else { - return kIceCandidatePairHostPublicHostPublic; - } + bool remote_hostname = !remote.address().hostname().empty() && + remote.address().IsUnresolvedIP(); + bool local_private = IPIsPrivate(local.address().ipaddr()); + bool remote_private = IPIsPrivate(remote.address().ipaddr()); + if (local_hostname) { + if (remote_hostname) { + return kIceCandidatePairHostNameHostName; + } else if (remote_private) { + return kIceCandidatePairHostNameHostPrivate; + } else { + return kIceCandidatePairHostNameHostPublic; + } + } else if (local_private) { + if (remote_hostname) { + return kIceCandidatePairHostPrivateHostName; + } else if (remote_private) { + return kIceCandidatePairHostPrivateHostPrivate; + } else { + return kIceCandidatePairHostPrivateHostPublic; + } + } else { + if (remote_hostname) { + return kIceCandidatePairHostPublicHostName; + } 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; + 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; } // Logic to decide if an m= section can be recycled. This means that the new @@ -365,9 +321,9 @@ bool IsMediaSectionBeingRecycled(SdpType type, const ContentInfo& content, const ContentInfo* old_content_one, const ContentInfo* old_content_two) { - return type == SdpType::kOffer && !content.rejected && - ((old_content_one && old_content_one->rejected) || - (old_content_two && old_content_two->rejected)); + return type == SdpType::kOffer && !content.rejected && + ((old_content_one && old_content_one->rejected) || + (old_content_two && old_content_two->rejected)); } // Verify that the order of media sections in |new_desc| matches @@ -383,81 +339,81 @@ bool MediaSectionsInSameOrder(const SessionDescription& current_desc, const SessionDescription* secondary_current_desc, const SessionDescription& new_desc, const SdpType type) { - if (current_desc.contents().size() > new_desc.contents().size()) { - return false; - } - - for (size_t i = 0; i < current_desc.contents().size(); ++i) { - const cricket::ContentInfo* secondary_content_info = nullptr; - if (secondary_current_desc && - i < secondary_current_desc->contents().size()) { - secondary_content_info = &secondary_current_desc->contents()[i]; + if (current_desc.contents().size() > new_desc.contents().size()) { + return false; } - if (IsMediaSectionBeingRecycled(type, new_desc.contents()[i], - ¤t_desc.contents()[i], - secondary_content_info)) { - // For new offer descriptions, if the media section can be recycled, it's - // valid for the MID and media type to change. - continue; - } - if (new_desc.contents()[i].name != current_desc.contents()[i].name) { - return false; - } - const MediaContentDescription* new_desc_mdesc = + + for (size_t i = 0; i < current_desc.contents().size(); ++i) { + const cricket::ContentInfo* secondary_content_info = nullptr; + if (secondary_current_desc && + i < secondary_current_desc->contents().size()) { + secondary_content_info = &secondary_current_desc->contents()[i]; + } + if (IsMediaSectionBeingRecycled(type, new_desc.contents()[i], + ¤t_desc.contents()[i], + secondary_content_info)) { + // For new offer descriptions, if the media section can be recycled, it's + // valid for the MID and media type to change. + continue; + } + if (new_desc.contents()[i].name != current_desc.contents()[i].name) { + return false; + } + const MediaContentDescription* new_desc_mdesc = new_desc.contents()[i].media_description(); - const MediaContentDescription* current_desc_mdesc = + const MediaContentDescription* current_desc_mdesc = current_desc.contents()[i].media_description(); - if (new_desc_mdesc->type() != current_desc_mdesc->type()) { - return false; + if (new_desc_mdesc->type() != current_desc_mdesc->type()) { + return false; + } } - } - return true; + return true; } bool MediaSectionsHaveSameCount(const SessionDescription& desc1, const SessionDescription& desc2) { - return desc1.contents().size() == desc2.contents().size(); + return desc1.contents().size() == desc2.contents().size(); } void NoteKeyProtocolAndMedia(KeyExchangeProtocolType protocol_type, cricket::MediaType media_type) { - // Array of structs needed to map {KeyExchangeProtocolType, - // cricket::MediaType} to KeyExchangeProtocolMedia without using std::map in - // order to avoid -Wglobal-constructors and -Wexit-time-destructors. - static constexpr struct { - KeyExchangeProtocolType protocol_type; - cricket::MediaType media_type; - KeyExchangeProtocolMedia protocol_media; - } kEnumCounterKeyProtocolMediaMap[] = { - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_AUDIO, - kEnumCounterKeyProtocolMediaTypeDtlsAudio}, - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_VIDEO, - kEnumCounterKeyProtocolMediaTypeDtlsVideo}, - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_DATA, - kEnumCounterKeyProtocolMediaTypeDtlsData}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_AUDIO, - kEnumCounterKeyProtocolMediaTypeSdesAudio}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_VIDEO, - kEnumCounterKeyProtocolMediaTypeSdesVideo}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_DATA, - kEnumCounterKeyProtocolMediaTypeSdesData}, - }; - - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocol", protocol_type, - kEnumCounterKeyProtocolMax); - - for (const auto& i : kEnumCounterKeyProtocolMediaMap) { - if (i.protocol_type == protocol_type && i.media_type == media_type) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocolByMedia", - i.protocol_media, - kEnumCounterKeyProtocolMediaTypeMax); + // Array of structs needed to map {KeyExchangeProtocolType, + // cricket::MediaType} to KeyExchangeProtocolMedia without using std::map in + // order to avoid -Wglobal-constructors and -Wexit-time-destructors. + static constexpr struct { + KeyExchangeProtocolType protocol_type; + cricket::MediaType media_type; + KeyExchangeProtocolMedia protocol_media; + } kEnumCounterKeyProtocolMediaMap[] = { + {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_AUDIO, + kEnumCounterKeyProtocolMediaTypeDtlsAudio}, + {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_VIDEO, + kEnumCounterKeyProtocolMediaTypeDtlsVideo}, + {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_DATA, + kEnumCounterKeyProtocolMediaTypeDtlsData}, + {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_AUDIO, + kEnumCounterKeyProtocolMediaTypeSdesAudio}, + {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_VIDEO, + kEnumCounterKeyProtocolMediaTypeSdesVideo}, + {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_DATA, + kEnumCounterKeyProtocolMediaTypeSdesData}, + }; + + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocol", protocol_type, + kEnumCounterKeyProtocolMax); + + for (const auto& i : kEnumCounterKeyProtocolMediaMap) { + if (i.protocol_type == protocol_type && i.media_type == media_type) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocolByMedia", + i.protocol_media, + kEnumCounterKeyProtocolMediaTypeMax); + } } - } } void NoteAddIceCandidateResult(int result) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result, - kAddIceCandidateMax); + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result, + kAddIceCandidateMax); } // Checks that each non-rejected content has SDES crypto keys or a DTLS @@ -467,118 +423,118 @@ void NoteAddIceCandidateResult(int result) { // to SDES keys, will be caught in TgJsepTransport negotiation, and backstopped // by Channel's |srtp_required| check. RTCError VerifyCrypto(const SessionDescription* desc, bool dtls_enabled) { - const cricket::ContentGroup* bundle = - desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - for (const cricket::ContentInfo& content_info : desc->contents()) { - if (content_info.rejected) { - continue; + const cricket::ContentGroup* bundle = + desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + for (const cricket::ContentInfo& content_info : desc->contents()) { + if (content_info.rejected) { + continue; + } + // Note what media is used with each crypto protocol, for all sections. + NoteKeyProtocolAndMedia(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls + : webrtc::kEnumCounterKeyProtocolSdes, + content_info.media_description()->type()); + const std::string& mid = content_info.name; + if (bundle && bundle->HasContentName(mid) && + mid != *(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 = content_info.media_description(); + const TransportInfo* tinfo = desc->GetTransportInfoByName(mid); + if (!media || !tinfo) { + // Something is not right. + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); + } + if (dtls_enabled) { + if (!tinfo->description.identity_fingerprint) { + RTC_LOG(LS_WARNING) + << "Session description must have DTLS fingerprint if " + "DTLS enabled."; + return RTCError(RTCErrorType::INVALID_PARAMETER, + kSdpWithoutDtlsFingerprint); + } + } else { + if (media->cryptos().empty()) { + RTC_LOG(LS_WARNING) + << "Session description must have SDES when DTLS disabled."; + return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto); + } + } } - // Note what media is used with each crypto protocol, for all sections. - NoteKeyProtocolAndMedia(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls - : webrtc::kEnumCounterKeyProtocolSdes, - content_info.media_description()->type()); - const std::string& mid = content_info.name; - if (bundle && bundle->HasContentName(mid) && - mid != *(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 = content_info.media_description(); - const TransportInfo* tinfo = desc->GetTransportInfoByName(mid); - if (!media || !tinfo) { - // Something is not right. - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); - } - if (dtls_enabled) { - if (!tinfo->description.identity_fingerprint) { - RTC_LOG(LS_WARNING) - << "Session description must have DTLS fingerprint if " - "DTLS enabled."; - return RTCError(RTCErrorType::INVALID_PARAMETER, - kSdpWithoutDtlsFingerprint); - } - } else { - if (media->cryptos().empty()) { - RTC_LOG(LS_WARNING) - << "Session description must have SDES when DTLS disabled."; - return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto); - } - } - } - return RTCError::OK(); + return RTCError::OK(); } // 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); - for (const cricket::ContentInfo& content_info : desc->contents()) { - if (content_info.rejected) { - continue; + const cricket::ContentGroup* bundle = + desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + for (const cricket::ContentInfo& content_info : desc->contents()) { + if (content_info.rejected) { + continue; + } + const std::string& mid = content_info.name; + if (bundle && bundle->HasContentName(mid) && + mid != *(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(mid); + if (!tinfo) { + // Something is not right. + RTC_LOG(LS_ERROR) << kInvalidSdp; + return false; + } + if (tinfo->description.ice_ufrag.empty() || + tinfo->description.ice_pwd.empty()) { + RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; + return false; + } } - const std::string& mid = content_info.name; - if (bundle && bundle->HasContentName(mid) && - mid != *(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(mid); - if (!tinfo) { - // Something is not right. - RTC_LOG(LS_ERROR) << kInvalidSdp; - return false; - } - if (tinfo->description.ice_ufrag.empty() || - tinfo->description.ice_pwd.empty()) { - RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; - return false; - } - } - return true; + return true; } // 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) { + 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)) { + RTC_LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name + << "."; + return true; + } 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)) { - RTC_LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name - << "."; - return true; - } - return false; } // Generates a string error message for SetLocalDescription/SetRemoteDescription @@ -586,112 +542,112 @@ bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc, std::string GetSetDescriptionErrorMessage(cricket::ContentSource source, SdpType type, const RTCError& error) { - rtc::StringBuilder oss; - oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote") - << " " << SdpTypeToString(type) << " sdp: " << error.message(); - return oss.Release(); + rtc::StringBuilder oss; + oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote") + << " " << SdpTypeToString(type) << " sdp: " << error.message(); + return oss.Release(); } std::string GetStreamIdsString(rtc::ArrayView stream_ids) { - std::string output = "streams=["; - const char* separator = ""; - for (const auto& stream_id : stream_ids) { - output.append(separator).append(stream_id); - separator = ", "; - } - output.append("]"); - return output; + std::string output = "streams=["; + const char* separator = ""; + for (const auto& stream_id : stream_ids) { + output.append(separator).append(stream_id); + separator = ", "; + } + output.append("]"); + return output; } absl::optional RTCConfigurationToIceConfigOptionalInt( - int rtc_configuration_parameter) { - if (rtc_configuration_parameter == - webrtc::PeerConnectionInterface::RTCConfiguration::kUndefined) { - return absl::nullopt; - } - return rtc_configuration_parameter; + int rtc_configuration_parameter) { + if (rtc_configuration_parameter == + webrtc::PeerConnectionInterface::RTCConfiguration::kUndefined) { + return absl::nullopt; + } + return rtc_configuration_parameter; } void ReportSimulcastApiVersion(const char* name, const SessionDescription& session) { - bool has_legacy = false; - bool has_spec_compliant = false; - for (const ContentInfo& content : session.contents()) { - if (!content.media_description()) { - continue; + bool has_legacy = false; + bool has_spec_compliant = false; + for (const ContentInfo& content : session.contents()) { + if (!content.media_description()) { + continue; + } + has_spec_compliant |= content.media_description()->HasSimulcast(); + for (const StreamParams& sp : content.media_description()->streams()) { + has_legacy |= sp.has_ssrc_group(cricket::kSimSsrcGroupSemantics); + } } - has_spec_compliant |= content.media_description()->HasSimulcast(); - for (const StreamParams& sp : content.media_description()->streams()) { - has_legacy |= sp.has_ssrc_group(cricket::kSimSsrcGroupSemantics); + + if (has_legacy) { + RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionLegacy, + kSimulcastApiVersionMax); + } + if (has_spec_compliant) { + RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionSpecCompliant, + kSimulcastApiVersionMax); + } + if (!has_legacy && !has_spec_compliant) { + RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionNone, + kSimulcastApiVersionMax); } - } - - if (has_legacy) { - RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionLegacy, - kSimulcastApiVersionMax); - } - if (has_spec_compliant) { - RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionSpecCompliant, - kSimulcastApiVersionMax); - } - if (!has_legacy && !has_spec_compliant) { - RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionNone, - kSimulcastApiVersionMax); - } } const ContentInfo* FindTransceiverMSection( - RtpTransceiverProxyWithInternal* transceiver, - const SessionDescriptionInterface* session_description) { - return transceiver->mid() - ? session_description->description()->GetContentByName( - *transceiver->mid()) - : nullptr; + RtpTransceiverProxyWithInternal* transceiver, + const SessionDescriptionInterface* session_description) { + return transceiver->mid() + ? session_description->description()->GetContentByName( + *transceiver->mid()) + : nullptr; } // Wraps a CreateSessionDescriptionObserver and an OperationsChain operation // complete callback. When the observer is invoked, the wrapped observer is // invoked followed by invoking the completion callback. class CreateSessionDescriptionObserverOperationWrapper - : public CreateSessionDescriptionObserver { - public: - CreateSessionDescriptionObserverOperationWrapper( - rtc::scoped_refptr observer, - std::function operation_complete_callback) - : observer_(std::move(observer)), - operation_complete_callback_(std::move(operation_complete_callback)) { - RTC_DCHECK(observer_); - } - ~CreateSessionDescriptionObserverOperationWrapper() override { - RTC_DCHECK(was_called_); - } - - void OnSuccess(SessionDescriptionInterface* desc) override { - RTC_DCHECK(!was_called_); +: public CreateSessionDescriptionObserver { +public: + CreateSessionDescriptionObserverOperationWrapper( + rtc::scoped_refptr observer, + std::function operation_complete_callback) + : observer_(std::move(observer)), + operation_complete_callback_(std::move(operation_complete_callback)) { + RTC_DCHECK(observer_); + } + ~CreateSessionDescriptionObserverOperationWrapper() override { + RTC_DCHECK(was_called_); + } + + void OnSuccess(SessionDescriptionInterface* desc) override { + RTC_DCHECK(!was_called_); #ifdef RTC_DCHECK_IS_ON - was_called_ = true; + was_called_ = true; #endif // RTC_DCHECK_IS_ON - // Completing the operation before invoking the observer allows the observer - // to execute SetLocalDescription() without delay. - operation_complete_callback_(); - observer_->OnSuccess(desc); - } - - void OnFailure(RTCError error) override { - RTC_DCHECK(!was_called_); + // Completing the operation before invoking the observer allows the observer + // to execute SetLocalDescription() without delay. + operation_complete_callback_(); + observer_->OnSuccess(desc); + } + + void OnFailure(RTCError error) override { + RTC_DCHECK(!was_called_); #ifdef RTC_DCHECK_IS_ON - was_called_ = true; + was_called_ = true; #endif // RTC_DCHECK_IS_ON - operation_complete_callback_(); - observer_->OnFailure(std::move(error)); - } - - private: + operation_complete_callback_(); + observer_->OnFailure(std::move(error)); + } + +private: #ifdef RTC_DCHECK_IS_ON - bool was_called_ = false; + bool was_called_ = false; #endif // RTC_DCHECK_IS_ON - rtc::scoped_refptr observer_; - std::function operation_complete_callback_; + rtc::scoped_refptr observer_; + std::function operation_complete_callback_; }; } // namespace @@ -705,123 +661,123 @@ class CreateSessionDescriptionObserverOperationWrapper // TODO(hbos): If/when we process SLD messages in ~PeerConnection, the // consistent thing would be to inform the observer here. class TgPeerConnection::ImplicitCreateSessionDescriptionObserver - : public CreateSessionDescriptionObserver { - public: - ImplicitCreateSessionDescriptionObserver( - rtc::WeakPtr pc, - rtc::scoped_refptr - set_local_description_observer) - : pc_(std::move(pc)), - set_local_description_observer_( - std::move(set_local_description_observer)) {} - ~ImplicitCreateSessionDescriptionObserver() override { - RTC_DCHECK(was_called_); - } - - void SetOperationCompleteCallback( - std::function operation_complete_callback) { - operation_complete_callback_ = std::move(operation_complete_callback); - } - - bool was_called() const { return was_called_; } - - void OnSuccess(SessionDescriptionInterface* desc_ptr) override { - RTC_DCHECK(!was_called_); - std::unique_ptr desc(desc_ptr); - was_called_ = true; - - // Abort early if |pc_| is no longer valid. - if (!pc_) { - operation_complete_callback_(); - return; +: public CreateSessionDescriptionObserver { +public: + ImplicitCreateSessionDescriptionObserver( + rtc::WeakPtr pc, + rtc::scoped_refptr + set_local_description_observer) + : pc_(std::move(pc)), + set_local_description_observer_( + std::move(set_local_description_observer)) {} + ~ImplicitCreateSessionDescriptionObserver() override { + RTC_DCHECK(was_called_); } - // DoSetLocalDescription() is currently implemented as a synchronous - // operation but where the |set_local_description_observer_|'s callbacks are - // invoked asynchronously in a post to TgPeerConnection::OnMessage(). - pc_->DoSetLocalDescription(std::move(desc), - std::move(set_local_description_observer_)); - // For backwards-compatability reasons, we declare the operation as - // completed here (rather than in TgPeerConnection::OnMessage()). This ensures - // that subsequent offer/answer operations can start immediately (without - // waiting for OnMessage()). - operation_complete_callback_(); - } - - void OnFailure(RTCError error) override { - RTC_DCHECK(!was_called_); - was_called_ = true; - - // Abort early if |pc_| is no longer valid. - if (!pc_) { - operation_complete_callback_(); - return; + + void SetOperationCompleteCallback( + std::function operation_complete_callback) { + operation_complete_callback_ = std::move(operation_complete_callback); } - // DoSetLocalDescription() reports its failures in a post. We do the - // same thing here for consistency. - pc_->PostSetSessionDescriptionFailure( - set_local_description_observer_, - RTCError(error.type(), - std::string("SetLocalDescription failed to create " - "session description - ") + - error.message())); - operation_complete_callback_(); - } - - private: - bool was_called_ = false; - rtc::WeakPtr pc_; - rtc::scoped_refptr - set_local_description_observer_; - std::function operation_complete_callback_; + + bool was_called() const { return was_called_; } + + void OnSuccess(SessionDescriptionInterface* desc_ptr) override { + RTC_DCHECK(!was_called_); + std::unique_ptr desc(desc_ptr); + was_called_ = true; + + // Abort early if |pc_| is no longer valid. + if (!pc_) { + operation_complete_callback_(); + return; + } + // DoSetLocalDescription() is currently implemented as a synchronous + // operation but where the |set_local_description_observer_|'s callbacks are + // invoked asynchronously in a post to TgPeerConnection::OnMessage(). + pc_->DoSetLocalDescription(std::move(desc), + std::move(set_local_description_observer_)); + // For backwards-compatability reasons, we declare the operation as + // completed here (rather than in TgPeerConnection::OnMessage()). This ensures + // that subsequent offer/answer operations can start immediately (without + // waiting for OnMessage()). + operation_complete_callback_(); + } + + void OnFailure(RTCError error) override { + RTC_DCHECK(!was_called_); + was_called_ = true; + + // Abort early if |pc_| is no longer valid. + if (!pc_) { + operation_complete_callback_(); + return; + } + // DoSetLocalDescription() reports its failures in a post. We do the + // same thing here for consistency. + pc_->PostSetSessionDescriptionFailure( + set_local_description_observer_, + RTCError(error.type(), + std::string("SetLocalDescription failed to create " + "session description - ") + + error.message())); + operation_complete_callback_(); + } + +private: + bool was_called_ = false; + rtc::WeakPtr pc_; + rtc::scoped_refptr + set_local_description_observer_; + std::function operation_complete_callback_; }; class TgPeerConnection::LocalIceCredentialsToReplace { - public: - // Sets the ICE credentials that need restarting to the ICE credentials of - // the current and pending descriptions. - void SetIceCredentialsFromLocalDescriptions( - const SessionDescriptionInterface* current_local_description, - const SessionDescriptionInterface* pending_local_description) { - ice_credentials_.clear(); - if (current_local_description) { - AppendIceCredentialsFromSessionDescription(*current_local_description); +public: + // Sets the ICE credentials that need restarting to the ICE credentials of + // the current and pending descriptions. + void SetIceCredentialsFromLocalDescriptions( + const SessionDescriptionInterface* current_local_description, + const SessionDescriptionInterface* pending_local_description) { + ice_credentials_.clear(); + if (current_local_description) { + AppendIceCredentialsFromSessionDescription(*current_local_description); + } + if (pending_local_description) { + AppendIceCredentialsFromSessionDescription(*pending_local_description); + } } - if (pending_local_description) { - AppendIceCredentialsFromSessionDescription(*pending_local_description); + + void ClearIceCredentials() { ice_credentials_.clear(); } + + // Returns true if we have ICE credentials that need restarting. + bool HasIceCredentials() const { return !ice_credentials_.empty(); } + + // Returns true if |local_description| shares no ICE credentials with the + // ICE credentials that need restarting. + bool SatisfiesIceRestart( + const SessionDescriptionInterface& local_description) const { + for (const auto& transport_info : + local_description.description()->transport_infos()) { + if (ice_credentials_.find(std::make_pair( + transport_info.description.ice_ufrag, + transport_info.description.ice_pwd)) != ice_credentials_.end()) { + return false; + } + } + return true; } - } - - void ClearIceCredentials() { ice_credentials_.clear(); } - - // Returns true if we have ICE credentials that need restarting. - bool HasIceCredentials() const { return !ice_credentials_.empty(); } - - // Returns true if |local_description| shares no ICE credentials with the - // ICE credentials that need restarting. - bool SatisfiesIceRestart( - const SessionDescriptionInterface& local_description) const { - for (const auto& transport_info : - local_description.description()->transport_infos()) { - if (ice_credentials_.find(std::make_pair( - transport_info.description.ice_ufrag, - transport_info.description.ice_pwd)) != ice_credentials_.end()) { - return false; - } + +private: + void AppendIceCredentialsFromSessionDescription( + const SessionDescriptionInterface& desc) { + for (const auto& transport_info : desc.description()->transport_infos()) { + ice_credentials_.insert( + std::make_pair(transport_info.description.ice_ufrag, + transport_info.description.ice_pwd)); + } } - return true; - } - - private: - void AppendIceCredentialsFromSessionDescription( - const SessionDescriptionInterface& desc) { - for (const auto& transport_info : desc.description()->transport_infos()) { - ice_credentials_.insert( - std::make_pair(transport_info.description.ice_ufrag, - transport_info.description.ice_pwd)); - } - } - - std::set> ice_credentials_; + + std::set> ice_credentials_; }; // Upon completion, posts a task to execute the callback of the @@ -833,1587 +789,1449 @@ class TgPeerConnection::LocalIceCredentialsToReplace { // PeerConnectionInterface::SetRemoteDescription() that takes a // SetSessionDescriptionObserver as an argument. class TgPeerConnection::SetRemoteDescriptionObserverAdapter - : public rtc::RefCountedObject { - public: - SetRemoteDescriptionObserverAdapter( - rtc::scoped_refptr pc, - rtc::scoped_refptr wrapper) - : pc_(std::move(pc)), wrapper_(std::move(wrapper)) {} - - // SetRemoteDescriptionObserverInterface implementation. - void OnSetRemoteDescriptionComplete(RTCError error) override { - if (error.ok()) - pc_->PostSetSessionDescriptionSuccess(wrapper_); - else - pc_->PostSetSessionDescriptionFailure(wrapper_, std::move(error)); - } - - private: - rtc::scoped_refptr pc_; - rtc::scoped_refptr wrapper_; +: public rtc::RefCountedObject { +public: + SetRemoteDescriptionObserverAdapter( + rtc::scoped_refptr pc, + rtc::scoped_refptr wrapper) + : pc_(std::move(pc)), wrapper_(std::move(wrapper)) {} + + // SetRemoteDescriptionObserverInterface implementation. + void OnSetRemoteDescriptionComplete(RTCError error) override { + if (error.ok()) + pc_->PostSetSessionDescriptionSuccess(wrapper_); + else + pc_->PostSetSessionDescriptionFailure(wrapper_, std::move(error)); + } + +private: + rtc::scoped_refptr pc_; + rtc::scoped_refptr wrapper_; }; bool PeerConnectionInterface::RTCConfiguration::operator==( - const PeerConnectionInterface::RTCConfiguration& o) const { - // This static_assert prevents us from accidentally breaking operator==. - // Note: Order matters! Fields must be ordered the same as RTCConfiguration. - struct stuff_being_tested_for_equality { - IceServers servers; - IceTransportsType type; - BundlePolicy bundle_policy; - RtcpMuxPolicy rtcp_mux_policy; - std::vector> certificates; - int ice_candidate_pool_size; - bool disable_ipv6; - bool disable_ipv6_on_wifi; - int max_ipv6_networks; - bool disable_link_local_networks; - bool enable_rtp_data_channel; - absl::optional screencast_min_bitrate; - absl::optional combined_audio_video_bwe; - absl::optional enable_dtls_srtp; - TcpCandidatePolicy tcp_candidate_policy; - CandidateNetworkPolicy candidate_network_policy; - int audio_jitter_buffer_max_packets; - bool audio_jitter_buffer_fast_accelerate; - int audio_jitter_buffer_min_delay_ms; - bool audio_jitter_buffer_enable_rtx_handling; - int ice_connection_receiving_timeout; - int ice_backup_candidate_pair_ping_interval; - ContinualGatheringPolicy continual_gathering_policy; - bool prioritize_most_likely_ice_candidate_pairs; - struct cricket::MediaConfig media_config; - bool prune_turn_ports; - PortPrunePolicy turn_port_prune_policy; - bool presume_writable_when_fully_relayed; - bool enable_ice_renomination; - bool redetermine_role_on_ice_restart; - bool surface_ice_candidates_on_ice_transport_type_changed; - absl::optional ice_check_interval_strong_connectivity; - absl::optional ice_check_interval_weak_connectivity; - absl::optional ice_check_min_interval; - absl::optional ice_unwritable_timeout; - absl::optional ice_unwritable_min_checks; - absl::optional ice_inactive_timeout; - absl::optional stun_candidate_keepalive_interval; - absl::optional ice_regather_interval_range; - webrtc::TurnCustomizer* turn_customizer; - SdpSemantics sdp_semantics; - absl::optional network_preference; - bool active_reset_srtp_params; - bool use_media_transport; - bool use_media_transport_for_data_channels; - absl::optional use_datagram_transport; - absl::optional use_datagram_transport_for_data_channels; - absl::optional use_datagram_transport_for_data_channels_receive_only; - absl::optional crypto_options; - bool offer_extmap_allow_mixed; - std::string turn_logging_id; - bool enable_implicit_rollback; - absl::optional allow_codec_switching; - }; - static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), - "Did you add something to RTCConfiguration and forget to " - "update operator==?"); - return type == o.type && servers == o.servers && - bundle_policy == o.bundle_policy && - rtcp_mux_policy == o.rtcp_mux_policy && - tcp_candidate_policy == o.tcp_candidate_policy && - candidate_network_policy == o.candidate_network_policy && - audio_jitter_buffer_max_packets == o.audio_jitter_buffer_max_packets && - audio_jitter_buffer_fast_accelerate == - o.audio_jitter_buffer_fast_accelerate && - audio_jitter_buffer_min_delay_ms == - o.audio_jitter_buffer_min_delay_ms && - audio_jitter_buffer_enable_rtx_handling == - o.audio_jitter_buffer_enable_rtx_handling && - ice_connection_receiving_timeout == - o.ice_connection_receiving_timeout && - ice_backup_candidate_pair_ping_interval == - o.ice_backup_candidate_pair_ping_interval && - continual_gathering_policy == o.continual_gathering_policy && - certificates == o.certificates && - prioritize_most_likely_ice_candidate_pairs == - o.prioritize_most_likely_ice_candidate_pairs && - media_config == o.media_config && disable_ipv6 == o.disable_ipv6 && - disable_ipv6_on_wifi == o.disable_ipv6_on_wifi && - max_ipv6_networks == o.max_ipv6_networks && - disable_link_local_networks == o.disable_link_local_networks && - enable_rtp_data_channel == o.enable_rtp_data_channel && - screencast_min_bitrate == o.screencast_min_bitrate && - combined_audio_video_bwe == o.combined_audio_video_bwe && - enable_dtls_srtp == o.enable_dtls_srtp && - ice_candidate_pool_size == o.ice_candidate_pool_size && - prune_turn_ports == o.prune_turn_ports && - turn_port_prune_policy == o.turn_port_prune_policy && - presume_writable_when_fully_relayed == - o.presume_writable_when_fully_relayed && - enable_ice_renomination == o.enable_ice_renomination && - redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart && - surface_ice_candidates_on_ice_transport_type_changed == - o.surface_ice_candidates_on_ice_transport_type_changed && - ice_check_interval_strong_connectivity == - o.ice_check_interval_strong_connectivity && - ice_check_interval_weak_connectivity == - o.ice_check_interval_weak_connectivity && - ice_check_min_interval == o.ice_check_min_interval && - ice_unwritable_timeout == o.ice_unwritable_timeout && - ice_unwritable_min_checks == o.ice_unwritable_min_checks && - ice_inactive_timeout == o.ice_inactive_timeout && - stun_candidate_keepalive_interval == - o.stun_candidate_keepalive_interval && - ice_regather_interval_range == o.ice_regather_interval_range && - turn_customizer == o.turn_customizer && - sdp_semantics == o.sdp_semantics && - network_preference == o.network_preference && - active_reset_srtp_params == o.active_reset_srtp_params && - use_media_transport == o.use_media_transport && - use_media_transport_for_data_channels == - o.use_media_transport_for_data_channels && - use_datagram_transport == o.use_datagram_transport && - use_datagram_transport_for_data_channels == - o.use_datagram_transport_for_data_channels && - use_datagram_transport_for_data_channels_receive_only == - o.use_datagram_transport_for_data_channels_receive_only && - crypto_options == o.crypto_options && - offer_extmap_allow_mixed == o.offer_extmap_allow_mixed && - turn_logging_id == o.turn_logging_id && - enable_implicit_rollback == o.enable_implicit_rollback && - allow_codec_switching == o.allow_codec_switching; + const PeerConnectionInterface::RTCConfiguration& o) const { + // This static_assert prevents us from accidentally breaking operator==. + // Note: Order matters! Fields must be ordered the same as RTCConfiguration. + struct stuff_being_tested_for_equality { + IceServers servers; + IceTransportsType type; + BundlePolicy bundle_policy; + RtcpMuxPolicy rtcp_mux_policy; + std::vector> certificates; + int ice_candidate_pool_size; + bool disable_ipv6; + bool disable_ipv6_on_wifi; + int max_ipv6_networks; + bool disable_link_local_networks; + bool enable_rtp_data_channel; + absl::optional screencast_min_bitrate; + absl::optional combined_audio_video_bwe; + absl::optional enable_dtls_srtp; + TcpCandidatePolicy tcp_candidate_policy; + CandidateNetworkPolicy candidate_network_policy; + int audio_jitter_buffer_max_packets; + bool audio_jitter_buffer_fast_accelerate; + int audio_jitter_buffer_min_delay_ms; + bool audio_jitter_buffer_enable_rtx_handling; + int ice_connection_receiving_timeout; + int ice_backup_candidate_pair_ping_interval; + ContinualGatheringPolicy continual_gathering_policy; + bool prioritize_most_likely_ice_candidate_pairs; + struct cricket::MediaConfig media_config; + bool prune_turn_ports; + PortPrunePolicy turn_port_prune_policy; + bool presume_writable_when_fully_relayed; + bool enable_ice_renomination; + bool redetermine_role_on_ice_restart; + bool surface_ice_candidates_on_ice_transport_type_changed; + absl::optional ice_check_interval_strong_connectivity; + absl::optional ice_check_interval_weak_connectivity; + absl::optional ice_check_min_interval; + absl::optional ice_unwritable_timeout; + absl::optional ice_unwritable_min_checks; + absl::optional ice_inactive_timeout; + absl::optional stun_candidate_keepalive_interval; + absl::optional ice_regather_interval_range; + webrtc::TurnCustomizer* turn_customizer; + SdpSemantics sdp_semantics; + absl::optional network_preference; + bool active_reset_srtp_params; + bool use_media_transport; + bool use_media_transport_for_data_channels; + absl::optional use_datagram_transport; + absl::optional use_datagram_transport_for_data_channels; + absl::optional use_datagram_transport_for_data_channels_receive_only; + absl::optional crypto_options; + bool offer_extmap_allow_mixed; + std::string turn_logging_id; + bool enable_implicit_rollback; + absl::optional allow_codec_switching; + }; + static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), + "Did you add something to RTCConfiguration and forget to " + "update operator==?"); + return type == o.type && servers == o.servers && + bundle_policy == o.bundle_policy && + rtcp_mux_policy == o.rtcp_mux_policy && + tcp_candidate_policy == o.tcp_candidate_policy && + candidate_network_policy == o.candidate_network_policy && + audio_jitter_buffer_max_packets == o.audio_jitter_buffer_max_packets && + audio_jitter_buffer_fast_accelerate == + o.audio_jitter_buffer_fast_accelerate && + audio_jitter_buffer_min_delay_ms == + o.audio_jitter_buffer_min_delay_ms && + audio_jitter_buffer_enable_rtx_handling == + o.audio_jitter_buffer_enable_rtx_handling && + ice_connection_receiving_timeout == + o.ice_connection_receiving_timeout && + ice_backup_candidate_pair_ping_interval == + o.ice_backup_candidate_pair_ping_interval && + continual_gathering_policy == o.continual_gathering_policy && + certificates == o.certificates && + prioritize_most_likely_ice_candidate_pairs == + o.prioritize_most_likely_ice_candidate_pairs && + media_config == o.media_config && disable_ipv6 == o.disable_ipv6 && + disable_ipv6_on_wifi == o.disable_ipv6_on_wifi && + max_ipv6_networks == o.max_ipv6_networks && + disable_link_local_networks == o.disable_link_local_networks && + enable_rtp_data_channel == o.enable_rtp_data_channel && + screencast_min_bitrate == o.screencast_min_bitrate && + combined_audio_video_bwe == o.combined_audio_video_bwe && + enable_dtls_srtp == o.enable_dtls_srtp && + ice_candidate_pool_size == o.ice_candidate_pool_size && + prune_turn_ports == o.prune_turn_ports && + turn_port_prune_policy == o.turn_port_prune_policy && + presume_writable_when_fully_relayed == + o.presume_writable_when_fully_relayed && + enable_ice_renomination == o.enable_ice_renomination && + redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart && + surface_ice_candidates_on_ice_transport_type_changed == + o.surface_ice_candidates_on_ice_transport_type_changed && + ice_check_interval_strong_connectivity == + o.ice_check_interval_strong_connectivity && + ice_check_interval_weak_connectivity == + o.ice_check_interval_weak_connectivity && + ice_check_min_interval == o.ice_check_min_interval && + ice_unwritable_timeout == o.ice_unwritable_timeout && + ice_unwritable_min_checks == o.ice_unwritable_min_checks && + ice_inactive_timeout == o.ice_inactive_timeout && + stun_candidate_keepalive_interval == + o.stun_candidate_keepalive_interval && + ice_regather_interval_range == o.ice_regather_interval_range && + turn_customizer == o.turn_customizer && + sdp_semantics == o.sdp_semantics && + network_preference == o.network_preference && + active_reset_srtp_params == o.active_reset_srtp_params && + use_media_transport == o.use_media_transport && + use_media_transport_for_data_channels == + o.use_media_transport_for_data_channels && + use_datagram_transport == o.use_datagram_transport && + use_datagram_transport_for_data_channels == + o.use_datagram_transport_for_data_channels && + use_datagram_transport_for_data_channels_receive_only == + o.use_datagram_transport_for_data_channels_receive_only && + crypto_options == o.crypto_options && + offer_extmap_allow_mixed == o.offer_extmap_allow_mixed && + turn_logging_id == o.turn_logging_id && + enable_implicit_rollback == o.enable_implicit_rollback && + allow_codec_switching == o.allow_codec_switching; } bool PeerConnectionInterface::RTCConfiguration::operator!=( - const PeerConnectionInterface::RTCConfiguration& o) const { - return !(*this == o); + const PeerConnectionInterface::RTCConfiguration& o) const { + return !(*this == o); } void TgPeerConnection::TransceiverStableState::set_newly_created() { - RTC_DCHECK(!has_m_section_); - newly_created_ = true; + RTC_DCHECK(!has_m_section_); + newly_created_ = true; } void TgPeerConnection::TransceiverStableState::SetMSectionIfUnset( - absl::optional mid, - absl::optional mline_index) { - if (!has_m_section_) { - mid_ = mid; - mline_index_ = mline_index; - has_m_section_ = true; - } + absl::optional mid, + absl::optional mline_index) { + if (!has_m_section_) { + mid_ = mid; + mline_index_ = mline_index; + has_m_section_ = true; + } } void TgPeerConnection::TransceiverStableState::SetRemoteStreamIdsIfUnset( - const std::vector& ids) { - if (!remote_stream_ids_.has_value()) { - remote_stream_ids_ = ids; - } + const std::vector& ids) { + if (!remote_stream_ids_.has_value()) { + remote_stream_ids_ = ids; + } } // Generate a RTCP CNAME when a TgPeerConnection is created. std::string GenerateRtcpCname() { - std::string cname; - if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) { - RTC_LOG(LS_ERROR) << "Failed to generate CNAME."; - RTC_NOTREACHED(); - } - return cname; + std::string cname; + if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) { + RTC_LOG(LS_ERROR) << "Failed to generate CNAME."; + RTC_NOTREACHED(); + } + return cname; } bool ValidateOfferAnswerOptions( - const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) { - return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) && - IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video); + const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) { + return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) && + IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video); } // From |rtc_options|, fill parts of |session_options| shared by all generated // m= sections (in other words, nothing that involves a map/array). void ExtractSharedMediaSessionOptions( - const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options, - cricket::MediaSessionOptions* session_options) { - session_options->vad_enabled = rtc_options.voice_activity_detection; - session_options->bundle_enabled = rtc_options.use_rtp_mux; - session_options->raw_packetization_for_video = - rtc_options.raw_packetization_for_video; + const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options, + cricket::MediaSessionOptions* session_options) { + session_options->vad_enabled = rtc_options.voice_activity_detection; + session_options->bundle_enabled = rtc_options.use_rtp_mux; + session_options->raw_packetization_for_video = + rtc_options.raw_packetization_for_video; } TgPeerConnection::TgPeerConnection(TgPeerConnectionFactory* factory, - std::unique_ptr event_log, - std::unique_ptr call) - : factory_(factory), - event_log_(std::move(event_log)), - event_log_ptr_(event_log_.get()), - operations_chain_(rtc::OperationsChain::Create()), - datagram_transport_config_( - field_trial::FindFullName(kDatagramTransportFieldTrial)), - datagram_transport_data_channel_config_( - field_trial::FindFullName(kDatagramTransportDataChannelFieldTrial)), - rtcp_cname_(GenerateRtcpCname()), - local_streams_(StreamCollection::Create()), - remote_streams_(StreamCollection::Create()), - call_(std::move(call)), - call_ptr_(call_.get()), - local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()), - weak_ptr_factory_(this) {} + std::unique_ptr event_log, + std::unique_ptr call) +: factory_(factory), +event_log_(std::move(event_log)), +event_log_ptr_(event_log_.get()), +operations_chain_(rtc::OperationsChain::Create()), +datagram_transport_config_( + field_trial::FindFullName(kDatagramTransportFieldTrial)), +datagram_transport_data_channel_config_( + field_trial::FindFullName(kDatagramTransportDataChannelFieldTrial)), +rtcp_cname_(GenerateRtcpCname()), +local_streams_(StreamCollection::Create()), +remote_streams_(StreamCollection::Create()), +call_(std::move(call)), +call_ptr_(call_.get()), +local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()), +weak_ptr_factory_(this) {} TgPeerConnection::~TgPeerConnection() { - TRACE_EVENT0("webrtc", "TgPeerConnection::~TgPeerConnection"); - RTC_DCHECK_RUN_ON(signaling_thread()); - - weak_ptr_factory_.InvalidateWeakPtrs(); - - // Need to stop transceivers before destroying the stats collector because - // AudioRtpSender has a reference to the StatsCollector it will update when - // stopping. - for (const auto& transceiver : transceivers_) { - transceiver->Stop(); - } - - stats_.reset(nullptr); - if (stats_collector_) { - stats_collector_->WaitForPendingRequest(); - stats_collector_ = nullptr; - } - - // Don't destroy BaseChannels until after stats has been cleaned up so that - // the last stats request can still read from the channels. - DestroyAllChannels(); - - RTC_LOG(LS_INFO) << "Session: " << session_id() << " is destroyed."; - - webrtc_session_desc_factory_.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] { - RTC_DCHECK_RUN_ON(network_thread()); - port_allocator_.reset(); - }); - // call_ and event_log_ must be destroyed on the worker thread. - worker_thread()->Invoke(RTC_FROM_HERE, [this] { - RTC_DCHECK_RUN_ON(worker_thread()); - call_.reset(); - // The event log must outlive call (and any other object that uses it). - event_log_.reset(); - }); - - // Process all pending notifications in the message queue. If we don't do - // this, requests will linger and not know they succeeded or failed. - rtc::MessageList list; - signaling_thread()->Clear(this, rtc::MQID_ANY, &list); - for (auto& msg : list) { - if (msg.message_id == MSG_CREATE_SESSIONDESCRIPTION_FAILED) { - // Processing CreateOffer() and CreateAnswer() messages ensures their - // observers are invoked even if the TgPeerConnection is destroyed early. - OnMessage(&msg); - } else { - // TODO(hbos): Consider processing all pending messages. This would mean - // that SetLocalDescription() and SetRemoteDescription() observers are - // informed of successes and failures; this is currently NOT the case. - delete msg.pdata; + TRACE_EVENT0("webrtc", "TgPeerConnection::~TgPeerConnection"); + RTC_DCHECK_RUN_ON(signaling_thread()); + + weak_ptr_factory_.InvalidateWeakPtrs(); + + // Need to stop transceivers before destroying the stats collector because + // TgAudioRtpSender has a reference to the StatsCollector it will update when + // stopping. + for (const auto& transceiver : transceivers_) { + transceiver->Stop(); + } + + // Don't destroy BaseChannels until after stats has been cleaned up so that + // the last stats request can still read from the channels. + DestroyAllChannels(); + + RTC_LOG(LS_INFO) << "Session: " << session_id() << " is destroyed."; + + webrtc_session_desc_factory_.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] { + RTC_DCHECK_RUN_ON(network_thread()); + port_allocator_.reset(); + }); + // call_ and event_log_ must be destroyed on the worker thread. + worker_thread()->Invoke(RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(worker_thread()); + call_.reset(); + // The event log must outlive call (and any other object that uses it). + event_log_.reset(); + }); + + // Process all pending notifications in the message queue. If we don't do + // this, requests will linger and not know they succeeded or failed. + rtc::MessageList list; + signaling_thread()->Clear(this, rtc::MQID_ANY, &list); + for (auto& msg : list) { + if (msg.message_id == MSG_CREATE_SESSIONDESCRIPTION_FAILED) { + // Processing CreateOffer() and CreateAnswer() messages ensures their + // observers are invoked even if the TgPeerConnection is destroyed early. + OnMessage(&msg); + } else { + // TODO(hbos): Consider processing all pending messages. This would mean + // that SetLocalDescription() and SetRemoteDescription() observers are + // informed of successes and failures; this is currently NOT the case. + delete msg.pdata; + } } - } } void TgPeerConnection::DestroyAllChannels() { - // Destroy video channels first since they may have a pointer to a voice - // channel. - for (const auto& transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { - DestroyTransceiverChannel(transceiver); + // Destroy video channels first since they may have a pointer to a voice + // channel. + for (const auto& transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { + DestroyTransceiverChannel(transceiver); + } } - } - for (const auto& transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - DestroyTransceiverChannel(transceiver); + for (const auto& transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + DestroyTransceiverChannel(transceiver); + } } - } } bool TgPeerConnection::Initialize( - const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::Initialize"); - - RTCError config_error = ValidateConfiguration(configuration); - if (!config_error.ok()) { - RTC_LOG(LS_ERROR) << "Invalid configuration: " << config_error.message(); - return false; - } - - if (!dependencies.allocator) { - RTC_LOG(LS_ERROR) + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::Initialize"); + + RTCError config_error = ValidateConfiguration(configuration); + if (!config_error.ok()) { + RTC_LOG(LS_ERROR) << "Invalid configuration: " << config_error.message(); + return false; + } + + if (!dependencies.allocator) { + RTC_LOG(LS_ERROR) << "TgPeerConnection initialized without a PortAllocator? " - "This shouldn't happen if using PeerConnectionFactory."; - return false; - } - - if (!dependencies.observer) { - // TODO(deadbeef): Why do we do this? - RTC_LOG(LS_ERROR) << "TgPeerConnection initialized without a " - "PeerConnectionObserver"; - return false; - } - - observer_ = dependencies.observer; - async_resolver_factory_ = std::move(dependencies.async_resolver_factory); - port_allocator_ = std::move(dependencies.allocator); - ice_transport_factory_ = std::move(dependencies.ice_transport_factory); - tls_cert_verifier_ = std::move(dependencies.tls_cert_verifier); - - cricket::ServerAddresses stun_servers; - std::vector turn_servers; - - RTCErrorType parse_error = - ParseIceServers(configuration.servers, &stun_servers, &turn_servers); - if (parse_error != RTCErrorType::NONE) { - return false; - } - - // Add the turn logging id to all turn servers - for (cricket::RelayServerConfig& turn_server : turn_servers) { - turn_server.turn_logging_id = configuration.turn_logging_id; - } - - // The port allocator lives on the network thread and should be initialized - // there. - const auto pa_result = - network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::InitializePortAllocator_n, this, - stun_servers, turn_servers, configuration)); - - // If initialization was successful, note if STUN or TURN servers - // were supplied. - if (!stun_servers.empty()) { - NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); - } - if (!turn_servers.empty()) { - NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); - } - - // Send information about IPv4/IPv6 status. - PeerConnectionAddressFamilyCounter address_family; - if (pa_result.enable_ipv6) { - address_family = kPeerConnection_IPv6; - } else { - address_family = kPeerConnection_IPv4; - } - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", address_family, - kPeerConnectionAddressFamilyCounter_Max); - - const PeerConnectionFactoryInterface::Options& options = factory_->options(); - - // 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); - TgJsepTransportController::Config config; - config.redetermine_role_on_ice_restart = - configuration.redetermine_role_on_ice_restart; - config.ssl_max_version = factory_->options().ssl_max_version; - config.disable_encryption = options.disable_encryption; - config.bundle_policy = configuration.bundle_policy; - config.rtcp_mux_policy = configuration.rtcp_mux_policy; - // TODO(bugs.webrtc.org/9891) - Remove options.crypto_options then remove this - // stub. - config.crypto_options = configuration.crypto_options.has_value() - ? *configuration.crypto_options - : options.crypto_options; - config.transport_observer = this; - // It's safe to pass |this| and using |rtcp_invoker_| and the |call_| pointer - // since the TgJsepTransportController instance is owned by this PeerConnection - // instance and is destroyed before both |rtcp_invoker_| and the |call_| - // pointer. - config.rtcp_handler = [this](const rtc::CopyOnWriteBuffer& packet, - int64_t packet_time_us) { - RTC_DCHECK_RUN_ON(network_thread()); - rtcp_invoker_.AsyncInvoke( - RTC_FROM_HERE, worker_thread(), [this, packet, packet_time_us] { - RTC_DCHECK_RUN_ON(worker_thread()); - // |call_| is reset on the worker thread in the PeerConnection - // destructor, so we check that it's still valid before propagating - // the packet. - if (call_) { - call_->Receiver()->DeliverPacket(MediaType::ANY, packet, - packet_time_us); - } - }); - }; - config.event_log = event_log_ptr_; -#if defined(ENABLE_EXTERNAL_AUTH) - config.enable_external_auth = true; -#endif - config.active_reset_srtp_params = configuration.active_reset_srtp_params; - - use_datagram_transport_ = datagram_transport_config_.enabled && - configuration.use_datagram_transport.value_or( - datagram_transport_config_.default_value); - use_datagram_transport_for_data_channels_ = - datagram_transport_data_channel_config_.enabled && - configuration.use_datagram_transport_for_data_channels.value_or( - datagram_transport_data_channel_config_.default_value); - use_datagram_transport_for_data_channels_receive_only_ = - configuration.use_datagram_transport_for_data_channels_receive_only - .value_or(datagram_transport_data_channel_config_.receive_only); - if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { - if (!factory_->media_transport_factory()) { - RTC_DCHECK(false) - << "PeerConnecton is initialized with use_datagram_transport = true " - "or use_datagram_transport_for_data_channels = true " - << "but media transport factory is not set in PeerConnectionFactory"; - return false; + "This shouldn't happen if using PeerConnectionFactory."; + return false; } - - config.use_datagram_transport = use_datagram_transport_; - config.use_datagram_transport_for_data_channels = - use_datagram_transport_for_data_channels_; - config.use_datagram_transport_for_data_channels_receive_only = - use_datagram_transport_for_data_channels_receive_only_; - config.media_transport_factory = factory_->media_transport_factory(); - } - - // 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]; - } - - if (options.disable_encryption) { - dtls_enabled_ = false; - } else { - // Enable DTLS by default if we have an identity store or a certificate. - dtls_enabled_ = (dependencies.cert_generator || certificate); - // |configuration| can override the default |dtls_enabled_| value. - if (configuration.enable_dtls_srtp) { - dtls_enabled_ = *(configuration.enable_dtls_srtp); + + if (!dependencies.observer) { + // TODO(deadbeef): Why do we do this? + RTC_LOG(LS_ERROR) << "TgPeerConnection initialized without a " + "PeerConnectionObserver"; + return false; } - } - - sctp_factory_ = factory_->CreateSctpTransportInternalFactory(); - - if (use_datagram_transport_for_data_channels_) { - if (configuration.enable_rtp_data_channel) { - RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and " - "use_datagram_transport_for_data_channels are " - "incompatible and cannot both be set to true"; - return false; + + observer_ = dependencies.observer; + async_resolver_factory_ = std::move(dependencies.async_resolver_factory); + port_allocator_ = std::move(dependencies.allocator); + ice_transport_factory_ = std::move(dependencies.ice_transport_factory); + tls_cert_verifier_ = std::move(dependencies.tls_cert_verifier); + + cricket::ServerAddresses stun_servers; + std::vector turn_servers; + + RTCErrorType parse_error = + ParseIceServers(configuration.servers, &stun_servers, &turn_servers); + if (parse_error != RTCErrorType::NONE) { + return false; } - if (configuration.enable_dtls_srtp && !*configuration.enable_dtls_srtp) { + + // Add the turn logging id to all turn servers + for (cricket::RelayServerConfig& turn_server : turn_servers) { + turn_server.turn_logging_id = configuration.turn_logging_id; + } + + // The port allocator lives on the network thread and should be initialized + // there. + const auto pa_result = + network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::InitializePortAllocator_n, this, + stun_servers, turn_servers, configuration)); + + // If initialization was successful, note if STUN or TURN servers + // were supplied. + if (!stun_servers.empty()) { + NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); + } + if (!turn_servers.empty()) { + NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); + } + + // Send information about IPv4/IPv6 status. + PeerConnectionAddressFamilyCounter address_family; + if (pa_result.enable_ipv6) { + address_family = kPeerConnection_IPv6; } else { - config.sctp_factory = sctp_factory_.get(); + address_family = kPeerConnection_IPv4; } - } else if (configuration.enable_rtp_data_channel) { - // Enable creation of RTP data channels if the kEnableRtpDataChannels is - // set. It takes precendence over the disable_sctp_data_channels - // PeerConnectionFactoryInterface::Options. - } else { - // DTLS has to be enabled to use SCTP. - if (!options.disable_sctp_data_channels && dtls_enabled_) { - config.sctp_factory = sctp_factory_.get(); + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", address_family, + kPeerConnectionAddressFamilyCounter_Max); + + const PeerConnectionFactoryInterface::Options& options = factory_->options(); + + // 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); + TgJsepTransportController::Config config; + config.redetermine_role_on_ice_restart = + configuration.redetermine_role_on_ice_restart; + config.ssl_max_version = factory_->options().ssl_max_version; + config.disable_encryption = options.disable_encryption; + config.bundle_policy = configuration.bundle_policy; + config.rtcp_mux_policy = configuration.rtcp_mux_policy; + // TODO(bugs.webrtc.org/9891) - Remove options.crypto_options then remove this + // stub. + config.crypto_options = configuration.crypto_options.has_value() + ? *configuration.crypto_options + : options.crypto_options; + config.transport_observer = this; + // It's safe to pass |this| and using |rtcp_invoker_| and the |call_| pointer + // since the TgJsepTransportController instance is owned by this PeerConnection + // instance and is destroyed before both |rtcp_invoker_| and the |call_| + // pointer. + config.rtcp_handler = [this](const rtc::CopyOnWriteBuffer& packet, + int64_t packet_time_us) { + RTC_DCHECK_RUN_ON(network_thread()); + rtcp_invoker_.AsyncInvoke( + RTC_FROM_HERE, worker_thread(), [this, packet, packet_time_us] { + RTC_DCHECK_RUN_ON(worker_thread()); + // |call_| is reset on the worker thread in the PeerConnection + // destructor, so we check that it's still valid before propagating + // the packet. + if (call_) { + call_->Receiver()->DeliverPacket(MediaType::ANY, packet, + packet_time_us); + } + }); + }; + config.event_log = event_log_ptr_; +#if defined(ENABLE_EXTERNAL_AUTH) + config.enable_external_auth = true; +#endif + config.active_reset_srtp_params = configuration.active_reset_srtp_params; + + use_datagram_transport_ = datagram_transport_config_.enabled && + configuration.use_datagram_transport.value_or( + datagram_transport_config_.default_value); + use_datagram_transport_for_data_channels_ = + datagram_transport_data_channel_config_.enabled && + configuration.use_datagram_transport_for_data_channels.value_or( + datagram_transport_data_channel_config_.default_value); + use_datagram_transport_for_data_channels_receive_only_ = + configuration.use_datagram_transport_for_data_channels_receive_only + .value_or(datagram_transport_data_channel_config_.receive_only); + if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + if (!factory_->media_transport_factory()) { + RTC_DCHECK(false) + << "PeerConnecton is initialized with use_datagram_transport = true " + "or use_datagram_transport_for_data_channels = true " + << "but media transport factory is not set in PeerConnectionFactory"; + return false; + } + + config.use_datagram_transport = use_datagram_transport_; + config.use_datagram_transport_for_data_channels = + use_datagram_transport_for_data_channels_; + config.use_datagram_transport_for_data_channels_receive_only = + use_datagram_transport_for_data_channels_receive_only_; + config.media_transport_factory = factory_->media_transport_factory(); } - } - - config.ice_transport_factory = ice_transport_factory_.get(); - - transport_controller_.reset(new TgJsepTransportController( - signaling_thread(), network_thread(), port_allocator_.get(), - async_resolver_factory_.get(), config)); - transport_controller_->SignalIceConnectionState.connect( - this, &TgPeerConnection::OnTransportControllerConnectionState); - transport_controller_->SignalStandardizedIceConnectionState.connect( - this, &TgPeerConnection::SetStandardizedIceConnectionState); - transport_controller_->SignalConnectionState.connect( - this, &TgPeerConnection::SetConnectionState); - transport_controller_->SignalIceGatheringState.connect( - this, &TgPeerConnection::OnTransportControllerGatheringState); - transport_controller_->SignalIceCandidatesGathered.connect( - this, &TgPeerConnection::OnTransportControllerCandidatesGathered); - transport_controller_->SignalIceCandidateError.connect( - this, &TgPeerConnection::OnTransportControllerCandidateError); - transport_controller_->SignalIceCandidatesRemoved.connect( - this, &TgPeerConnection::OnTransportControllerCandidatesRemoved); - transport_controller_->SignalDtlsHandshakeError.connect( - this, &TgPeerConnection::OnTransportControllerDtlsHandshakeError); - transport_controller_->SignalIceCandidatePairChanged.connect( - this, &TgPeerConnection::OnTransportControllerCandidateChanged); - - stats_.reset(new StatsCollector(this)); - stats_collector_ = RTCStatsCollector::Create(this); - - configuration_ = configuration; - - transport_controller_->SetIceConfig(ParseIceConfig(configuration)); - - 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 = - configuration.audio_jitter_buffer_max_packets; - - audio_options_.audio_jitter_buffer_fast_accelerate = - configuration.audio_jitter_buffer_fast_accelerate; - - audio_options_.audio_jitter_buffer_min_delay_ms = - configuration.audio_jitter_buffer_min_delay_ms; - - audio_options_.audio_jitter_buffer_enable_rtx_handling = - configuration.audio_jitter_buffer_enable_rtx_handling; - - // 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_) { - dependencies.cert_generator.reset(); - certificate = nullptr; - } else if (certificate) { - // Favor generated certificate over the certificate generator. - dependencies.cert_generator.reset(); - } - - webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( - signaling_thread(), channel_manager(), this, session_id(), - std::move(dependencies.cert_generator), certificate, &ssrc_generator_)); - webrtc_session_desc_factory_->SignalCertificateReady.connect( - this, &TgPeerConnection::OnCertificateReady); - - if (options.disable_encryption) { - webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); - } - - webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( - GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions); - webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan()); - - // Add default audio/video transceivers for Plan B SDP. - if (!IsUnifiedPlan()) { - transceivers_.push_back( - RtpTransceiverProxyWithInternal::Create( - signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_AUDIO))); - transceivers_.push_back( - RtpTransceiverProxyWithInternal::Create( - signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO))); - } - int delay_ms = - return_histogram_very_quickly_ ? 0 : REPORT_USAGE_PATTERN_DELAY_MS; - signaling_thread()->PostDelayed(RTC_FROM_HERE, delay_ms, this, - MSG_REPORT_USAGE_PATTERN, nullptr); - - if (dependencies.video_bitrate_allocator_factory) { - video_bitrate_allocator_factory_ = + + // 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]; + } + + if (options.disable_encryption) { + dtls_enabled_ = false; + } else { + // Enable DTLS by default if we have an identity store or a certificate. + dtls_enabled_ = (dependencies.cert_generator || certificate); + // |configuration| can override the default |dtls_enabled_| value. + if (configuration.enable_dtls_srtp) { + dtls_enabled_ = *(configuration.enable_dtls_srtp); + } + } + + sctp_factory_ = factory_->CreateSctpTransportInternalFactory(); + + if (use_datagram_transport_for_data_channels_) { + if (configuration.enable_rtp_data_channel) { + RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and " + "use_datagram_transport_for_data_channels are " + "incompatible and cannot both be set to true"; + return false; + } + if (configuration.enable_dtls_srtp && !*configuration.enable_dtls_srtp) { + } else { + config.sctp_factory = sctp_factory_.get(); + } + } else if (configuration.enable_rtp_data_channel) { + // Enable creation of RTP data channels if the kEnableRtpDataChannels is + // set. It takes precendence over the disable_sctp_data_channels + // PeerConnectionFactoryInterface::Options. + } else { + // DTLS has to be enabled to use SCTP. + if (!options.disable_sctp_data_channels && dtls_enabled_) { + config.sctp_factory = sctp_factory_.get(); + } + } + + config.ice_transport_factory = ice_transport_factory_.get(); + + transport_controller_.reset(new TgJsepTransportController( + signaling_thread(), network_thread(), port_allocator_.get(), + async_resolver_factory_.get(), config)); + transport_controller_->SignalIceConnectionState.connect( + this, &TgPeerConnection::OnTransportControllerConnectionState); + transport_controller_->SignalStandardizedIceConnectionState.connect( + this, &TgPeerConnection::SetStandardizedIceConnectionState); + transport_controller_->SignalConnectionState.connect( + this, &TgPeerConnection::SetConnectionState); + transport_controller_->SignalIceGatheringState.connect( + this, &TgPeerConnection::OnTransportControllerGatheringState); + transport_controller_->SignalIceCandidatesGathered.connect( + this, &TgPeerConnection::OnTransportControllerCandidatesGathered); + transport_controller_->SignalIceCandidateError.connect( + this, &TgPeerConnection::OnTransportControllerCandidateError); + transport_controller_->SignalIceCandidatesRemoved.connect( + this, &TgPeerConnection::OnTransportControllerCandidatesRemoved); + transport_controller_->SignalDtlsHandshakeError.connect( + this, &TgPeerConnection::OnTransportControllerDtlsHandshakeError); + transport_controller_->SignalIceCandidatePairChanged.connect( + this, &TgPeerConnection::OnTransportControllerCandidateChanged); + + configuration_ = configuration; + + transport_controller_->SetIceConfig(ParseIceConfig(configuration)); + + 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 = + configuration.audio_jitter_buffer_max_packets; + + audio_options_.audio_jitter_buffer_fast_accelerate = + configuration.audio_jitter_buffer_fast_accelerate; + + audio_options_.audio_jitter_buffer_min_delay_ms = + configuration.audio_jitter_buffer_min_delay_ms; + + audio_options_.audio_jitter_buffer_enable_rtx_handling = + configuration.audio_jitter_buffer_enable_rtx_handling; + + // 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_) { + dependencies.cert_generator.reset(); + certificate = nullptr; + } else if (certificate) { + // Favor generated certificate over the certificate generator. + dependencies.cert_generator.reset(); + } + + webrtc_session_desc_factory_.reset(new TgWebRtcSessionDescriptionFactory( + signaling_thread(), channel_manager(), this, session_id(), + std::move(dependencies.cert_generator), certificate, &ssrc_generator_)); + webrtc_session_desc_factory_->SignalCertificateReady.connect( + this, &TgPeerConnection::OnCertificateReady); + + if (options.disable_encryption) { + webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); + } + + webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( + GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions); + webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan()); + + // Add default audio/video transceivers for Plan B SDP. + if (!IsUnifiedPlan()) { + transceivers_.push_back( + RtpTransceiverProxyWithInternal::Create( + signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_AUDIO))); + transceivers_.push_back( + RtpTransceiverProxyWithInternal::Create( + signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO))); + } + int delay_ms = + return_histogram_very_quickly_ ? 0 : REPORT_USAGE_PATTERN_DELAY_MS; + signaling_thread()->PostDelayed(RTC_FROM_HERE, delay_ms, this, + MSG_REPORT_USAGE_PATTERN, nullptr); + + if (dependencies.video_bitrate_allocator_factory) { + video_bitrate_allocator_factory_ = std::move(dependencies.video_bitrate_allocator_factory); - } else { - video_bitrate_allocator_factory_ = + } else { + video_bitrate_allocator_factory_ = CreateBuiltinVideoBitrateAllocatorFactory(); - } - return true; + } + return true; } RTCError TgPeerConnection::ValidateConfiguration( - const RTCConfiguration& config) const { - if (config.ice_regather_interval_range && - config.continual_gathering_policy == GATHER_ONCE) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "ice_regather_interval_range specified but continual " - "gathering policy is GATHER_ONCE"); - } - auto result = - cricket::P2PTransportChannel::ValidateIceConfig(ParseIceConfig(config)); - return result; + const PeerConnectionInterface::RTCConfiguration& config) const { + if (config.ice_regather_interval_range && + config.continual_gathering_policy == PeerConnectionInterface::GATHER_ONCE) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "ice_regather_interval_range specified but continual " + "gathering policy is GATHER_ONCE"); + } + auto result = + cricket::P2PTransportChannel::ValidateIceConfig(ParseIceConfig(config)); + return result; } rtc::scoped_refptr TgPeerConnection::local_streams() { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified " - "Plan SdpSemantics. Please use GetSenders " - "instead."; - return local_streams_; + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified " + "Plan SdpSemantics. Please use GetSenders " + "instead."; + return local_streams_; } rtc::scoped_refptr TgPeerConnection::remote_streams() { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified " - "Plan SdpSemantics. Please use GetReceivers " - "instead."; - return remote_streams_; + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified " + "Plan SdpSemantics. Please use GetReceivers " + "instead."; + return remote_streams_; } bool TgPeerConnection::AddStream(MediaStreamInterface* local_stream) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan " - "SdpSemantics. Please use AddTrack instead."; - TRACE_EVENT0("webrtc", "TgPeerConnection::AddStream"); - if (IsClosed()) { - return false; - } - if (!CanAddLocalMediaStream(local_streams_, local_stream)) { - return false; - } - - local_streams_->AddStream(local_stream); - MediaStreamObserver* observer = new MediaStreamObserver(local_stream); - observer->SignalAudioTrackAdded.connect(this, - &TgPeerConnection::OnAudioTrackAdded); - observer->SignalAudioTrackRemoved.connect( - this, &TgPeerConnection::OnAudioTrackRemoved); - observer->SignalVideoTrackAdded.connect(this, - &TgPeerConnection::OnVideoTrackAdded); - observer->SignalVideoTrackRemoved.connect( - this, &TgPeerConnection::OnVideoTrackRemoved); - stream_observers_.push_back(std::unique_ptr(observer)); - - for (const auto& track : local_stream->GetAudioTracks()) { - AddAudioTrack(track.get(), local_stream); - } - for (const auto& track : local_stream->GetVideoTracks()) { - AddVideoTrack(track.get(), local_stream); - } - - stats_->AddStream(local_stream); - UpdateNegotiationNeeded(); - return true; + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan " + "SdpSemantics. Please use AddTrack instead."; + TRACE_EVENT0("webrtc", "TgPeerConnection::AddStream"); + if (IsClosed()) { + return false; + } + if (!CanAddLocalMediaStream(local_streams_, local_stream)) { + return false; + } + + local_streams_->AddStream(local_stream); + MediaStreamObserver* observer = new MediaStreamObserver(local_stream); + observer->SignalAudioTrackAdded.connect(this, + &TgPeerConnection::OnAudioTrackAdded); + observer->SignalAudioTrackRemoved.connect( + this, &TgPeerConnection::OnAudioTrackRemoved); + observer->SignalVideoTrackAdded.connect(this, + &TgPeerConnection::OnVideoTrackAdded); + observer->SignalVideoTrackRemoved.connect( + this, &TgPeerConnection::OnVideoTrackRemoved); + stream_observers_.push_back(std::unique_ptr(observer)); + + for (const auto& track : local_stream->GetAudioTracks()) { + AddAudioTrack(track.get(), local_stream); + } + for (const auto& track : local_stream->GetVideoTracks()) { + AddVideoTrack(track.get(), local_stream); + } + + UpdateNegotiationNeeded(); + return true; } void TgPeerConnection::RemoveStream(MediaStreamInterface* local_stream) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified " - "Plan SdpSemantics. Please use RemoveTrack " - "instead."; - TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveStream"); - if (!IsClosed()) { - for (const auto& track : local_stream->GetAudioTracks()) { - RemoveAudioTrack(track.get(), local_stream); + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified " + "Plan SdpSemantics. Please use RemoveTrack " + "instead."; + TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveStream"); + if (!IsClosed()) { + for (const auto& track : local_stream->GetAudioTracks()) { + RemoveAudioTrack(track.get(), local_stream); + } + for (const auto& track : local_stream->GetVideoTracks()) { + RemoveVideoTrack(track.get(), local_stream); + } } - for (const auto& track : local_stream->GetVideoTracks()) { - RemoveVideoTrack(track.get(), local_stream); + local_streams_->RemoveStream(local_stream); + stream_observers_.erase( + std::remove_if( + stream_observers_.begin(), stream_observers_.end(), + [local_stream](const std::unique_ptr& observer) { + return observer->stream()->id().compare(local_stream->id()) == 0; + }), + stream_observers_.end()); + + if (IsClosed()) { + return; } - } - local_streams_->RemoveStream(local_stream); - stream_observers_.erase( - std::remove_if( - stream_observers_.begin(), stream_observers_.end(), - [local_stream](const std::unique_ptr& observer) { - return observer->stream()->id().compare(local_stream->id()) == 0; - }), - stream_observers_.end()); - - if (IsClosed()) { - return; - } - UpdateNegotiationNeeded(); + UpdateNegotiationNeeded(); } RTCErrorOr> TgPeerConnection::AddTrack( - rtc::scoped_refptr track, - const std::vector& stream_ids) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::AddTrack"); - if (!track) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Track is null."); - } - if (!(track->kind() == MediaStreamTrackInterface::kAudioKind || - track->kind() == MediaStreamTrackInterface::kVideoKind)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Track has invalid kind: " + track->kind()); - } - if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "TgPeerConnection is closed."); - } - if (FindSenderForTrack(track)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Sender already exists for track " + track->id() + "."); - } - auto sender_or_error = - (IsUnifiedPlan() ? AddTrackUnifiedPlan(track, stream_ids) - : AddTrackPlanB(track, stream_ids)); - if (sender_or_error.ok()) { - UpdateNegotiationNeeded(); - stats_->AddTrack(track); - } - return sender_or_error; -} - -RTCErrorOr> -TgPeerConnection::AddTrackPlanB( - rtc::scoped_refptr track, - const std::vector& stream_ids) { - if (stream_ids.size() > 1u) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "AddTrack with more than one stream is not " - "supported with Plan B semantics."); - } - std::vector adjusted_stream_ids = stream_ids; - if (adjusted_stream_ids.empty()) { - adjusted_stream_ids.push_back(rtc::CreateRandomUuid()); - } - cricket::MediaType media_type = - (track->kind() == MediaStreamTrackInterface::kAudioKind - ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO); - auto new_sender = - CreateSender(media_type, track->id(), track, adjusted_stream_ids, {}); - if (track->kind() == MediaStreamTrackInterface::kAudioKind) { - new_sender->internal()->SetMediaChannel(voice_media_channel()); - GetAudioTransceiver()->internal()->AddSender(new_sender); - const RtpSenderInfo* sender_info = - FindSenderInfo(local_audio_sender_infos_, - new_sender->internal()->stream_ids()[0], track->id()); - if (sender_info) { - new_sender->internal()->SetSsrc(sender_info->first_ssrc); + rtc::scoped_refptr track, + const std::vector& stream_ids) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::AddTrack"); + if (!track) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Track is null."); } - } else { - RTC_DCHECK_EQ(MediaStreamTrackInterface::kVideoKind, track->kind()); - new_sender->internal()->SetMediaChannel(video_media_channel()); - GetVideoTransceiver()->internal()->AddSender(new_sender); - const RtpSenderInfo* sender_info = - FindSenderInfo(local_video_sender_infos_, - new_sender->internal()->stream_ids()[0], track->id()); - if (sender_info) { - new_sender->internal()->SetSsrc(sender_info->first_ssrc); + if (!(track->kind() == MediaStreamTrackInterface::kAudioKind || + track->kind() == MediaStreamTrackInterface::kVideoKind)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Track has invalid kind: " + track->kind()); } - } - return rtc::scoped_refptr(new_sender); + if (IsClosed()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "TgPeerConnection is closed."); + } + if (FindSenderForTrack(track)) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "Sender already exists for track " + track->id() + "."); + } + auto sender_or_error = AddTrackUnifiedPlan(track, stream_ids); + if (sender_or_error.ok()) { + UpdateNegotiationNeeded(); + } + return sender_or_error; } RTCErrorOr> TgPeerConnection::AddTrackUnifiedPlan( - rtc::scoped_refptr track, - const std::vector& stream_ids) { - auto transceiver = FindFirstTransceiverForAddedTrack(track); - if (transceiver) { - RTC_LOG(LS_INFO) << "Reusing an existing " - << cricket::MediaTypeToString(transceiver->media_type()) - << " transceiver for AddTrack."; - if (transceiver->direction() == RtpTransceiverDirection::kRecvOnly) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kSendRecv); - } else if (transceiver->direction() == RtpTransceiverDirection::kInactive) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kSendOnly); - } - transceiver->sender()->SetTrack(track); - transceiver->internal()->sender_internal()->set_stream_ids(stream_ids); - transceiver->internal()->set_reused_for_addtrack(true); - } else { - cricket::MediaType media_type = + rtc::scoped_refptr track, + const std::vector& stream_ids) { + auto transceiver = FindFirstTransceiverForAddedTrack(track); + if (transceiver) { + RTC_LOG(LS_INFO) << "Reusing an existing " + << cricket::MediaTypeToString(transceiver->media_type()) + << " transceiver for AddTrack."; + if (transceiver->direction() == RtpTransceiverDirection::kRecvOnly) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kSendRecv); + } else if (transceiver->direction() == RtpTransceiverDirection::kInactive) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kSendOnly); + } + transceiver->sender()->SetTrack(track); + transceiver->internal()->sender_internal()->set_stream_ids(stream_ids); + transceiver->internal()->set_reused_for_addtrack(true); + } else { + cricket::MediaType media_type = (track->kind() == MediaStreamTrackInterface::kAudioKind - ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO); - RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) - << " transceiver in response to a call to AddTrack."; - std::string sender_id = track->id(); - // Avoid creating a sender with an existing ID by generating a random ID. - // This can happen if this is the second time AddTrack has created a sender - // for this track. - if (FindSenderById(sender_id)) { - sender_id = rtc::CreateRandomUuid(); + ? cricket::MEDIA_TYPE_AUDIO + : cricket::MEDIA_TYPE_VIDEO); + RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) + << " transceiver in response to a call to AddTrack."; + std::string sender_id = track->id(); + // Avoid creating a sender with an existing ID by generating a random ID. + // This can happen if this is the second time AddTrack has created a sender + // for this track. + if (FindSenderById(sender_id)) { + sender_id = rtc::CreateRandomUuid(); + } + auto sender = CreateSender(media_type, sender_id, track, stream_ids, {}); + auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); + transceiver = CreateAndAddTransceiver(sender, receiver); + transceiver->internal()->set_created_by_addtrack(true); + transceiver->internal()->set_direction(RtpTransceiverDirection::kSendRecv); } - auto sender = CreateSender(media_type, sender_id, track, stream_ids, {}); - auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); - transceiver = CreateAndAddTransceiver(sender, receiver); - transceiver->internal()->set_created_by_addtrack(true); - transceiver->internal()->set_direction(RtpTransceiverDirection::kSendRecv); - } - return transceiver->sender(); + return transceiver->sender(); } rtc::scoped_refptr> TgPeerConnection::FindFirstTransceiverForAddedTrack( - rtc::scoped_refptr track) { - RTC_DCHECK(track); - for (auto transceiver : transceivers_) { - if (!transceiver->sender()->track() && - cricket::MediaTypeToString(transceiver->media_type()) == + rtc::scoped_refptr track) { + RTC_DCHECK(track); + for (auto transceiver : transceivers_) { + if (!transceiver->sender()->track() && + cricket::MediaTypeToString(transceiver->media_type()) == track->kind() && - !transceiver->internal()->has_ever_been_used_to_send() && - !transceiver->stopped()) { - return transceiver; + !transceiver->internal()->has_ever_been_used_to_send() && + !transceiver->stopped()) { + return transceiver; + } } - } - return nullptr; + return nullptr; } bool TgPeerConnection::RemoveTrack(RtpSenderInterface* sender) { - TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveTrack"); - return RemoveTrackNew(sender).ok(); + TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveTrack"); + return RemoveTrackNew(sender).ok(); } RTCError TgPeerConnection::RemoveTrackNew( - rtc::scoped_refptr sender) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!sender) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Sender is null."); - } - if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "TgPeerConnection is closed."); - } - if (IsUnifiedPlan()) { - auto transceiver = FindTransceiverBySender(sender); - if (!transceiver || !sender->track()) { - return RTCError::OK(); + rtc::scoped_refptr sender) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!sender) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Sender is null."); } - sender->SetTrack(nullptr); - if (transceiver->direction() == RtpTransceiverDirection::kSendRecv) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kRecvOnly); - } else if (transceiver->direction() == RtpTransceiverDirection::kSendOnly) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kInactive); + if (IsClosed()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "TgPeerConnection is closed."); } - } else { - bool removed; - if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { - removed = GetAudioTransceiver()->internal()->RemoveSender(sender); + if (IsUnifiedPlan()) { + auto transceiver = FindTransceiverBySender(sender); + if (!transceiver || !sender->track()) { + return RTCError::OK(); + } + sender->SetTrack(nullptr); + if (transceiver->direction() == RtpTransceiverDirection::kSendRecv) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kRecvOnly); + } else if (transceiver->direction() == RtpTransceiverDirection::kSendOnly) { + transceiver->internal()->set_direction( + RtpTransceiverDirection::kInactive); + } } else { - RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, sender->media_type()); - removed = GetVideoTransceiver()->internal()->RemoveSender(sender); + bool removed; + if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { + removed = GetAudioTransceiver()->internal()->RemoveSender(sender); + } else { + RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, sender->media_type()); + removed = GetVideoTransceiver()->internal()->RemoveSender(sender); + } + if (!removed) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "Couldn't find sender " + sender->id() + " to remove."); + } } - if (!removed) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Couldn't find sender " + sender->id() + " to remove."); - } - } - UpdateNegotiationNeeded(); - return RTCError::OK(); + UpdateNegotiationNeeded(); + return RTCError::OK(); } rtc::scoped_refptr> TgPeerConnection::FindTransceiverBySender( - rtc::scoped_refptr sender) { - for (auto transceiver : transceivers_) { - if (transceiver->sender() == sender) { - return transceiver; + rtc::scoped_refptr sender) { + for (auto transceiver : transceivers_) { + if (transceiver->sender() == sender) { + return transceiver; + } } - } - return nullptr; + return nullptr; } RTCErrorOr> TgPeerConnection::AddTransceiver( - rtc::scoped_refptr track) { - return AddTransceiver(track, RtpTransceiverInit()); + rtc::scoped_refptr track) { + return AddTransceiver(track, RtpTransceiverInit()); } RTCErrorOr> TgPeerConnection::AddTransceiver( - rtc::scoped_refptr track, - const RtpTransceiverInit& init) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(IsUnifiedPlan()) - << "AddTransceiver is only available with Unified Plan SdpSemantics"; - if (!track) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "track is null"); - } - cricket::MediaType media_type; - if (track->kind() == MediaStreamTrackInterface::kAudioKind) { - media_type = cricket::MEDIA_TYPE_AUDIO; - } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) { - media_type = cricket::MEDIA_TYPE_VIDEO; - } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Track kind is not audio or video"); - } - return AddTransceiver(media_type, track, init); + rtc::scoped_refptr track, + const RtpTransceiverInit& init) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(IsUnifiedPlan()) + << "AddTransceiver is only available with Unified Plan SdpSemantics"; + if (!track) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "track is null"); + } + cricket::MediaType media_type; + if (track->kind() == MediaStreamTrackInterface::kAudioKind) { + media_type = cricket::MEDIA_TYPE_AUDIO; + } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) { + media_type = cricket::MEDIA_TYPE_VIDEO; + } else { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Track kind is not audio or video"); + } + return AddTransceiver(media_type, track, init); } RTCErrorOr> TgPeerConnection::AddTransceiver(cricket::MediaType media_type) { - return AddTransceiver(media_type, RtpTransceiverInit()); + return AddTransceiver(media_type, RtpTransceiverInit()); } RTCErrorOr> TgPeerConnection::AddTransceiver(cricket::MediaType media_type, - const RtpTransceiverInit& init) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(IsUnifiedPlan()) - << "AddTransceiver is only available with Unified Plan SdpSemantics"; - if (!(media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "media type is not audio or video"); - } - return AddTransceiver(media_type, nullptr, init); + const RtpTransceiverInit& init) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(IsUnifiedPlan()) + << "AddTransceiver is only available with Unified Plan SdpSemantics"; + if (!(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "media type is not audio or video"); + } + return AddTransceiver(media_type, nullptr, init); } RTCErrorOr> TgPeerConnection::AddTransceiver( - cricket::MediaType media_type, - rtc::scoped_refptr track, - const RtpTransceiverInit& init, - bool update_negotiation_needed) { - RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO)); - if (track) { - RTC_DCHECK_EQ(media_type, - (track->kind() == MediaStreamTrackInterface::kAudioKind + cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init, + bool update_negotiation_needed) { + RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO)); + if (track) { + RTC_DCHECK_EQ(media_type, + (track->kind() == MediaStreamTrackInterface::kAudioKind ? cricket::MEDIA_TYPE_AUDIO : cricket::MEDIA_TYPE_VIDEO)); - } - - RTC_HISTOGRAM_COUNTS_LINEAR(kSimulcastNumberOfEncodings, - (int)init.send_encodings.size(), 0, 7, 8); - - size_t num_rids = absl::c_count_if(init.send_encodings, - [](const RtpEncodingParameters& encoding) { - return !encoding.rid.empty(); - }); - if (num_rids > 0 && num_rids != init.send_encodings.size()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "RIDs must be provided for either all or none of the send encodings."); - } - - if (num_rids > 0 && absl::c_any_of(init.send_encodings, - [](const RtpEncodingParameters& encoding) { - return !IsLegalRsidName(encoding.rid); - })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Invalid RID value provided."); - } - - if (absl::c_any_of(init.send_encodings, - [](const RtpEncodingParameters& encoding) { - return encoding.ssrc.has_value(); - })) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); - } - - RtpParameters parameters; - parameters.encodings = init.send_encodings; - - // Encodings are dropped from the tail if too many are provided. - if (parameters.encodings.size() > kMaxSimulcastStreams) { - parameters.encodings.erase( - parameters.encodings.begin() + kMaxSimulcastStreams, - parameters.encodings.end()); - } - - // Single RID should be removed. - if (parameters.encodings.size() == 1 && - !parameters.encodings[0].rid.empty()) { - RTC_LOG(LS_INFO) << "Removing RID: " << parameters.encodings[0].rid << "."; - parameters.encodings[0].rid.clear(); - } - - // If RIDs were not provided, they are generated for simulcast scenario. - if (parameters.encodings.size() > 1 && num_rids == 0) { - rtc::UniqueStringGenerator rid_generator; - for (RtpEncodingParameters& encoding : parameters.encodings) { - encoding.rid = rid_generator(); } - } - - if (UnimplementedRtpParameterHasValue(parameters)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); - } - - auto result = cricket::CheckRtpParametersValues(parameters); - if (!result.ok()) { - LOG_AND_RETURN_ERROR(result.type(), result.message()); - } - - RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) - << " transceiver in response to a call to AddTransceiver."; - // Set the sender ID equal to the track ID if the track is specified unless - // that sender ID is already in use. - std::string sender_id = - (track && !FindSenderById(track->id()) ? track->id() - : rtc::CreateRandomUuid()); - auto sender = CreateSender(media_type, sender_id, track, init.stream_ids, - parameters.encodings); - auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); - auto transceiver = CreateAndAddTransceiver(sender, receiver); - transceiver->internal()->set_direction(init.direction); - - if (update_negotiation_needed) { - UpdateNegotiationNeeded(); - } - - return rtc::scoped_refptr(transceiver); + + RTC_HISTOGRAM_COUNTS_LINEAR(kSimulcastNumberOfEncodings, + (int)init.send_encodings.size(), 0, 7, 8); + + size_t num_rids = absl::c_count_if(init.send_encodings, + [](const RtpEncodingParameters& encoding) { + return !encoding.rid.empty(); + }); + if (num_rids > 0 && num_rids != init.send_encodings.size()) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "RIDs must be provided for either all or none of the send encodings."); + } + + if (num_rids > 0 && absl::c_any_of(init.send_encodings, + [](const RtpEncodingParameters& encoding) { + return !IsLegalRsidName(encoding.rid); + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Invalid RID value provided."); + } + + if (absl::c_any_of(init.send_encodings, + [](const RtpEncodingParameters& encoding) { + return encoding.ssrc.has_value(); + })) { + LOG_AND_RETURN_ERROR( + RTCErrorType::UNSUPPORTED_PARAMETER, + "Attempted to set an unimplemented parameter of RtpParameters."); + } + + RtpParameters parameters; + parameters.encodings = init.send_encodings; + + // Encodings are dropped from the tail if too many are provided. + if (parameters.encodings.size() > kMaxSimulcastStreams) { + parameters.encodings.erase( + parameters.encodings.begin() + kMaxSimulcastStreams, + parameters.encodings.end()); + } + + // Single RID should be removed. + if (parameters.encodings.size() == 1 && + !parameters.encodings[0].rid.empty()) { + RTC_LOG(LS_INFO) << "Removing RID: " << parameters.encodings[0].rid << "."; + parameters.encodings[0].rid.clear(); + } + + // If RIDs were not provided, they are generated for simulcast scenario. + if (parameters.encodings.size() > 1 && num_rids == 0) { + rtc::UniqueStringGenerator rid_generator; + for (RtpEncodingParameters& encoding : parameters.encodings) { + encoding.rid = rid_generator(); + } + } + + if (UnimplementedRtpParameterHasValue(parameters)) { + LOG_AND_RETURN_ERROR( + RTCErrorType::UNSUPPORTED_PARAMETER, + "Attempted to set an unimplemented parameter of RtpParameters."); + } + + auto result = cricket::CheckRtpParametersValues(parameters); + if (!result.ok()) { + LOG_AND_RETURN_ERROR(result.type(), result.message()); + } + + RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) + << " transceiver in response to a call to AddTransceiver."; + // Set the sender ID equal to the track ID if the track is specified unless + // that sender ID is already in use. + std::string sender_id = + (track && !FindSenderById(track->id()) ? track->id() + : rtc::CreateRandomUuid()); + auto sender = CreateSender(media_type, sender_id, track, init.stream_ids, + parameters.encodings); + auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); + auto transceiver = CreateAndAddTransceiver(sender, receiver); + transceiver->internal()->set_direction(init.direction); + + if (update_negotiation_needed) { + UpdateNegotiationNeeded(); + } + + return rtc::scoped_refptr(transceiver); } -rtc::scoped_refptr> +rtc::scoped_refptr> TgPeerConnection::CreateSender( - cricket::MediaType media_type, - const std::string& id, - rtc::scoped_refptr track, - const std::vector& stream_ids, - const std::vector& send_encodings) { - RTC_DCHECK_RUN_ON(signaling_thread()); - rtc::scoped_refptr> sender; - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - RTC_DCHECK(!track || - (track->kind() == MediaStreamTrackInterface::kAudioKind)); - sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), - AudioRtpSender::Create(worker_thread(), id, stats_.get(), this)); - NoteUsageEvent(UsageEvent::AUDIO_ADDED); - } else { - RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); - RTC_DCHECK(!track || - (track->kind() == MediaStreamTrackInterface::kVideoKind)); - sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), VideoRtpSender::Create(worker_thread(), id, this)); - NoteUsageEvent(UsageEvent::VIDEO_ADDED); - } - bool set_track_succeeded = sender->SetTrack(track); - RTC_DCHECK(set_track_succeeded); - sender->internal()->set_stream_ids(stream_ids); - sender->internal()->set_init_send_encodings(send_encodings); - return sender; + cricket::MediaType media_type, + const std::string& id, + rtc::scoped_refptr track, + const std::vector& stream_ids, + const std::vector& send_encodings) { + RTC_DCHECK_RUN_ON(signaling_thread()); + rtc::scoped_refptr> sender; + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + RTC_DCHECK(!track || + (track->kind() == MediaStreamTrackInterface::kAudioKind)); + sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), + TgAudioRtpSender::Create(worker_thread(), id, stats_.get(), this)); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); + } else { + RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); + RTC_DCHECK(!track || + (track->kind() == MediaStreamTrackInterface::kVideoKind)); + sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), TgVideoRtpSender::Create(worker_thread(), id, this)); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); + } + bool set_track_succeeded = sender->SetTrack(track); + RTC_DCHECK(set_track_succeeded); + sender->internal()->set_stream_ids(stream_ids); + sender->internal()->set_init_send_encodings(send_encodings); + return sender; } rtc::scoped_refptr> TgPeerConnection::CreateReceiver(cricket::MediaType media_type, - const std::string& receiver_id) { - rtc::scoped_refptr> - receiver; - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), new AudioRtpReceiver(worker_thread(), receiver_id, - std::vector({}))); - NoteUsageEvent(UsageEvent::AUDIO_ADDED); - } else { - RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); - receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), new VideoRtpReceiver(worker_thread(), receiver_id, - std::vector({}))); - NoteUsageEvent(UsageEvent::VIDEO_ADDED); - } - return receiver; + const std::string& receiver_id) { + rtc::scoped_refptr> + receiver; + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), new AudioRtpReceiver(worker_thread(), receiver_id, + std::vector({}))); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); + } else { + RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); + receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), new VideoRtpReceiver(worker_thread(), receiver_id, + std::vector({}))); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); + } + return receiver; } rtc::scoped_refptr> TgPeerConnection::CreateAndAddTransceiver( - rtc::scoped_refptr> sender, - rtc::scoped_refptr> - receiver) { - // Ensure that the new sender does not have an ID that is already in use by - // another sender. - // Allow receiver IDs to conflict since those come from remote SDP (which - // could be invalid, but should not cause a crash). - RTC_DCHECK(!FindSenderById(sender->id())); - auto transceiver = RtpTransceiverProxyWithInternal::Create( - signaling_thread(), - new RtpTransceiver(sender, receiver, channel_manager())); - transceivers_.push_back(transceiver); - transceiver->internal()->SignalNegotiationNeeded.connect( - this, &TgPeerConnection::OnNegotiationNeeded); - return transceiver; + rtc::scoped_refptr> sender, + rtc::scoped_refptr> + receiver) { + // Ensure that the new sender does not have an ID that is already in use by + // another sender. + // Allow receiver IDs to conflict since those come from remote SDP (which + // could be invalid, but should not cause a crash). + RTC_DCHECK(!FindSenderById(sender->id())); + auto transceiver = RtpTransceiverProxyWithInternal::Create( + signaling_thread(), + new RtpTransceiver(sender, receiver, channel_manager())); + transceivers_.push_back(transceiver); + transceiver->internal()->SignalNegotiationNeeded.connect( + this, &TgPeerConnection::OnNegotiationNeeded); + return transceiver; } void TgPeerConnection::OnNegotiationNeeded() { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(!IsClosed()); - UpdateNegotiationNeeded(); + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(!IsClosed()); + UpdateNegotiationNeeded(); } rtc::scoped_refptr TgPeerConnection::CreateSender( - const std::string& kind, - const std::string& stream_id) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "CreateSender is not available with Unified " - "Plan SdpSemantics. Please use AddTransceiver " - "instead."; - TRACE_EVENT0("webrtc", "TgPeerConnection::CreateSender"); - if (IsClosed()) { - return nullptr; - } - - // Internally we need to have one stream with Plan B semantics, so we - // generate a random stream ID if not specified. - std::vector stream_ids; - if (stream_id.empty()) { - stream_ids.push_back(rtc::CreateRandomUuid()); - RTC_LOG(LS_INFO) + const std::string& kind, + const std::string& stream_id) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(!IsUnifiedPlan()) << "CreateSender is not available with Unified " + "Plan SdpSemantics. Please use AddTransceiver " + "instead."; + TRACE_EVENT0("webrtc", "TgPeerConnection::CreateSender"); + if (IsClosed()) { + return nullptr; + } + + // Internally we need to have one stream with Plan B semantics, so we + // generate a random stream ID if not specified. + std::vector stream_ids; + if (stream_id.empty()) { + stream_ids.push_back(rtc::CreateRandomUuid()); + RTC_LOG(LS_INFO) << "No stream_id specified for sender. Generated stream ID: " << stream_ids[0]; - } else { - stream_ids.push_back(stream_id); - } - - // TODO(steveanton): Move construction of the RtpSenders to RtpTransceiver. - rtc::scoped_refptr> new_sender; - if (kind == MediaStreamTrackInterface::kAudioKind) { - auto audio_sender = AudioRtpSender::Create( - worker_thread(), rtc::CreateRandomUuid(), stats_.get(), this); - audio_sender->SetMediaChannel(voice_media_channel()); - new_sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), audio_sender); - GetAudioTransceiver()->internal()->AddSender(new_sender); - } else if (kind == MediaStreamTrackInterface::kVideoKind) { - auto video_sender = - VideoRtpSender::Create(worker_thread(), rtc::CreateRandomUuid(), this); - video_sender->SetMediaChannel(video_media_channel()); - new_sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), video_sender); - GetVideoTransceiver()->internal()->AddSender(new_sender); - } else { - RTC_LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind; - return nullptr; - } - new_sender->internal()->set_stream_ids(stream_ids); - - return new_sender; + } else { + stream_ids.push_back(stream_id); + } + + // TODO(steveanton): Move construction of the RtpSenders to RtpTransceiver. + rtc::scoped_refptr> new_sender; + if (kind == MediaStreamTrackInterface::kAudioKind) { + auto audio_sender = TgAudioRtpSender::Create( + worker_thread(), rtc::CreateRandomUuid(), stats_.get(), this); + audio_sender->SetMediaChannel(voice_media_channel()); + new_sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), audio_sender); + GetAudioTransceiver()->internal()->AddSender(new_sender); + } else if (kind == MediaStreamTrackInterface::kVideoKind) { + auto video_sender = + TgVideoRtpSender::Create(worker_thread(), rtc::CreateRandomUuid(), this); + video_sender->SetMediaChannel(video_media_channel()); + new_sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), video_sender); + GetVideoTransceiver()->internal()->AddSender(new_sender); + } else { + RTC_LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind; + return nullptr; + } + new_sender->internal()->set_stream_ids(stream_ids); + + return new_sender; } std::vector> TgPeerConnection::GetSenders() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - std::vector> ret; - for (const auto& sender : GetSendersInternal()) { - ret.push_back(sender); - } - return ret; +const { + RTC_DCHECK_RUN_ON(signaling_thread()); + std::vector> ret; + for (const auto& sender : GetSendersInternal()) { + ret.push_back(sender); + } + return ret; } -std::vector>> +std::vector>> TgPeerConnection::GetSendersInternal() const { - std::vector>> - all_senders; - for (const auto& transceiver : transceivers_) { - auto senders = transceiver->internal()->senders(); - all_senders.insert(all_senders.end(), senders.begin(), senders.end()); - } - return all_senders; + std::vector>> + all_senders; + for (const auto& transceiver : transceivers_) { + auto senders = transceiver->internal()->senders(); + all_senders.insert(all_senders.end(), senders.begin(), senders.end()); + } + return all_senders; } std::vector> TgPeerConnection::GetReceivers() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - std::vector> ret; - for (const auto& receiver : GetReceiversInternal()) { - ret.push_back(receiver); - } - return ret; + RTC_DCHECK_RUN_ON(signaling_thread()); + std::vector> ret; + for (const auto& receiver : GetReceiversInternal()) { + ret.push_back(receiver); + } + return ret; } std::vector< - rtc::scoped_refptr>> +rtc::scoped_refptr>> TgPeerConnection::GetReceiversInternal() const { - std::vector< - rtc::scoped_refptr>> - all_receivers; - for (const auto& transceiver : transceivers_) { - auto receivers = transceiver->internal()->receivers(); - all_receivers.insert(all_receivers.end(), receivers.begin(), - receivers.end()); - } - return all_receivers; + std::vector< + rtc::scoped_refptr>> + all_receivers; + for (const auto& transceiver : transceivers_) { + auto receivers = transceiver->internal()->receivers(); + all_receivers.insert(all_receivers.end(), receivers.begin(), + receivers.end()); + } + return all_receivers; +} + +rtc::scoped_refptr TgPeerConnection::CreateDataChannel( +const std::string& label, + const DataChannelInit* config) { + return nullptr; } std::vector> TgPeerConnection::GetTransceivers() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(IsUnifiedPlan()) - << "GetTransceivers is only supported with Unified Plan SdpSemantics."; - std::vector> all_transceivers; - for (const auto& transceiver : transceivers_) { - all_transceivers.push_back(transceiver); - } - return all_transceivers; -} - -bool TgPeerConnection::GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track, - StatsOutputLevel level) { - TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!observer) { - RTC_LOG(LS_ERROR) << "GetStats - observer is NULL."; - return false; - } - - stats_->UpdateStats(level); - // The StatsCollector is used to tell if a track is valid because it may - // remember tracks that the TgPeerConnection previously removed. - if (track && !stats_->IsValidTrack(track->id())) { - RTC_LOG(LS_WARNING) << "GetStats is called with an invalid track: " - << track->id(); - return false; - } - signaling_thread()->Post(RTC_FROM_HERE, this, MSG_GETSTATS, - new GetStatsMsg(observer, track)); - return true; -} - -void TgPeerConnection::GetStats(RTCStatsCollectorCallback* callback) { - TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(stats_collector_); - RTC_DCHECK(callback); - stats_collector_->GetStatsReport(callback); -} - -void TgPeerConnection::GetStats( - rtc::scoped_refptr selector, - rtc::scoped_refptr callback) { - TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(callback); - RTC_DCHECK(stats_collector_); - rtc::scoped_refptr internal_sender; - if (selector) { - for (const auto& proxy_transceiver : transceivers_) { - for (const auto& proxy_sender : - proxy_transceiver->internal()->senders()) { - if (proxy_sender == selector) { - internal_sender = proxy_sender->internal(); - break; - } - } - if (internal_sender) - break; + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_CHECK(IsUnifiedPlan()) + << "GetTransceivers is only supported with Unified Plan SdpSemantics."; + std::vector> all_transceivers; + for (const auto& transceiver : transceivers_) { + all_transceivers.push_back(transceiver); } - } - // If there is no |internal_sender| then |selector| is either null or does not - // belong to the TgPeerConnection (in Plan B, senders can be removed from the - // TgPeerConnection). This means that "all the stats objects representing the - // selector" is an empty set. Invoking GetStatsReport() with a null selector - // produces an empty stats report. - stats_collector_->GetStatsReport(internal_sender, callback); -} - -void TgPeerConnection::GetStats( - rtc::scoped_refptr selector, - rtc::scoped_refptr callback) { - TRACE_EVENT0("webrtc", "TgPeerConnection::GetStats"); - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(callback); - RTC_DCHECK(stats_collector_); - rtc::scoped_refptr internal_receiver; - if (selector) { - for (const auto& proxy_transceiver : transceivers_) { - for (const auto& proxy_receiver : - proxy_transceiver->internal()->receivers()) { - if (proxy_receiver == selector) { - internal_receiver = proxy_receiver->internal(); - break; - } - } - if (internal_receiver) - break; - } - } - // If there is no |internal_receiver| then |selector| is either null or does - // not belong to the TgPeerConnection (in Plan B, receivers can be removed from - // the TgPeerConnection). This means that "all the stats objects representing - // the selector" is an empty set. Invoking GetStatsReport() with a null - // selector produces an empty stats report. - stats_collector_->GetStatsReport(internal_receiver, callback); + return all_transceivers; } PeerConnectionInterface::SignalingState TgPeerConnection::signaling_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return signaling_state_; + RTC_DCHECK_RUN_ON(signaling_thread()); + return signaling_state_; } PeerConnectionInterface::IceConnectionState TgPeerConnection::ice_connection_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return ice_connection_state_; + RTC_DCHECK_RUN_ON(signaling_thread()); + return ice_connection_state_; } PeerConnectionInterface::IceConnectionState TgPeerConnection::standardized_ice_connection_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return standardized_ice_connection_state_; + RTC_DCHECK_RUN_ON(signaling_thread()); + return standardized_ice_connection_state_; } PeerConnectionInterface::PeerConnectionState TgPeerConnection::peer_connection_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return connection_state_; + RTC_DCHECK_RUN_ON(signaling_thread()); + return connection_state_; } PeerConnectionInterface::IceGatheringState TgPeerConnection::ice_gathering_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return ice_gathering_state_; + RTC_DCHECK_RUN_ON(signaling_thread()); + return ice_gathering_state_; } void TgPeerConnection::RestartIce() { - RTC_DCHECK_RUN_ON(signaling_thread()); - local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions( - current_local_description_.get(), pending_local_description_.get()); - UpdateNegotiationNeeded(); + RTC_DCHECK_RUN_ON(signaling_thread()); + local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions( + current_local_description_.get(), pending_local_description_.get()); + UpdateNegotiationNeeded(); } void TgPeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer, - const RTCOfferAnswerOptions& options) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - options](std::function operations_chain_callback) { + const PeerConnectionInterface::RTCOfferAnswerOptions& options) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + options](std::function operations_chain_callback) { // Abort early if |this_weak_ptr| is no longer valid. if (!this_weak_ptr) { - observer_refptr->OnFailure( - RTCError(RTCErrorType::INTERNAL_ERROR, - "CreateOffer failed because the session was shut down")); - operations_chain_callback(); - return; + observer_refptr->OnFailure( + RTCError(RTCErrorType::INTERNAL_ERROR, + "CreateOffer failed because the session was shut down")); + operations_chain_callback(); + return; } // The operation completes asynchronously when the wrapper is invoked. rtc::scoped_refptr - observer_wrapper(new rtc::RefCountedObject< - CreateSessionDescriptionObserverOperationWrapper>( - std::move(observer_refptr), - std::move(operations_chain_callback))); + observer_wrapper(new rtc::RefCountedObject< + CreateSessionDescriptionObserverOperationWrapper>( + std::move(observer_refptr), + std::move(operations_chain_callback))); this_weak_ptr->DoCreateOffer(options, observer_wrapper); - }); + }); } void TgPeerConnection::DoCreateOffer( - const RTCOfferAnswerOptions& options, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateOffer"); - - if (!observer) { - RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL."; - return; - } - - if (IsClosed()) { - std::string error = "CreateOffer called when TgPeerConnection is closed."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message; - PostCreateSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - if (!ValidateOfferAnswerOptions(options)) { - std::string error = "CreateOffer called with invalid options."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error))); - return; - } - - // Legacy handling for offer_to_receive_audio and offer_to_receive_video. - // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions". - if (IsUnifiedPlan()) { - RTCError error = HandleLegacyOfferOptions(options); - if (!error.ok()) { - PostCreateSessionDescriptionFailure(observer, std::move(error)); - return; + const PeerConnectionInterface::RTCOfferAnswerOptions& options, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateOffer"); + + if (!observer) { + RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL."; + return; } - } - - cricket::MediaSessionOptions session_options; - GetOptionsForOffer(options, &session_options); - webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); + + if (IsClosed()) { + std::string error = "CreateOffer called when TgPeerConnection is closed."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailure( + observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); + return; + } + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message; + PostCreateSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + if (!ValidateOfferAnswerOptions(options)) { + std::string error = "CreateOffer called with invalid options."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailure( + observer, RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error))); + return; + } + + // Legacy handling for offer_to_receive_audio and offer_to_receive_video. + // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions". + if (IsUnifiedPlan()) { + RTCError error = HandleLegacyOfferOptions(options); + if (!error.ok()) { + PostCreateSessionDescriptionFailure(observer, std::move(error)); + return; + } + } + + cricket::MediaSessionOptions session_options; + GetOptionsForOffer(options, &session_options); + webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); } RTCError TgPeerConnection::HandleLegacyOfferOptions( - const RTCOfferAnswerOptions& options) { - RTC_DCHECK(IsUnifiedPlan()); - - if (options.offer_to_receive_audio == 0) { - RemoveRecvDirectionFromReceivingTransceiversOfType( - cricket::MEDIA_TYPE_AUDIO); - } else if (options.offer_to_receive_audio == 1) { - AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO); - } else if (options.offer_to_receive_audio > 1) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, - "offer_to_receive_audio > 1 is not supported."); - } - - if (options.offer_to_receive_video == 0) { - RemoveRecvDirectionFromReceivingTransceiversOfType( - cricket::MEDIA_TYPE_VIDEO); - } else if (options.offer_to_receive_video == 1) { - AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO); - } else if (options.offer_to_receive_video > 1) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, - "offer_to_receive_video > 1 is not supported."); - } - - return RTCError::OK(); + const PeerConnectionInterface::RTCOfferAnswerOptions& options) { + RTC_DCHECK(IsUnifiedPlan()); + + if (options.offer_to_receive_audio == 0) { + RemoveRecvDirectionFromReceivingTransceiversOfType( + cricket::MEDIA_TYPE_AUDIO); + } else if (options.offer_to_receive_audio == 1) { + AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO); + } else if (options.offer_to_receive_audio > 1) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, + "offer_to_receive_audio > 1 is not supported."); + } + + if (options.offer_to_receive_video == 0) { + RemoveRecvDirectionFromReceivingTransceiversOfType( + cricket::MEDIA_TYPE_VIDEO); + } else if (options.offer_to_receive_video == 1) { + AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO); + } else if (options.offer_to_receive_video > 1) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, + "offer_to_receive_video > 1 is not supported."); + } + + return RTCError::OK(); } void TgPeerConnection::RemoveRecvDirectionFromReceivingTransceiversOfType( - cricket::MediaType media_type) { - for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) { - RtpTransceiverDirection new_direction = + cricket::MediaType media_type) { + for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) { + RtpTransceiverDirection new_direction = RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false); - if (new_direction != transceiver->direction()) { - RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type) - << " transceiver (MID=" - << transceiver->mid().value_or("") << ") from " - << RtpTransceiverDirectionToString( - transceiver->direction()) - << " to " - << RtpTransceiverDirectionToString(new_direction) - << " since CreateOffer specified offer_to_receive=0"; - transceiver->internal()->set_direction(new_direction); + if (new_direction != transceiver->direction()) { + RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type) + << " transceiver (MID=" + << transceiver->mid().value_or("") << ") from " + << RtpTransceiverDirectionToString( + transceiver->direction()) + << " to " + << RtpTransceiverDirectionToString(new_direction) + << " since CreateOffer specified offer_to_receive=0"; + transceiver->internal()->set_direction(new_direction); + } } - } } void TgPeerConnection::AddUpToOneReceivingTransceiverOfType( - cricket::MediaType media_type) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (GetReceivingTransceiversOfType(media_type).empty()) { - RTC_LOG(LS_INFO) + cricket::MediaType media_type) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (GetReceivingTransceiversOfType(media_type).empty()) { + RTC_LOG(LS_INFO) << "Adding one recvonly " << cricket::MediaTypeToString(media_type) << " transceiver since CreateOffer specified offer_to_receive=1"; - RtpTransceiverInit init; - init.direction = RtpTransceiverDirection::kRecvOnly; - AddTransceiver(media_type, nullptr, init, - /*update_negotiation_needed=*/false); - } + RtpTransceiverInit init; + init.direction = RtpTransceiverDirection::kRecvOnly; + AddTransceiver(media_type, nullptr, init, + /*update_negotiation_needed=*/false); + } } std::vector>> TgPeerConnection::GetReceivingTransceiversOfType(cricket::MediaType media_type) { - std::vector< - rtc::scoped_refptr>> - receiving_transceivers; - for (const auto& transceiver : transceivers_) { - if (!transceiver->stopped() && transceiver->media_type() == media_type && - RtpTransceiverDirectionHasRecv(transceiver->direction())) { - receiving_transceivers.push_back(transceiver); + std::vector< + rtc::scoped_refptr>> + receiving_transceivers; + for (const auto& transceiver : transceivers_) { + if (!transceiver->stopped() && transceiver->media_type() == media_type && + RtpTransceiverDirectionHasRecv(transceiver->direction())) { + receiving_transceivers.push_back(transceiver); + } } - } - return receiving_transceivers; + return receiving_transceivers; } void TgPeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer, - const RTCOfferAnswerOptions& options) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - options](std::function operations_chain_callback) { + const PeerConnectionInterface::RTCOfferAnswerOptions& options) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + options](std::function operations_chain_callback) { // Abort early if |this_weak_ptr| is no longer valid. if (!this_weak_ptr) { - observer_refptr->OnFailure(RTCError( - RTCErrorType::INTERNAL_ERROR, - "CreateAnswer failed because the session was shut down")); - operations_chain_callback(); - return; + observer_refptr->OnFailure(RTCError( + RTCErrorType::INTERNAL_ERROR, + "CreateAnswer failed because the session was shut down")); + operations_chain_callback(); + return; } // The operation completes asynchronously when the wrapper is invoked. rtc::scoped_refptr - observer_wrapper(new rtc::RefCountedObject< - CreateSessionDescriptionObserverOperationWrapper>( - std::move(observer_refptr), - std::move(operations_chain_callback))); + observer_wrapper(new rtc::RefCountedObject< + CreateSessionDescriptionObserverOperationWrapper>( + std::move(observer_refptr), + std::move(operations_chain_callback))); this_weak_ptr->DoCreateAnswer(options, observer_wrapper); - }); + }); } void TgPeerConnection::DoCreateAnswer( - const RTCOfferAnswerOptions& options, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateAnswer"); - if (!observer) { - RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL."; - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message; - PostCreateSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - if (!(signaling_state_ == kHaveRemoteOffer || - signaling_state_ == kHaveLocalPrAnswer)) { - std::string error = + const PeerConnectionInterface::RTCOfferAnswerOptions& options, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateAnswer"); + if (!observer) { + RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL."; + return; + } + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message; + PostCreateSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + if (!(signaling_state_ == PeerConnectionInterface::kHaveRemoteOffer || + signaling_state_ == PeerConnectionInterface::kHaveLocalPrAnswer)) { + std::string error = "TgPeerConnection cannot create an answer in a state other than " "have-remote-offer or have-local-pranswer."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); - return; - } - - // The remote description should be set if we're in the right state. - RTC_DCHECK(remote_description()); - - if (IsUnifiedPlan()) { - if (options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) { - RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not " - "supported with Unified Plan semantics. Use the " - "RtpTransceiver API instead."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailure( + observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); + return; } - if (options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) { - RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not " - "supported with Unified Plan semantics. Use the " - "RtpTransceiver API instead."; + + // The remote description should be set if we're in the right state. + RTC_DCHECK(remote_description()); + + if (IsUnifiedPlan()) { + if (options.offer_to_receive_audio != PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) { + RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not " + "supported with Unified Plan semantics. Use the " + "RtpTransceiver API instead."; + } + if (options.offer_to_receive_video != PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) { + RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not " + "supported with Unified Plan semantics. Use the " + "RtpTransceiver API instead."; + } } - } - - cricket::MediaSessionOptions session_options; - GetOptionsForAnswer(options, &session_options); - - webrtc_session_desc_factory_->CreateAnswer(observer, session_options); + + cricket::MediaSessionOptions session_options; + GetOptionsForAnswer(options, &session_options); + + webrtc_session_desc_factory_->CreateAnswer(observer, session_options); } void TgPeerConnection::SetLocalDescription( - SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc_ptr) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - desc = std::unique_ptr(desc_ptr)]( - std::function operations_chain_callback) mutable { + SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc_ptr) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + desc = std::unique_ptr(desc_ptr)]( + std::function operations_chain_callback) mutable { // Abort early if |this_weak_ptr| is no longer valid. if (!this_weak_ptr) { - // For consistency with DoSetLocalDescription(), we DO NOT inform the - // |observer_refptr| that the operation failed in this case. - // TODO(hbos): If/when we process SLD messages in ~TgPeerConnection, - // the consistent thing would be to inform the observer here. - operations_chain_callback(); - return; + // For consistency with DoSetLocalDescription(), we DO NOT inform the + // |observer_refptr| that the operation failed in this case. + // TODO(hbos): If/when we process SLD messages in ~TgPeerConnection, + // the consistent thing would be to inform the observer here. + operations_chain_callback(); + return; } this_weak_ptr->DoSetLocalDescription(std::move(desc), std::move(observer_refptr)); @@ -2425,457 +2243,419 @@ void TgPeerConnection::SetLocalDescription( // subsequent offer/answer operations can start immediately (without // waiting for OnMessage()). operations_chain_callback(); - }); + }); } void TgPeerConnection::SetLocalDescription( - SetSessionDescriptionObserver* observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // The |create_sdp_observer| handles performing DoSetLocalDescription() with - // the resulting description as well as completing the operation. - rtc::scoped_refptr - create_sdp_observer( - new rtc::RefCountedObject( - weak_ptr_factory_.GetWeakPtr(), - rtc::scoped_refptr(observer))); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - create_sdp_observer](std::function operations_chain_callback) { + SetSessionDescriptionObserver* observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // The |create_sdp_observer| handles performing DoSetLocalDescription() with + // the resulting description as well as completing the operation. + rtc::scoped_refptr + create_sdp_observer( + new rtc::RefCountedObject( + weak_ptr_factory_.GetWeakPtr(), + rtc::scoped_refptr(observer))); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + create_sdp_observer](std::function operations_chain_callback) { // The |create_sdp_observer| is responsible for completing the // operation. create_sdp_observer->SetOperationCompleteCallback( - std::move(operations_chain_callback)); + std::move(operations_chain_callback)); // Abort early if |this_weak_ptr| is no longer valid. This triggers the // same code path as if DoCreateOffer() or DoCreateAnswer() failed. if (!this_weak_ptr) { - create_sdp_observer->OnFailure(RTCError( - RTCErrorType::INTERNAL_ERROR, - "SetLocalDescription failed because the session was shut down")); - return; + create_sdp_observer->OnFailure(RTCError( + RTCErrorType::INTERNAL_ERROR, + "SetLocalDescription failed because the session was shut down")); + return; } switch (this_weak_ptr->signaling_state()) { - case PeerConnectionInterface::kStable: - case PeerConnectionInterface::kHaveLocalOffer: - case PeerConnectionInterface::kHaveRemotePrAnswer: - // TODO(hbos): If [LastCreatedOffer] exists and still represents the - // current state of the system, use that instead of creating another - // offer. - this_weak_ptr->DoCreateOffer(RTCOfferAnswerOptions(), - create_sdp_observer); - break; - case PeerConnectionInterface::kHaveLocalPrAnswer: - case PeerConnectionInterface::kHaveRemoteOffer: - // TODO(hbos): If [LastCreatedAnswer] exists and still represents - // the current state of the system, use that instead of creating - // another answer. - this_weak_ptr->DoCreateAnswer(RTCOfferAnswerOptions(), - create_sdp_observer); - break; - case PeerConnectionInterface::kClosed: - create_sdp_observer->OnFailure(RTCError( - RTCErrorType::INVALID_STATE, - "SetLocalDescription called when TgPeerConnection is closed.")); - break; + case PeerConnectionInterface::kStable: + case PeerConnectionInterface::kHaveLocalOffer: + case PeerConnectionInterface::kHaveRemotePrAnswer: + // TODO(hbos): If [LastCreatedOffer] exists and still represents the + // current state of the system, use that instead of creating another + // offer. + this_weak_ptr->DoCreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions(), + create_sdp_observer); + break; + case PeerConnectionInterface::kHaveLocalPrAnswer: + case PeerConnectionInterface::kHaveRemoteOffer: + // TODO(hbos): If [LastCreatedAnswer] exists and still represents + // the current state of the system, use that instead of creating + // another answer. + this_weak_ptr->DoCreateAnswer(PeerConnectionInterface::RTCOfferAnswerOptions(), + create_sdp_observer); + break; + case PeerConnectionInterface::kClosed: + create_sdp_observer->OnFailure(RTCError( + RTCErrorType::INVALID_STATE, + "SetLocalDescription called when TgPeerConnection is closed.")); + break; } - }); + }); } void TgPeerConnection::DoSetLocalDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetLocalDescription"); - - if (!observer) { - RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL."; - return; - } - - if (!desc) { - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL.")); - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message; - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - // For SLD we support only explicit rollback. - if (desc->GetType() == SdpType::kRollback) { - if (IsUnifiedPlan()) { - RTCError error = Rollback(desc->GetType()); - if (error.ok()) { - PostSetSessionDescriptionSuccess(observer); - } else { - PostSetSessionDescriptionFailure(observer, std::move(error)); - } - } else { - PostSetSessionDescriptionFailure( - observer, RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Rollback not supported in Plan B")); + std::unique_ptr desc, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetLocalDescription"); + + if (!observer) { + RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL."; + return; } - return; - } - - RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL); - if (!error.ok()) { - std::string error_message = GetSetDescriptionErrorMessage( - cricket::CS_LOCAL, desc->GetType(), error); - RTC_LOG(LS_ERROR) << error_message; - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - // Grab the description type before moving ownership to ApplyLocalDescription, - // which may destroy it before returning. - const SdpType type = desc->GetType(); - - error = ApplyLocalDescription(std::move(desc)); - // |desc| may be destroyed at this point. - - if (!error.ok()) { - // If ApplyLocalDescription fails, the TgPeerConnection could be in an - // inconsistent state, so act conservatively here and set the session error - // so that future calls to SetLocalDescription/SetRemoteDescription fail. - SetSessionError(SessionError::kContent, error.message()); - std::string error_message = + + if (!desc) { + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL.")); + return; + } + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message; + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + // For SLD we support only explicit rollback. + if (desc->GetType() == SdpType::kRollback) { + if (IsUnifiedPlan()) { + RTCError error = Rollback(desc->GetType()); + if (error.ok()) { + PostSetSessionDescriptionSuccess(observer); + } else { + PostSetSessionDescriptionFailure(observer, std::move(error)); + } + } else { + PostSetSessionDescriptionFailure( + observer, RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Rollback not supported in Plan B")); + } + return; + } + + RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL); + if (!error.ok()) { + std::string error_message = GetSetDescriptionErrorMessage( + cricket::CS_LOCAL, desc->GetType(), error); + RTC_LOG(LS_ERROR) << error_message; + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + + // Grab the description type before moving ownership to ApplyLocalDescription, + // which may destroy it before returning. + const SdpType type = desc->GetType(); + + error = ApplyLocalDescription(std::move(desc)); + // |desc| may be destroyed at this point. + + if (!error.ok()) { + // If ApplyLocalDescription fails, the TgPeerConnection could be in an + // inconsistent state, so act conservatively here and set the session error + // so that future calls to SetLocalDescription/SetRemoteDescription fail. + SetSessionError(SessionError::kContent, error.message()); + std::string error_message = GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error); - RTC_LOG(LS_ERROR) << error_message; - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - RTC_DCHECK(local_description()); - - PostSetSessionDescriptionSuccess(observer); - - // MaybeStartGathering needs to be called after posting - // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates - // before signaling that SetLocalDescription completed. - transport_controller_->MaybeStartGathering(); - - if (local_description()->GetType() == SdpType::kAnswer) { - // TODO(deadbeef): We already had to hop to the network thread for - // MaybeStartGathering... - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, - port_allocator_.get())); - // Make UMA notes about what was agreed to. - ReportNegotiatedSdpSemantics(*local_description()); - } - - if (IsUnifiedPlan()) { - bool was_negotiation_needed = is_negotiation_needed_; - UpdateNegotiationNeeded(); - if (signaling_state() == kStable && was_negotiation_needed && - is_negotiation_needed_) { - Observer()->OnRenegotiationNeeded(); + RTC_LOG(LS_ERROR) << error_message; + PostSetSessionDescriptionFailure( + observer, + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; } - } - - NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED); + RTC_DCHECK(local_description()); + + PostSetSessionDescriptionSuccess(observer); + + // MaybeStartGathering needs to be called after posting + // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates + // before signaling that SetLocalDescription completed. + transport_controller_->MaybeStartGathering(); + + if (local_description()->GetType() == SdpType::kAnswer) { + // TODO(deadbeef): We already had to hop to the network thread for + // MaybeStartGathering... + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, + port_allocator_.get())); + // Make UMA notes about what was agreed to. + ReportNegotiatedSdpSemantics(*local_description()); + } + + if (IsUnifiedPlan()) { + bool was_negotiation_needed = is_negotiation_needed_; + UpdateNegotiationNeeded(); + if (signaling_state() == PeerConnectionInterface::kStable && was_negotiation_needed && + is_negotiation_needed_) { + Observer()->OnRenegotiationNeeded(); + } + } + + NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED); } RTCError TgPeerConnection::ApplyLocalDescription( - std::unique_ptr desc) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(desc); - - // Update stats here so that we have the most recent stats for tracks and - // streams that might be removed by updating the session description. - stats_->UpdateStats(kStatsOutputLevelStandard); - - // Take a reference to the old local description since it's used below to - // compare against the new local description. When setting the new local - // description, grab ownership of the replaced session description in case it - // is the same as |old_local_description|, to keep it alive for the duration - // of the method. - const SessionDescriptionInterface* old_local_description = - local_description(); - std::unique_ptr replaced_local_description; - SdpType type = desc->GetType(); - if (type == SdpType::kAnswer) { - replaced_local_description = pending_local_description_ - ? std::move(pending_local_description_) - : std::move(current_local_description_); - current_local_description_ = std::move(desc); - pending_local_description_ = nullptr; - current_remote_description_ = std::move(pending_remote_description_); - } else { - replaced_local_description = std::move(pending_local_description_); - pending_local_description_ = std::move(desc); - } - // The session description to apply now must be accessed by - // |local_description()|. - RTC_DCHECK(local_description()); - - // Report statistics about any use of simulcast. - ReportSimulcastApiVersion(kSimulcastVersionApplyLocalDescription, - *local_description()->description()); - - if (!is_caller_) { - if (remote_description()) { - // Remote description was applied first, so this PC is the callee. - is_caller_ = false; + std::unique_ptr desc) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(desc); + + // Take a reference to the old local description since it's used below to + // compare against the new local description. When setting the new local + // description, grab ownership of the replaced session description in case it + // is the same as |old_local_description|, to keep it alive for the duration + // of the method. + const SessionDescriptionInterface* old_local_description = + local_description(); + std::unique_ptr replaced_local_description; + SdpType type = desc->GetType(); + if (type == SdpType::kAnswer) { + replaced_local_description = pending_local_description_ + ? std::move(pending_local_description_) + : std::move(current_local_description_); + current_local_description_ = std::move(desc); + pending_local_description_ = nullptr; + current_remote_description_ = std::move(pending_remote_description_); } else { - // Local description is applied first, so this PC is the caller. - is_caller_ = true; + replaced_local_description = std::move(pending_local_description_); + pending_local_description_ = std::move(desc); } - } - - RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type); - if (!error.ok()) { - return error; - } - - if (IsUnifiedPlan()) { - RTCError error = UpdateTransceiversAndDataChannels( - cricket::CS_LOCAL, *local_description(), old_local_description, - remote_description()); + // The session description to apply now must be accessed by + // |local_description()|. + RTC_DCHECK(local_description()); + + // Report statistics about any use of simulcast. + ReportSimulcastApiVersion(kSimulcastVersionApplyLocalDescription, + *local_description()->description()); + + if (!is_caller_) { + if (remote_description()) { + // Remote description was applied first, so this PC is the callee. + is_caller_ = false; + } else { + // Local description is applied first, so this PC is the caller. + is_caller_ = true; + } + } + + RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type); if (!error.ok()) { - return error; + return error; + } + + error = UpdateTransceiversAndDataChannels( + cricket::CS_LOCAL, *local_description(), old_local_description, + remote_description()); + if (!error.ok()) { + return error; } std::vector> remove_list; std::vector> removed_streams; for (const auto& transceiver : transceivers_) { - // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots. - // Note that code paths that don't set MID won't be able to use - // information about DTLS transports. - if (transceiver->mid()) { - auto dtls_transport = + // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots. + // Note that code paths that don't set MID won't be able to use + // information about DTLS transports. + if (transceiver->mid()) { + auto dtls_transport = LookupDtlsTransportByMidInternal(*transceiver->mid()); - transceiver->internal()->sender_internal()->set_transport( - dtls_transport); - transceiver->internal()->receiver_internal()->set_transport( - dtls_transport); - } - - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, local_description()); - if (!content) { - continue; - } - const MediaContentDescription* media_desc = content->media_description(); - // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run - // the following steps: - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and - // transceiver's [[FiredDirection]] slot is either "sendrecv" or - // "recvonly", process the removal of a remote track for the media - // description, given transceiver, removeList, and muteTracks. - if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) && - (transceiver->internal()->fired_direction() && - RtpTransceiverDirectionHasRecv( - *transceiver->internal()->fired_direction()))) { - ProcessRemovalOfRemoteTrack(transceiver, &remove_list, - &removed_streams); + transceiver->internal()->sender_internal()->set_transport( + dtls_transport); + transceiver->internal()->receiver_internal()->set_transport( + dtls_transport); + } + + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, local_description()); + if (!content) { + continue; + } + const MediaContentDescription* media_desc = content->media_description(); + // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run + // the following steps: + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and + // transceiver's [[FiredDirection]] slot is either "sendrecv" or + // "recvonly", process the removal of a remote track for the media + // description, given transceiver, removeList, and muteTracks. + if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) && + (transceiver->internal()->fired_direction() && + RtpTransceiverDirectionHasRecv( + *transceiver->internal()->fired_direction()))) { + ProcessRemovalOfRemoteTrack(transceiver, &remove_list, + &removed_streams); + } + // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and + // [[FiredDirection]] slots to direction. + transceiver->internal()->set_current_direction(media_desc->direction()); + transceiver->internal()->set_fired_direction(media_desc->direction()); } - // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and - // [[FiredDirection]] slots to direction. - transceiver->internal()->set_current_direction(media_desc->direction()); - transceiver->internal()->set_fired_direction(media_desc->direction()); - } } auto observer = Observer(); for (const auto& transceiver : remove_list) { - observer->OnRemoveTrack(transceiver->receiver()); + observer->OnRemoveTrack(transceiver->receiver()); } for (const auto& stream : removed_streams) { - observer->OnRemoveStream(stream); + observer->OnRemoveStream(stream); } - } else { - // Media channels will be created only when offer is set. These may use new - // transports just created by PushdownTransportDescription. - if (type == SdpType::kOffer) { - // TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local - // description is applied. Restore back to old description. - RTCError error = CreateChannels(*local_description()->description()); - if (!error.ok()) { + + error = UpdateSessionState(type, cricket::CS_LOCAL, + local_description()->description()); + if (!error.ok()) { return error; - } } - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(local_description()->description()); - } - - error = UpdateSessionState(type, cricket::CS_LOCAL, - local_description()->description()); - if (!error.ok()) { - return error; - } - - if (remote_description()) { - // Now that we have a local description, we can push down remote candidates. - UseCandidatesInSessionDescription(remote_description()); - } - - pending_ice_restarts_.clear(); - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } - - if (IsUnifiedPlan()) { - for (const auto& transceiver : transceivers_) { - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, local_description()); - if (!content) { - continue; - } - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (content->rejected || !channel || channel->local_streams().empty()) { - // 0 is a special value meaning "this sender has no associated send - // stream". Need to call this so the sender won't attempt to configure - // a no longer existing stream and run into DCHECKs in the lower - // layers. - transceiver->internal()->sender_internal()->SetSsrc(0); - } else { - // Get the StreamParams from the channel which could generate SSRCs. - const std::vector& streams = channel->local_streams(); - transceiver->internal()->sender_internal()->set_stream_ids( - streams[0].stream_ids()); - transceiver->internal()->sender_internal()->SetSsrc( - streams[0].first_ssrc()); - } + + if (remote_description()) { + // Now that we have a local description, we can push down remote candidates. + UseCandidatesInSessionDescription(remote_description()); } - } else { - // Plan B semantics. - - // Update state and SSRC of local MediaStreams and DataChannels based on the - // local session description. - const cricket::ContentInfo* audio_content = + + pending_ice_restarts_.clear(); + if (session_error() != SessionError::kNone) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); + } + + if (IsUnifiedPlan()) { + for (const auto& transceiver : transceivers_) { + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, local_description()); + if (!content) { + continue; + } + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (content->rejected || !channel || channel->local_streams().empty()) { + // 0 is a special value meaning "this sender has no associated send + // stream". Need to call this so the sender won't attempt to configure + // a no longer existing stream and run into DCHECKs in the lower + // layers. + transceiver->internal()->sender_internal()->SetSsrc(0); + } else { + // Get the StreamParams from the channel which could generate SSRCs. + const std::vector& streams = channel->local_streams(); + transceiver->internal()->sender_internal()->set_stream_ids( + streams[0].stream_ids()); + transceiver->internal()->sender_internal()->SetSsrc( + streams[0].first_ssrc()); + } + } + } else { + // Plan B semantics. + + // Update state and SSRC of local MediaStreams and DataChannels based on the + // local session description. + const cricket::ContentInfo* audio_content = GetFirstAudioContent(local_description()->description()); - if (audio_content) { - if (audio_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_AUDIO); - } else { - const cricket::AudioContentDescription* audio_desc = - audio_content->media_description()->as_audio(); - UpdateLocalSenders(audio_desc->streams(), audio_desc->type()); - } - } - - const cricket::ContentInfo* video_content = + if (audio_content) { + if (audio_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_AUDIO); + } else { + const cricket::AudioContentDescription* audio_desc = + audio_content->media_description()->as_audio(); + UpdateLocalSenders(audio_desc->streams(), audio_desc->type()); + } + } + + const cricket::ContentInfo* video_content = GetFirstVideoContent(local_description()->description()); - if (video_content) { - if (video_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_VIDEO); - } else { - const cricket::VideoContentDescription* video_desc = - video_content->media_description()->as_video(); - UpdateLocalSenders(video_desc->streams(), video_desc->type()); - } + if (video_content) { + if (video_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_VIDEO); + } else { + const cricket::VideoContentDescription* video_desc = + video_content->media_description()->as_video(); + UpdateLocalSenders(video_desc->streams(), video_desc->type()); + } + } } - } - - if (type == SdpType::kAnswer && - local_ice_credentials_to_replace_->SatisfiesIceRestart( - *current_local_description_)) { - local_ice_credentials_to_replace_->ClearIceCredentials(); - } - - return RTCError::OK(); -} - -// The SDP parser used to populate these values by default for the 'content -// name' if an a=mid line was absent. -static absl::string_view GetDefaultMidForPlanB(cricket::MediaType media_type) { - switch (media_type) { - case cricket::MEDIA_TYPE_AUDIO: - return cricket::CN_AUDIO; - case cricket::MEDIA_TYPE_VIDEO: - return cricket::CN_VIDEO; - case cricket::MEDIA_TYPE_DATA: - return cricket::CN_DATA; - } - RTC_NOTREACHED(); - return ""; + + if (type == SdpType::kAnswer && + local_ice_credentials_to_replace_->SatisfiesIceRestart( + *current_local_description_)) { + local_ice_credentials_to_replace_->ClearIceCredentials(); + } + + return RTCError::OK(); } void TgPeerConnection::FillInMissingRemoteMids( - cricket::SessionDescription* new_remote_description) { - RTC_DCHECK(new_remote_description); - const cricket::ContentInfos no_infos; - const cricket::ContentInfos& local_contents = - (local_description() ? local_description()->description()->contents() - : no_infos); - const cricket::ContentInfos& remote_contents = - (remote_description() ? remote_description()->description()->contents() - : no_infos); - for (size_t i = 0; i < new_remote_description->contents().size(); ++i) { - cricket::ContentInfo& content = new_remote_description->contents()[i]; - if (!content.name.empty()) { - continue; + cricket::SessionDescription* new_remote_description) { + RTC_DCHECK(new_remote_description); + const cricket::ContentInfos no_infos; + const cricket::ContentInfos& local_contents = + (local_description() ? local_description()->description()->contents() + : no_infos); + const cricket::ContentInfos& remote_contents = + (remote_description() ? remote_description()->description()->contents() + : no_infos); + for (size_t i = 0; i < new_remote_description->contents().size(); ++i) { + cricket::ContentInfo& content = new_remote_description->contents()[i]; + if (!content.name.empty()) { + continue; + } + std::string new_mid; + absl::string_view source_explanation; + + if (i < local_contents.size()) { + new_mid = local_contents[i].name; + source_explanation = "from the matching local media section"; + } else if (i < remote_contents.size()) { + new_mid = remote_contents[i].name; + source_explanation = "from the matching previous remote media section"; + } else { + new_mid = mid_generator_(); + source_explanation = "generated just now"; + } + + RTC_DCHECK(!new_mid.empty()); + content.name = new_mid; + new_remote_description->transport_infos()[i].content_name = new_mid; + RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i + << " is missing an a=mid line. Filling in the value '" + << new_mid << "' " << source_explanation << "."; } - std::string new_mid; - absl::string_view source_explanation; - if (IsUnifiedPlan()) { - if (i < local_contents.size()) { - new_mid = local_contents[i].name; - source_explanation = "from the matching local media section"; - } else if (i < remote_contents.size()) { - new_mid = remote_contents[i].name; - source_explanation = "from the matching previous remote media section"; - } else { - new_mid = mid_generator_(); - source_explanation = "generated just now"; - } - } else { - new_mid = std::string( - GetDefaultMidForPlanB(content.media_description()->type())); - source_explanation = "to match pre-existing behavior"; - } - RTC_DCHECK(!new_mid.empty()); - content.name = new_mid; - new_remote_description->transport_infos()[i].content_name = new_mid; - RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i - << " is missing an a=mid line. Filling in the value '" - << new_mid << "' " << source_explanation << "."; - } } void TgPeerConnection::SetRemoteDescription( - SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc_ptr) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - desc = std::unique_ptr(desc_ptr)]( - std::function operations_chain_callback) mutable { + SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc_ptr) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + observer_refptr = + rtc::scoped_refptr(observer), + desc = std::unique_ptr(desc_ptr)]( + std::function operations_chain_callback) mutable { // Abort early if |this_weak_ptr| is no longer valid. if (!this_weak_ptr) { - // For consistency with SetRemoteDescriptionObserverAdapter, we DO NOT - // inform the |observer_refptr| that the operation failed in this - // case. - // TODO(hbos): If/when we process SRD messages in ~TgPeerConnection, - // the consistent thing would be to inform the observer here. - operations_chain_callback(); - return; + // For consistency with SetRemoteDescriptionObserverAdapter, we DO NOT + // inform the |observer_refptr| that the operation failed in this + // case. + // TODO(hbos): If/when we process SRD messages in ~TgPeerConnection, + // the consistent thing would be to inform the observer here. + operations_chain_callback(); + return; } this_weak_ptr->DoSetRemoteDescription( - std::move(desc), - rtc::scoped_refptr( - new SetRemoteDescriptionObserverAdapter( - this_weak_ptr.get(), std::move(observer_refptr)))); + std::move(desc), + rtc::scoped_refptr( + new SetRemoteDescriptionObserverAdapter( + this_weak_ptr.get(), std::move(observer_refptr)))); // DoSetRemoteDescription() is currently implemented as a synchronous // operation but where SetRemoteDescriptionObserverAdapter ensures that // the |observer|'s callbacks are invoked asynchronously in a post to @@ -2885,30 +2665,30 @@ void TgPeerConnection::SetRemoteDescription( // subsequent offer/answer operations can start immediately (without // waiting for OnMessage()). operations_chain_callback(); - }); + }); } void TgPeerConnection::SetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer, - desc = std::move(desc)]( - std::function operations_chain_callback) mutable { + std::unique_ptr desc, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer, + desc = std::move(desc)]( + std::function operations_chain_callback) mutable { // Abort early if |this_weak_ptr| is no longer valid. if (!this_weak_ptr) { - // For consistency with DoSetRemoteDescription(), we DO inform the - // |observer| that the operation failed in this case. - observer->OnSetRemoteDescriptionComplete(RTCError( - RTCErrorType::INVALID_STATE, - "Failed to set remote offer sdp: failed because the session was " - "shut down")); - operations_chain_callback(); - return; + // For consistency with DoSetRemoteDescription(), we DO inform the + // |observer| that the operation failed in this case. + observer->OnSetRemoteDescriptionComplete(RTCError( + RTCErrorType::INVALID_STATE, + "Failed to set remote offer sdp: failed because the session was " + "shut down")); + operations_chain_callback(); + return; } this_weak_ptr->DoSetRemoteDescription(std::move(desc), std::move(observer)); @@ -2917,3761 +2697,3603 @@ void TgPeerConnection::SetRemoteDescription( // completed, and we can mark this operation as complete without any // loose ends. operations_chain_callback(); - }); + }); } void TgPeerConnection::DoSetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetRemoteDescription"); - - if (!observer) { - RTC_LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL."; - return; - } - - if (!desc) { - observer->OnSetRemoteDescriptionComplete(RTCError( - RTCErrorType::INVALID_PARAMETER, "SessionDescription is NULL.")); - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "SetRemoteDescription: " << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - if (IsUnifiedPlan()) { - if (configuration_.enable_implicit_rollback) { - if (desc->GetType() == SdpType::kOffer && - signaling_state() == kHaveLocalOffer) { - Rollback(desc->GetType()); - } + std::unique_ptr desc, + rtc::scoped_refptr observer) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetRemoteDescription"); + + if (!observer) { + RTC_LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL."; + return; } - // Explicit rollback. - if (desc->GetType() == SdpType::kRollback) { - observer->OnSetRemoteDescriptionComplete(Rollback(desc->GetType())); - return; + + if (!desc) { + observer->OnSetRemoteDescriptionComplete(RTCError( + RTCErrorType::INVALID_PARAMETER, "SessionDescription is NULL.")); + return; } - } else if (desc->GetType() == SdpType::kRollback) { - observer->OnSetRemoteDescriptionComplete( - RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Rollback not supported in Plan B")); - return; - } - if (desc->GetType() == SdpType::kOffer) { - // Report to UMA the format of the received offer. - ReportSdpFormatReceived(*desc); - } - - // Handle remote descriptions missing a=mid lines for interop with legacy end - // points. - FillInMissingRemoteMids(desc->description()); - - RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE); - if (!error.ok()) { - std::string error_message = GetSetDescriptionErrorMessage( - cricket::CS_REMOTE, desc->GetType(), error); - RTC_LOG(LS_ERROR) << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(error.type(), std::move(error_message))); - return; - } - - // Grab the description type before moving ownership to - // ApplyRemoteDescription, which may destroy it before returning. - const SdpType type = desc->GetType(); - - error = ApplyRemoteDescription(std::move(desc)); - // |desc| may be destroyed at this point. - - if (!error.ok()) { - // If ApplyRemoteDescription fails, the TgPeerConnection could be in an - // inconsistent state, so act conservatively here and set the session error - // so that future calls to SetLocalDescription/SetRemoteDescription fail. - SetSessionError(SessionError::kContent, error.message()); - std::string error_message = + + // If a session error has occurred the TgPeerConnection is in a possibly + // inconsistent state so fail right away. + if (session_error() != SessionError::kNone) { + std::string error_message = GetSessionErrorMsg(); + RTC_LOG(LS_ERROR) << "SetRemoteDescription: " << error_message; + observer->OnSetRemoteDescriptionComplete( + RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); + return; + } + if (IsUnifiedPlan()) { + if (configuration_.enable_implicit_rollback) { + if (desc->GetType() == SdpType::kOffer && + signaling_state() == PeerConnectionInterface::kHaveLocalOffer) { + Rollback(desc->GetType()); + } + } + // Explicit rollback. + if (desc->GetType() == SdpType::kRollback) { + observer->OnSetRemoteDescriptionComplete(Rollback(desc->GetType())); + return; + } + } else if (desc->GetType() == SdpType::kRollback) { + observer->OnSetRemoteDescriptionComplete( + RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Rollback not supported in Plan B")); + return; + } + if (desc->GetType() == SdpType::kOffer) { + // Report to UMA the format of the received offer. + ReportSdpFormatReceived(*desc); + } + + // Handle remote descriptions missing a=mid lines for interop with legacy end + // points. + FillInMissingRemoteMids(desc->description()); + + RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE); + if (!error.ok()) { + std::string error_message = GetSetDescriptionErrorMessage( + cricket::CS_REMOTE, desc->GetType(), error); + RTC_LOG(LS_ERROR) << error_message; + observer->OnSetRemoteDescriptionComplete( + RTCError(error.type(), std::move(error_message))); + return; + } + + // Grab the description type before moving ownership to + // ApplyRemoteDescription, which may destroy it before returning. + const SdpType type = desc->GetType(); + + error = ApplyRemoteDescription(std::move(desc)); + // |desc| may be destroyed at this point. + + if (!error.ok()) { + // If ApplyRemoteDescription fails, the TgPeerConnection could be in an + // inconsistent state, so act conservatively here and set the session error + // so that future calls to SetLocalDescription/SetRemoteDescription fail. + SetSessionError(SessionError::kContent, error.message()); + std::string error_message = GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type, error); - RTC_LOG(LS_ERROR) << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(error.type(), std::move(error_message))); - return; - } - RTC_DCHECK(remote_description()); - - if (type == SdpType::kAnswer) { - // TODO(deadbeef): We already had to hop to the network thread for - // MaybeStartGathering... - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, - port_allocator_.get())); - // Make UMA notes about what was agreed to. - ReportNegotiatedSdpSemantics(*remote_description()); - } - - if (IsUnifiedPlan()) { - bool was_negotiation_needed = is_negotiation_needed_; - UpdateNegotiationNeeded(); - if (signaling_state() == kStable && was_negotiation_needed && - is_negotiation_needed_) { - Observer()->OnRenegotiationNeeded(); + RTC_LOG(LS_ERROR) << error_message; + observer->OnSetRemoteDescriptionComplete( + RTCError(error.type(), std::move(error_message))); + return; } - } - - observer->OnSetRemoteDescriptionComplete(RTCError::OK()); - NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED); + RTC_DCHECK(remote_description()); + + if (type == SdpType::kAnswer) { + // TODO(deadbeef): We already had to hop to the network thread for + // MaybeStartGathering... + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, + port_allocator_.get())); + // Make UMA notes about what was agreed to. + ReportNegotiatedSdpSemantics(*remote_description()); + } + + if (IsUnifiedPlan()) { + bool was_negotiation_needed = is_negotiation_needed_; + UpdateNegotiationNeeded(); + if (signaling_state() == PeerConnectionInterface::kStable && was_negotiation_needed && + is_negotiation_needed_) { + Observer()->OnRenegotiationNeeded(); + } + } + + observer->OnSetRemoteDescriptionComplete(RTCError::OK()); + NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED); } RTCError TgPeerConnection::ApplyRemoteDescription( - std::unique_ptr desc) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(desc); - - // Update stats here so that we have the most recent stats for tracks and - // streams that might be removed by updating the session description. - stats_->UpdateStats(kStatsOutputLevelStandard); - - // Take a reference to the old remote description since it's used below to - // compare against the new remote description. When setting the new remote - // description, grab ownership of the replaced session description in case it - // is the same as |old_remote_description|, to keep it alive for the duration - // of the method. - const SessionDescriptionInterface* old_remote_description = - remote_description(); - std::unique_ptr replaced_remote_description; - SdpType type = desc->GetType(); - if (type == SdpType::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); - } - // The session description to apply now must be accessed by - // |remote_description()|. - RTC_DCHECK(remote_description()); - - // Report statistics about any use of simulcast. - ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription, - *remote_description()->description()); - - RTCError error = PushdownTransportDescription(cricket::CS_REMOTE, type); - if (!error.ok()) { - return error; - } - // Transport and Media channels will be created only when offer is set. - if (IsUnifiedPlan()) { - RTCError error = UpdateTransceiversAndDataChannels( - cricket::CS_REMOTE, *remote_description(), local_description(), - old_remote_description); + std::unique_ptr desc) { + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(desc); + + // Take a reference to the old remote description since it's used below to + // compare against the new remote description. When setting the new remote + // description, grab ownership of the replaced session description in case it + // is the same as |old_remote_description|, to keep it alive for the duration + // of the method. + const SessionDescriptionInterface* old_remote_description = + remote_description(); + std::unique_ptr replaced_remote_description; + SdpType type = desc->GetType(); + if (type == SdpType::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); + } + // The session description to apply now must be accessed by + // |remote_description()|. + RTC_DCHECK(remote_description()); + + // Report statistics about any use of simulcast. + ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription, + *remote_description()->description()); + + RTCError error = PushdownTransportDescription(cricket::CS_REMOTE, type); if (!error.ok()) { - return error; - } - } else { - // Media channels will be created only when offer is set. These may use new - // transports just created by PushdownTransportDescription. - if (type == SdpType::kOffer) { - // TODO(mallinath) - Handle CreateChannel failure, as new local - // description is applied. Restore back to old description. - RTCError error = CreateChannels(*remote_description()->description()); - if (!error.ok()) { return error; - } } - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(remote_description()->description()); - } - - // NOTE: Candidates allocation will be initiated only when - // SetLocalDescription is called. - error = UpdateSessionState(type, cricket::CS_REMOTE, - remote_description()->description()); - if (!error.ok()) { - return error; - } - - if (local_description() && - !UseCandidatesInSessionDescription(remote_description())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidCandidates); - } - - 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)) { + // Transport and Media channels will be created only when offer is set. + if (IsUnifiedPlan()) { + RTCError error = UpdateTransceiversAndDataChannels( + cricket::CS_REMOTE, *remote_description(), local_description(), + old_remote_description); + if (!error.ok()) { + return error; + } + } else { + // Media channels will be created only when offer is set. These may use new + // transports just created by PushdownTransportDescription. if (type == SdpType::kOffer) { - pending_ice_restarts_.insert(content.name); + // TODO(mallinath) - Handle CreateChannel failure, as new local + // description is applied. Restore back to old description. + RTCError error = CreateChannels(*remote_description()->description()); + if (!error.ok()) { + return error; + } } - } 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, mutable_remote_description()); - } + // Remove unused channels if MediaContentDescription is rejected. + RemoveUnusedChannels(remote_description()->description()); } - } - - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } - - // 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()->GetType() != SdpType::kOffer && - remote_description()->number_of_mediasections() > 0u && - ice_connection_state() == PeerConnectionInterface::kIceConnectionNew) { - SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); - } - - if (IsUnifiedPlan()) { - std::vector> + + // NOTE: Candidates allocation will be initiated only when + // SetLocalDescription is called. + error = UpdateSessionState(type, cricket::CS_REMOTE, + remote_description()->description()); + if (!error.ok()) { + return error; + } + + if (local_description() && + !UseCandidatesInSessionDescription(remote_description())) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidCandidates); + } + + 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 (type == SdpType::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. + TgWebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( + old_remote_description, content.name, mutable_remote_description()); + } + } + } + + if (session_error() != SessionError::kNone) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); + } + + // 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()->GetType() != SdpType::kOffer && + remote_description()->number_of_mediasections() > 0u && + ice_connection_state() == PeerConnectionInterface::kIceConnectionNew) { + SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); + } + + if (IsUnifiedPlan()) { + std::vector> now_receiving_transceivers; - std::vector> remove_list; - std::vector> added_streams; - std::vector> removed_streams; - for (const auto& transceiver : transceivers_) { - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, remote_description()); - if (!content) { - continue; - } - const MediaContentDescription* media_desc = content->media_description(); - RtpTransceiverDirection local_direction = - RtpTransceiverDirectionReversed(media_desc->direction()); - // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the - // RTCSessionDescription: Set the associated remote streams given - // transceiver.[[Receiver]], msids, addList, and removeList". - // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription - if (RtpTransceiverDirectionHasRecv(local_direction)) { - std::vector stream_ids; - if (!media_desc->streams().empty()) { - // The remote description has signaled the stream IDs. - stream_ids = media_desc->streams()[0].stream_ids(); + std::vector> remove_list; + std::vector> added_streams; + std::vector> removed_streams; + for (const auto& transceiver : transceivers_) { + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, remote_description()); + if (!content) { + continue; + } + const MediaContentDescription* media_desc = content->media_description(); + RtpTransceiverDirection local_direction = + RtpTransceiverDirectionReversed(media_desc->direction()); + // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the + // RTCSessionDescription: Set the associated remote streams given + // transceiver.[[Receiver]], msids, addList, and removeList". + // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription + if (RtpTransceiverDirectionHasRecv(local_direction)) { + std::vector stream_ids; + if (!media_desc->streams().empty()) { + // The remote description has signaled the stream IDs. + stream_ids = media_desc->streams()[0].stream_ids(); + } + transceiver_stable_states_by_transceivers_[transceiver] + .SetRemoteStreamIdsIfUnset(transceiver->receiver()->stream_ids()); + + RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name + << " (" << GetStreamIdsString(stream_ids) << ")."; + SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), + stream_ids, &added_streams, + &removed_streams); + // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6 + // "Set the RTCSessionDescription: If direction is sendrecv or recvonly, + // and transceiver's current direction is neither sendrecv nor recvonly, + // process the addition of a remote track for the media description. + if (!transceiver->fired_direction() || + !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) { + RTC_LOG(LS_INFO) + << "Processing the addition of a remote track for MID=" + << content->name << "."; + now_receiving_transceivers.push_back(transceiver); + } + } + // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's + // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the + // removal of a remote track for the media description, given transceiver, + // removeList, and muteTracks. + if (!RtpTransceiverDirectionHasRecv(local_direction) && + (transceiver->fired_direction() && + RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) { + ProcessRemovalOfRemoteTrack(transceiver, &remove_list, + &removed_streams); + } + // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. + transceiver->internal()->set_fired_direction(local_direction); + // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run + // the following steps: + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to + // direction. + transceiver->internal()->set_current_direction(local_direction); + // 2.2.8.1.11.[3-6]: Set the transport internal slots. + if (transceiver->mid()) { + auto dtls_transport = + LookupDtlsTransportByMidInternal(*transceiver->mid()); + transceiver->internal()->sender_internal()->set_transport( + dtls_transport); + transceiver->internal()->receiver_internal()->set_transport( + dtls_transport); + } + } + // 2.2.8.1.12: If the media description is rejected, and transceiver is + // not already stopped, stop the RTCRtpTransceiver transceiver. + if (content->rejected && !transceiver->stopped()) { + RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name + << " since the media section was rejected."; + transceiver->Stop(); + } + if (!content->rejected && + RtpTransceiverDirectionHasRecv(local_direction)) { + if (!media_desc->streams().empty() && + media_desc->streams()[0].has_ssrcs()) { + uint32_t ssrc = media_desc->streams()[0].first_ssrc(); + transceiver->internal()->receiver_internal()->SetupMediaChannel(ssrc); + } else { + transceiver->internal() + ->receiver_internal() + ->SetupUnsignaledMediaChannel(); + } + } } - transceiver_stable_states_by_transceivers_[transceiver] - .SetRemoteStreamIdsIfUnset(transceiver->receiver()->stream_ids()); - - RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name - << " (" << GetStreamIdsString(stream_ids) << ")."; - SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), - stream_ids, &added_streams, - &removed_streams); - // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6 - // "Set the RTCSessionDescription: If direction is sendrecv or recvonly, - // and transceiver's current direction is neither sendrecv nor recvonly, - // process the addition of a remote track for the media description. - if (!transceiver->fired_direction() || - !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) { - RTC_LOG(LS_INFO) - << "Processing the addition of a remote track for MID=" - << content->name << "."; - now_receiving_transceivers.push_back(transceiver); + // Once all processing has finished, fire off callbacks. + auto observer = Observer(); + for (const auto& transceiver : now_receiving_transceivers) { + observer->OnTrack(transceiver); + observer->OnAddTrack(transceiver->receiver(), + transceiver->receiver()->streams()); } - } - // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's - // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the - // removal of a remote track for the media description, given transceiver, - // removeList, and muteTracks. - if (!RtpTransceiverDirectionHasRecv(local_direction) && - (transceiver->fired_direction() && - RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) { - ProcessRemovalOfRemoteTrack(transceiver, &remove_list, - &removed_streams); - } - // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. - transceiver->internal()->set_fired_direction(local_direction); - // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run - // the following steps: - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to - // direction. - transceiver->internal()->set_current_direction(local_direction); - // 2.2.8.1.11.[3-6]: Set the transport internal slots. - if (transceiver->mid()) { - auto dtls_transport = - LookupDtlsTransportByMidInternal(*transceiver->mid()); - transceiver->internal()->sender_internal()->set_transport( - dtls_transport); - transceiver->internal()->receiver_internal()->set_transport( - dtls_transport); + for (const auto& stream : added_streams) { + observer->OnAddStream(stream); } - } - // 2.2.8.1.12: If the media description is rejected, and transceiver is - // not already stopped, stop the RTCRtpTransceiver transceiver. - if (content->rejected && !transceiver->stopped()) { - RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name - << " since the media section was rejected."; - transceiver->Stop(); - } - if (!content->rejected && - RtpTransceiverDirectionHasRecv(local_direction)) { - if (!media_desc->streams().empty() && - media_desc->streams()[0].has_ssrcs()) { - uint32_t ssrc = media_desc->streams()[0].first_ssrc(); - transceiver->internal()->receiver_internal()->SetupMediaChannel(ssrc); - } else { - transceiver->internal() - ->receiver_internal() - ->SetupUnsignaledMediaChannel(); + for (const auto& transceiver : remove_list) { + observer->OnRemoveTrack(transceiver->receiver()); + } + for (const auto& stream : removed_streams) { + observer->OnRemoveStream(stream); } - } } - // Once all processing has finished, fire off callbacks. - auto observer = Observer(); - for (const auto& transceiver : now_receiving_transceivers) { - stats_->AddTrack(transceiver->receiver()->track()); - observer->OnTrack(transceiver); - observer->OnAddTrack(transceiver->receiver(), - transceiver->receiver()->streams()); + + const cricket::ContentInfo* audio_content = + GetFirstAudioContent(remote_description()->description()); + const cricket::ContentInfo* video_content = + GetFirstVideoContent(remote_description()->description()); + const cricket::AudioContentDescription* audio_desc = + GetFirstAudioContentDescription(remote_description()->description()); + const cricket::VideoContentDescription* video_desc = + GetFirstVideoContentDescription(remote_description()->description()); + + // Check if the descriptions include streams, just in case the peer supports + // MSID, but doesn't indicate so with "a=msid-semantic". + if (remote_description()->description()->msid_supported() || + (audio_desc && !audio_desc->streams().empty()) || + (video_desc && !video_desc->streams().empty())) { + remote_peer_supports_msid_ = true; } - for (const auto& stream : added_streams) { - observer->OnAddStream(stream); + + // We wait to signal new streams until we finish processing the description, + // since only at that point will new streams have all their tracks. + rtc::scoped_refptr new_streams(StreamCollection::Create()); + + if (!IsUnifiedPlan()) { + // 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 the SetRemoteDescription caller + // above so the cleanup that relies on them fails to run. The RemoveSenders + // 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. + if (audio_content) { + if (audio_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_AUDIO); + } else { + bool default_audio_track_needed = + !remote_peer_supports_msid_ && + RtpTransceiverDirectionHasSend(audio_desc->direction()); + UpdateRemoteSendersList(GetActiveStreams(audio_desc), + default_audio_track_needed, audio_desc->type(), + new_streams); + } + } + + // Find all video rtp streams and create corresponding remote VideoTracks + // and MediaStreams. + if (video_content) { + if (video_content->rejected) { + RemoveSenders(cricket::MEDIA_TYPE_VIDEO); + } else { + bool default_video_track_needed = + !remote_peer_supports_msid_ && + RtpTransceiverDirectionHasSend(video_desc->direction()); + UpdateRemoteSendersList(GetActiveStreams(video_desc), + default_video_track_needed, video_desc->type(), + new_streams); + } + } + + // Iterate new_streams and notify the observer about new MediaStreams. + auto observer = Observer(); + for (size_t i = 0; i < new_streams->count(); ++i) { + MediaStreamInterface* new_stream = new_streams->at(i); + observer->OnAddStream( + rtc::scoped_refptr(new_stream)); + } + + UpdateEndedRemoteMediaStreams(); } - for (const auto& transceiver : remove_list) { - observer->OnRemoveTrack(transceiver->receiver()); + + if (type == SdpType::kAnswer && + local_ice_credentials_to_replace_->SatisfiesIceRestart( + *current_local_description_)) { + local_ice_credentials_to_replace_->ClearIceCredentials(); } - for (const auto& stream : removed_streams) { - observer->OnRemoveStream(stream); - } - } - - const cricket::ContentInfo* audio_content = - GetFirstAudioContent(remote_description()->description()); - const cricket::ContentInfo* video_content = - GetFirstVideoContent(remote_description()->description()); - const cricket::AudioContentDescription* audio_desc = - GetFirstAudioContentDescription(remote_description()->description()); - const cricket::VideoContentDescription* video_desc = - GetFirstVideoContentDescription(remote_description()->description()); - - // Check if the descriptions include streams, just in case the peer supports - // MSID, but doesn't indicate so with "a=msid-semantic". - if (remote_description()->description()->msid_supported() || - (audio_desc && !audio_desc->streams().empty()) || - (video_desc && !video_desc->streams().empty())) { - remote_peer_supports_msid_ = true; - } - - // We wait to signal new streams until we finish processing the description, - // since only at that point will new streams have all their tracks. - rtc::scoped_refptr new_streams(StreamCollection::Create()); - - if (!IsUnifiedPlan()) { - // 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 the SetRemoteDescription caller - // above so the cleanup that relies on them fails to run. The RemoveSenders - // 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. - if (audio_content) { - if (audio_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_AUDIO); - } else { - bool default_audio_track_needed = - !remote_peer_supports_msid_ && - RtpTransceiverDirectionHasSend(audio_desc->direction()); - UpdateRemoteSendersList(GetActiveStreams(audio_desc), - default_audio_track_needed, audio_desc->type(), - new_streams); - } - } - - // Find all video rtp streams and create corresponding remote VideoTracks - // and MediaStreams. - if (video_content) { - if (video_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_VIDEO); - } else { - bool default_video_track_needed = - !remote_peer_supports_msid_ && - RtpTransceiverDirectionHasSend(video_desc->direction()); - UpdateRemoteSendersList(GetActiveStreams(video_desc), - default_video_track_needed, video_desc->type(), - new_streams); - } - } - - // Iterate new_streams and notify the observer about new MediaStreams. - auto observer = Observer(); - for (size_t i = 0; i < new_streams->count(); ++i) { - MediaStreamInterface* new_stream = new_streams->at(i); - stats_->AddStream(new_stream); - observer->OnAddStream( - rtc::scoped_refptr(new_stream)); - } - - UpdateEndedRemoteMediaStreams(); - } - - if (type == SdpType::kAnswer && - local_ice_credentials_to_replace_->SatisfiesIceRestart( - *current_local_description_)) { - local_ice_credentials_to_replace_->ClearIceCredentials(); - } - - return RTCError::OK(); + + return RTCError::OK(); } void TgPeerConnection::SetAssociatedRemoteStreams( - rtc::scoped_refptr receiver, - const std::vector& stream_ids, - std::vector>* added_streams, - std::vector>* removed_streams) { - std::vector> media_streams; - for (const std::string& stream_id : stream_ids) { - rtc::scoped_refptr stream = + rtc::scoped_refptr receiver, + const std::vector& stream_ids, + std::vector>* added_streams, + std::vector>* removed_streams) { + std::vector> media_streams; + for (const std::string& stream_id : stream_ids) { + rtc::scoped_refptr stream = remote_streams_->find(stream_id); - if (!stream) { - stream = MediaStreamProxy::Create(rtc::Thread::Current(), - MediaStream::Create(stream_id)); - remote_streams_->AddStream(stream); - added_streams->push_back(stream); + if (!stream) { + stream = MediaStreamProxy::Create(rtc::Thread::Current(), + MediaStream::Create(stream_id)); + remote_streams_->AddStream(stream); + added_streams->push_back(stream); + } + media_streams.push_back(stream); } - media_streams.push_back(stream); - } - // Special case: "a=msid" missing, use random stream ID. - if (media_streams.empty() && - !(remote_description()->description()->msid_signaling() & - cricket::kMsidSignalingMediaSection)) { - if (!missing_msid_default_stream_) { - missing_msid_default_stream_ = MediaStreamProxy::Create( - rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid())); - added_streams->push_back(missing_msid_default_stream_); + // Special case: "a=msid" missing, use random stream ID. + if (media_streams.empty() && + !(remote_description()->description()->msid_signaling() & + cricket::kMsidSignalingMediaSection)) { + if (!missing_msid_default_stream_) { + missing_msid_default_stream_ = MediaStreamProxy::Create( + rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid())); + added_streams->push_back(missing_msid_default_stream_); + } + media_streams.push_back(missing_msid_default_stream_); } - media_streams.push_back(missing_msid_default_stream_); - } - std::vector> previous_streams = - receiver->streams(); - // SetStreams() will add/remove the receiver's track to/from the streams. This - // differs from the spec - the spec uses an "addList" and "removeList" to - // update the stream-track relationships in a later step. We do this earlier, - // changing the order of things, but the end-result is the same. - // TODO(hbos): When we remove remote_streams(), use set_stream_ids() - // instead. https://crbug.com/webrtc/9480 - receiver->SetStreams(media_streams); - RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); + std::vector> previous_streams = + receiver->streams(); + // SetStreams() will add/remove the receiver's track to/from the streams. This + // differs from the spec - the spec uses an "addList" and "removeList" to + // update the stream-track relationships in a later step. We do this earlier, + // changing the order of things, but the end-result is the same. + // TODO(hbos): When we remove remote_streams(), use set_stream_ids() + // instead. https://crbug.com/webrtc/9480 + receiver->SetStreams(media_streams); + RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); } void TgPeerConnection::ProcessRemovalOfRemoteTrack( - rtc::scoped_refptr> - transceiver, - std::vector>* remove_list, - std::vector>* removed_streams) { - RTC_DCHECK(transceiver->mid()); - RTC_LOG(LS_INFO) << "Processing the removal of a track for MID=" - << *transceiver->mid(); - std::vector> previous_streams = - transceiver->internal()->receiver_internal()->streams(); - // This will remove the remote track from the streams. - transceiver->internal()->receiver_internal()->set_stream_ids({}); - remove_list->push_back(transceiver); - RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); + rtc::scoped_refptr> + transceiver, + std::vector>* remove_list, + std::vector>* removed_streams) { + RTC_DCHECK(transceiver->mid()); + RTC_LOG(LS_INFO) << "Processing the removal of a track for MID=" + << *transceiver->mid(); + std::vector> previous_streams = + transceiver->internal()->receiver_internal()->streams(); + // This will remove the remote track from the streams. + transceiver->internal()->receiver_internal()->set_stream_ids({}); + remove_list->push_back(transceiver); + RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); } void TgPeerConnection::RemoveRemoteStreamsIfEmpty( - const std::vector>& remote_streams, - std::vector>* removed_streams) { - // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of - // streams, see if the stream was removed by checking if this was the last - // receiver with that stream ID. - for (const auto& remote_stream : remote_streams) { - if (remote_stream->GetAudioTracks().empty() && - remote_stream->GetVideoTracks().empty()) { - remote_streams_->RemoveStream(remote_stream); - removed_streams->push_back(remote_stream); + const std::vector>& remote_streams, + std::vector>* removed_streams) { + // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of + // streams, see if the stream was removed by checking if this was the last + // receiver with that stream ID. + for (const auto& remote_stream : remote_streams) { + if (remote_stream->GetAudioTracks().empty() && + remote_stream->GetVideoTracks().empty()) { + remote_streams_->RemoveStream(remote_stream); + removed_streams->push_back(remote_stream); + } } - } } RTCError TgPeerConnection::UpdateTransceiversAndDataChannels( - cricket::ContentSource source, - const SessionDescriptionInterface& new_session, - const SessionDescriptionInterface* old_local_description, - const SessionDescriptionInterface* old_remote_description) { - RTC_DCHECK(IsUnifiedPlan()); - - const cricket::ContentGroup* bundle_group = nullptr; - if (new_session.GetType() == SdpType::kOffer) { - auto bundle_group_or_error = + cricket::ContentSource source, + const SessionDescriptionInterface& new_session, + const SessionDescriptionInterface* old_local_description, + const SessionDescriptionInterface* old_remote_description) { + RTC_DCHECK(IsUnifiedPlan()); + + const cricket::ContentGroup* bundle_group = nullptr; + if (new_session.GetType() == SdpType::kOffer) { + auto bundle_group_or_error = GetEarlyBundleGroup(*new_session.description()); - if (!bundle_group_or_error.ok()) { - return bundle_group_or_error.MoveError(); + if (!bundle_group_or_error.ok()) { + return bundle_group_or_error.MoveError(); + } + bundle_group = bundle_group_or_error.MoveValue(); } - bundle_group = bundle_group_or_error.MoveValue(); - } - - const ContentInfos& new_contents = new_session.description()->contents(); - for (size_t i = 0; i < new_contents.size(); ++i) { - const cricket::ContentInfo& new_content = new_contents[i]; - cricket::MediaType media_type = new_content.media_description()->type(); - mid_generator_.AddKnownId(new_content.name); - if (media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO) { - const cricket::ContentInfo* old_local_content = nullptr; - if (old_local_description && - i < old_local_description->description()->contents().size()) { - old_local_content = - &old_local_description->description()->contents()[i]; - } - const cricket::ContentInfo* old_remote_content = nullptr; - if (old_remote_description && - i < old_remote_description->description()->contents().size()) { - old_remote_content = - &old_remote_description->description()->contents()[i]; - } - auto transceiver_or_error = - AssociateTransceiver(source, new_session.GetType(), i, new_content, - old_local_content, old_remote_content); - if (!transceiver_or_error.ok()) { - return transceiver_or_error.MoveError(); - } - auto transceiver = transceiver_or_error.MoveValue(); - RTCError error = - UpdateTransceiverChannel(transceiver, new_content, bundle_group); - if (!error.ok()) { - return error; - } - } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Unknown section type."); + + const ContentInfos& new_contents = new_session.description()->contents(); + for (size_t i = 0; i < new_contents.size(); ++i) { + const cricket::ContentInfo& new_content = new_contents[i]; + cricket::MediaType media_type = new_content.media_description()->type(); + mid_generator_.AddKnownId(new_content.name); + if (media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO) { + const cricket::ContentInfo* old_local_content = nullptr; + if (old_local_description && + i < old_local_description->description()->contents().size()) { + old_local_content = + &old_local_description->description()->contents()[i]; + } + const cricket::ContentInfo* old_remote_content = nullptr; + if (old_remote_description && + i < old_remote_description->description()->contents().size()) { + old_remote_content = + &old_remote_description->description()->contents()[i]; + } + auto transceiver_or_error = + AssociateTransceiver(source, new_session.GetType(), i, new_content, + old_local_content, old_remote_content); + if (!transceiver_or_error.ok()) { + return transceiver_or_error.MoveError(); + } + auto transceiver = transceiver_or_error.MoveValue(); + RTCError error = + UpdateTransceiverChannel(transceiver, new_content, bundle_group); + if (!error.ok()) { + return error; + } + } else { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Unknown section type."); + } } - } - - return RTCError::OK(); + + return RTCError::OK(); } RTCError TgPeerConnection::UpdateTransceiverChannel( - rtc::scoped_refptr> - transceiver, - const cricket::ContentInfo& content, - const cricket::ContentGroup* bundle_group) { - RTC_DCHECK(IsUnifiedPlan()); - RTC_DCHECK(transceiver); - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (content.rejected) { - if (channel) { - transceiver->internal()->SetChannel(nullptr); - DestroyChannelInterface(channel); + rtc::scoped_refptr> + transceiver, + const cricket::ContentInfo& content, + const cricket::ContentGroup* bundle_group) { + RTC_DCHECK(IsUnifiedPlan()); + RTC_DCHECK(transceiver); + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (content.rejected) { + if (channel) { + transceiver->internal()->SetChannel(nullptr); + DestroyChannelInterface(channel); + } + } else { + if (!channel) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + channel = CreateVoiceChannel(content.name); + } else { + RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->media_type()); + channel = CreateVideoChannel(content.name); + } + if (!channel) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INTERNAL_ERROR, + "Failed to create channel for mid=" + content.name); + } + transceiver->internal()->SetChannel(channel); + } } - } else { - if (!channel) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - channel = CreateVoiceChannel(content.name); - } else { - RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->media_type()); - channel = CreateVideoChannel(content.name); - } - if (!channel) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "Failed to create channel for mid=" + content.name); - } - transceiver->internal()->SetChannel(channel); - } - } - return RTCError::OK(); + return RTCError::OK(); } // This method will extract any send encodings that were sent by the remote // connection. This is currently only relevant for Simulcast scenario (where // the number of layers may be communicated by the server). static std::vector GetSendEncodingsFromRemoteDescription( - const MediaContentDescription& desc) { - if (!desc.HasSimulcast()) { - return {}; - } - std::vector result; - const SimulcastDescription& simulcast = desc.simulcast_description(); - - // This is a remote description, the parameters we are after should appear - // as receive streams. - for (const auto& alternatives : simulcast.receive_layers()) { - RTC_DCHECK(!alternatives.empty()); - // There is currently no way to specify or choose from alternatives. - // We will always use the first alternative, which is the most preferred. - const SimulcastLayer& layer = alternatives[0]; - RtpEncodingParameters parameters; - parameters.rid = layer.rid; - parameters.active = !layer.is_paused; - result.push_back(parameters); - } - - return result; + const MediaContentDescription& desc) { + if (!desc.HasSimulcast()) { + return {}; + } + std::vector result; + const SimulcastDescription& simulcast = desc.simulcast_description(); + + // This is a remote description, the parameters we are after should appear + // as receive streams. + for (const auto& alternatives : simulcast.receive_layers()) { + RTC_DCHECK(!alternatives.empty()); + // There is currently no way to specify or choose from alternatives. + // We will always use the first alternative, which is the most preferred. + const SimulcastLayer& layer = alternatives[0]; + RtpEncodingParameters parameters; + parameters.rid = layer.rid; + parameters.active = !layer.is_paused; + result.push_back(parameters); + } + + return result; } static RTCError UpdateSimulcastLayerStatusInSender( - const std::vector& layers, - rtc::scoped_refptr sender) { - RTC_DCHECK(sender); - RtpParameters parameters = sender->GetParametersInternal(); - std::vector disabled_layers; - - // The simulcast envelope cannot be changed, only the status of the streams. - // So we will iterate over the send encodings rather than the layers. - for (RtpEncodingParameters& encoding : parameters.encodings) { - auto iter = std::find_if(layers.begin(), layers.end(), - [&encoding](const SimulcastLayer& layer) { - return layer.rid == encoding.rid; - }); - // A layer that cannot be found may have been removed by the remote party. - if (iter == layers.end()) { - disabled_layers.push_back(encoding.rid); - continue; + const std::vector& layers, + rtc::scoped_refptr sender) { + RTC_DCHECK(sender); + RtpParameters parameters = sender->GetParametersInternal(); + std::vector disabled_layers; + + // The simulcast envelope cannot be changed, only the status of the streams. + // So we will iterate over the send encodings rather than the layers. + for (RtpEncodingParameters& encoding : parameters.encodings) { + auto iter = std::find_if(layers.begin(), layers.end(), + [&encoding](const SimulcastLayer& layer) { + return layer.rid == encoding.rid; + }); + // A layer that cannot be found may have been removed by the remote party. + if (iter == layers.end()) { + disabled_layers.push_back(encoding.rid); + continue; + } + + encoding.active = !iter->is_paused; } - - encoding.active = !iter->is_paused; - } - - RTCError result = sender->SetParametersInternal(parameters); - if (result.ok()) { - result = sender->DisableEncodingLayers(disabled_layers); - } - - return result; + + RTCError result = sender->SetParametersInternal(parameters); + if (result.ok()) { + result = sender->DisableEncodingLayers(disabled_layers); + } + + return result; } static bool SimulcastIsRejected( - const ContentInfo* local_content, - const MediaContentDescription& answer_media_desc) { - bool simulcast_offered = local_content && - local_content->media_description() && - local_content->media_description()->HasSimulcast(); - bool simulcast_answered = answer_media_desc.HasSimulcast(); - bool rids_supported = RtpExtension::FindHeaderExtensionByUri( - answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri); - return simulcast_offered && (!simulcast_answered || !rids_supported); + const ContentInfo* local_content, + const MediaContentDescription& answer_media_desc) { + bool simulcast_offered = local_content && + local_content->media_description() && + local_content->media_description()->HasSimulcast(); + bool simulcast_answered = answer_media_desc.HasSimulcast(); + bool rids_supported = RtpExtension::FindHeaderExtensionByUri( + answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri); + return simulcast_offered && (!simulcast_answered || !rids_supported); } static RTCError DisableSimulcastInSender( - rtc::scoped_refptr sender) { - RTC_DCHECK(sender); - RtpParameters parameters = sender->GetParametersInternal(); - if (parameters.encodings.size() <= 1) { - return RTCError::OK(); - } - - std::vector disabled_layers; - std::transform( - parameters.encodings.begin() + 1, parameters.encodings.end(), - std::back_inserter(disabled_layers), - [](const RtpEncodingParameters& encoding) { return encoding.rid; }); - return sender->DisableEncodingLayers(disabled_layers); + rtc::scoped_refptr sender) { + RTC_DCHECK(sender); + RtpParameters parameters = sender->GetParametersInternal(); + if (parameters.encodings.size() <= 1) { + return RTCError::OK(); + } + + std::vector disabled_layers; + std::transform( + parameters.encodings.begin() + 1, parameters.encodings.end(), + std::back_inserter(disabled_layers), + [](const RtpEncodingParameters& encoding) { return encoding.rid; }); + return sender->DisableEncodingLayers(disabled_layers); } RTCErrorOr>> TgPeerConnection::AssociateTransceiver(cricket::ContentSource source, - SdpType type, - size_t mline_index, - const ContentInfo& content, - const ContentInfo* old_local_content, - const ContentInfo* old_remote_content) { - RTC_DCHECK(IsUnifiedPlan()); - // If this is an offer then the m= section might be recycled. If the m= - // section is being recycled (defined as: rejected in the current local or - // remote description and not rejected in new description), dissociate the - // currently associated RtpTransceiver by setting its mid property to null, - // and discard the mapping between the transceiver and its m= section index. - if (IsMediaSectionBeingRecycled(type, content, old_local_content, - old_remote_content)) { - // We want to dissociate the transceiver that has the rejected mid. - const std::string& old_mid = + SdpType type, + size_t mline_index, + const ContentInfo& content, + const ContentInfo* old_local_content, + const ContentInfo* old_remote_content) { + RTC_DCHECK(IsUnifiedPlan()); + // If this is an offer then the m= section might be recycled. If the m= + // section is being recycled (defined as: rejected in the current local or + // remote description and not rejected in new description), dissociate the + // currently associated RtpTransceiver by setting its mid property to null, + // and discard the mapping between the transceiver and its m= section index. + if (IsMediaSectionBeingRecycled(type, content, old_local_content, + old_remote_content)) { + // We want to dissociate the transceiver that has the rejected mid. + const std::string& old_mid = (old_local_content && old_local_content->rejected) - ? old_local_content->name - : old_remote_content->name; - auto old_transceiver = GetAssociatedTransceiver(old_mid); - if (old_transceiver) { - RTC_LOG(LS_INFO) << "Dissociating transceiver for MID=" << old_mid - << " since the media section is being recycled."; - old_transceiver->internal()->set_mid(absl::nullopt); - old_transceiver->internal()->set_mline_index(absl::nullopt); + ? old_local_content->name + : old_remote_content->name; + auto old_transceiver = GetAssociatedTransceiver(old_mid); + if (old_transceiver) { + RTC_LOG(LS_INFO) << "Dissociating transceiver for MID=" << old_mid + << " since the media section is being recycled."; + old_transceiver->internal()->set_mid(absl::nullopt); + old_transceiver->internal()->set_mline_index(absl::nullopt); + } } - } - const MediaContentDescription* media_desc = content.media_description(); - auto transceiver = GetAssociatedTransceiver(content.name); - if (source == cricket::CS_LOCAL) { - // Find the RtpTransceiver that corresponds to this m= section, using the - // mapping between transceivers and m= section indices established when - // creating the offer. - if (!transceiver) { - transceiver = GetTransceiverByMLineIndex(mline_index); + const MediaContentDescription* media_desc = content.media_description(); + auto transceiver = GetAssociatedTransceiver(content.name); + if (source == cricket::CS_LOCAL) { + // Find the RtpTransceiver that corresponds to this m= section, using the + // mapping between transceivers and m= section indices established when + // creating the offer. + if (!transceiver) { + transceiver = GetTransceiverByMLineIndex(mline_index); + } + if (!transceiver) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Unknown transceiver"); + } + } else { + RTC_DCHECK_EQ(source, cricket::CS_REMOTE); + // If the m= section is sendrecv or recvonly, and there are RtpTransceivers + // of the same type... + // When simulcast is requested, a transceiver cannot be associated because + // AddTrack cannot be called to initialize it. + if (!transceiver && + RtpTransceiverDirectionHasRecv(media_desc->direction()) && + !media_desc->HasSimulcast()) { + transceiver = FindAvailableTransceiverToReceive(media_desc->type()); + } + // If no RtpTransceiver was found in the previous step, create one with a + // recvonly direction. + if (!transceiver) { + RTC_LOG(LS_INFO) << "Adding " + << cricket::MediaTypeToString(media_desc->type()) + << " transceiver for MID=" << content.name + << " at i=" << mline_index + << " in response to the remote description."; + std::string sender_id = rtc::CreateRandomUuid(); + std::vector send_encodings = + GetSendEncodingsFromRemoteDescription(*media_desc); + auto sender = CreateSender(media_desc->type(), sender_id, nullptr, {}, + send_encodings); + std::string receiver_id; + if (!media_desc->streams().empty()) { + receiver_id = media_desc->streams()[0].id; + } else { + receiver_id = rtc::CreateRandomUuid(); + } + auto receiver = CreateReceiver(media_desc->type(), receiver_id); + transceiver = CreateAndAddTransceiver(sender, receiver); + transceiver->internal()->set_direction( + RtpTransceiverDirection::kRecvOnly); + if (type == SdpType::kOffer) { + transceiver_stable_states_by_transceivers_[transceiver] + .set_newly_created(); + } + } + // Check if the offer indicated simulcast but the answer rejected it. + // This can happen when simulcast is not supported on the remote party. + if (SimulcastIsRejected(old_local_content, *media_desc)) { + RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true); + /*RTCError error = + DisableSimulcastInSender(transceiver->internal()->sender_internal()); + if (!error.ok()) { + RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast."; + return std::move(error); + }*/ + } } - if (!transceiver) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Unknown transceiver"); + RTC_DCHECK(transceiver); + if (transceiver->media_type() != media_desc->type()) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_PARAMETER, + "Transceiver type does not match media description type."); } - } else { - RTC_DCHECK_EQ(source, cricket::CS_REMOTE); - // If the m= section is sendrecv or recvonly, and there are RtpTransceivers - // of the same type... - // When simulcast is requested, a transceiver cannot be associated because - // AddTrack cannot be called to initialize it. - if (!transceiver && - RtpTransceiverDirectionHasRecv(media_desc->direction()) && - !media_desc->HasSimulcast()) { - transceiver = FindAvailableTransceiverToReceive(media_desc->type()); - } - // If no RtpTransceiver was found in the previous step, create one with a - // recvonly direction. - if (!transceiver) { - RTC_LOG(LS_INFO) << "Adding " - << cricket::MediaTypeToString(media_desc->type()) - << " transceiver for MID=" << content.name - << " at i=" << mline_index - << " in response to the remote description."; - std::string sender_id = rtc::CreateRandomUuid(); - std::vector send_encodings = - GetSendEncodingsFromRemoteDescription(*media_desc); - auto sender = CreateSender(media_desc->type(), sender_id, nullptr, {}, - send_encodings); - std::string receiver_id; - if (!media_desc->streams().empty()) { - receiver_id = media_desc->streams()[0].id; - } else { - receiver_id = rtc::CreateRandomUuid(); - } - auto receiver = CreateReceiver(media_desc->type(), receiver_id); - transceiver = CreateAndAddTransceiver(sender, receiver); - transceiver->internal()->set_direction( - RtpTransceiverDirection::kRecvOnly); - if (type == SdpType::kOffer) { - transceiver_stable_states_by_transceivers_[transceiver] - .set_newly_created(); - } - } - // Check if the offer indicated simulcast but the answer rejected it. - // This can happen when simulcast is not supported on the remote party. - if (SimulcastIsRejected(old_local_content, *media_desc)) { - RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true); - RTCError error = - DisableSimulcastInSender(transceiver->internal()->sender_internal()); - if (!error.ok()) { - RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast."; - return std::move(error); - } - } - } - RTC_DCHECK(transceiver); - if (transceiver->media_type() != media_desc->type()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Transceiver type does not match media description type."); - } - if (media_desc->HasSimulcast()) { - std::vector layers = + if (media_desc->HasSimulcast()) { + std::vector layers = source == cricket::CS_LOCAL - ? media_desc->simulcast_description().send_layers().GetAllLayers() - : media_desc->simulcast_description() - .receive_layers() - .GetAllLayers(); - RTCError error = UpdateSimulcastLayerStatusInSender( - layers, transceiver->internal()->sender_internal()); - if (!error.ok()) { - RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers."; - return std::move(error); + ? media_desc->simulcast_description().send_layers().GetAllLayers() + : media_desc->simulcast_description() + .receive_layers() + .GetAllLayers(); + RTCError error = UpdateSimulcastLayerStatusInSender( + layers, transceiver->internal()->sender_internal()); + if (!error.ok()) { + RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers."; + return std::move(error); + } } - } - if (type == SdpType::kOffer) { - bool state_changes = transceiver->internal()->mid() != content.name || - transceiver->internal()->mline_index() != mline_index; - if (state_changes) { - transceiver_stable_states_by_transceivers_[transceiver] - .SetMSectionIfUnset(transceiver->internal()->mid(), - transceiver->internal()->mline_index()); + if (type == SdpType::kOffer) { + bool state_changes = transceiver->internal()->mid() != content.name || + transceiver->internal()->mline_index() != mline_index; + if (state_changes) { + transceiver_stable_states_by_transceivers_[transceiver] + .SetMSectionIfUnset(transceiver->internal()->mid(), + transceiver->internal()->mline_index()); + } } - } - // Associate the found or created RtpTransceiver with the m= section by - // setting the value of the RtpTransceiver's mid property to the MID of the m= - // section, and establish a mapping between the transceiver and the index of - // the m= section. - transceiver->internal()->set_mid(content.name); - transceiver->internal()->set_mline_index(mline_index); - return std::move(transceiver); + // Associate the found or created RtpTransceiver with the m= section by + // setting the value of the RtpTransceiver's mid property to the MID of the m= + // section, and establish a mapping between the transceiver and the index of + // the m= section. + transceiver->internal()->set_mid(content.name); + transceiver->internal()->set_mline_index(mline_index); + return std::move(transceiver); } rtc::scoped_refptr> TgPeerConnection::GetAssociatedTransceiver(const std::string& mid) const { - RTC_DCHECK(IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->mid() == mid) { - return transceiver; + RTC_DCHECK(IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->mid() == mid) { + return transceiver; + } } - } - return nullptr; + return nullptr; } rtc::scoped_refptr> TgPeerConnection::GetTransceiverByMLineIndex(size_t mline_index) const { - RTC_DCHECK(IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->internal()->mline_index() == mline_index) { - return transceiver; + RTC_DCHECK(IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->internal()->mline_index() == mline_index) { + return transceiver; + } } - } - return nullptr; + return nullptr; } rtc::scoped_refptr> TgPeerConnection::FindAvailableTransceiverToReceive( - cricket::MediaType media_type) const { - RTC_DCHECK(IsUnifiedPlan()); - // From JSEP section 5.10 (Applying a Remote Description): - // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of - // the same type that were added to the TgPeerConnection by addTrack and are not - // associated with any m= section and are not stopped, find the first such - // RtpTransceiver. - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == media_type && - transceiver->internal()->created_by_addtrack() && !transceiver->mid() && - !transceiver->stopped()) { - return transceiver; + cricket::MediaType media_type) const { + RTC_DCHECK(IsUnifiedPlan()); + // From JSEP section 5.10 (Applying a Remote Description): + // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of + // the same type that were added to the TgPeerConnection by addTrack and are not + // associated with any m= section and are not stopped, find the first such + // RtpTransceiver. + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == media_type && + transceiver->internal()->created_by_addtrack() && !transceiver->mid() && + !transceiver->stopped()) { + return transceiver; + } } - } - return nullptr; + return nullptr; } const cricket::ContentInfo* TgPeerConnection::FindMediaSectionForTransceiver( - rtc::scoped_refptr> - transceiver, - const SessionDescriptionInterface* sdesc) const { - RTC_DCHECK(transceiver); - RTC_DCHECK(sdesc); - if (IsUnifiedPlan()) { - if (!transceiver->internal()->mid()) { - // This transceiver is not associated with a media section yet. - return nullptr; + rtc::scoped_refptr> + transceiver, + const SessionDescriptionInterface* sdesc) const { + RTC_DCHECK(transceiver); + RTC_DCHECK(sdesc); + if (IsUnifiedPlan()) { + if (!transceiver->internal()->mid()) { + // This transceiver is not associated with a media section yet. + return nullptr; + } + return sdesc->description()->GetContentByName( + *transceiver->internal()->mid()); + } else { + // Plan B only allows at most one audio and one video section, so use the + // first media section of that type. + return cricket::GetFirstMediaContent(sdesc->description()->contents(), + transceiver->media_type()); } - return sdesc->description()->GetContentByName( - *transceiver->internal()->mid()); - } else { - // Plan B only allows at most one audio and one video section, so use the - // first media section of that type. - return cricket::GetFirstMediaContent(sdesc->description()->contents(), - transceiver->media_type()); - } } PeerConnectionInterface::RTCConfiguration TgPeerConnection::GetConfiguration() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return configuration_; + RTC_DCHECK_RUN_ON(signaling_thread()); + return configuration_; } RTCError TgPeerConnection::SetConfiguration( - const RTCConfiguration& configuration) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::SetConfiguration"); - if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "SetConfiguration: TgPeerConnection is closed."); - } - - // According to JSEP, after setLocalDescription, changing the candidate pool - // size is not allowed, and changing the set of ICE servers will not result - // in new candidates being gathered. - if (local_description() && configuration.ice_candidate_pool_size != - configuration_.ice_candidate_pool_size) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change candidate pool size after calling " - "SetLocalDescription."); - } - - if (local_description() && - configuration.crypto_options != configuration_.crypto_options) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change crypto_options after calling " - "SetLocalDescription."); - } - - if (local_description() && configuration.use_datagram_transport != - configuration_.use_datagram_transport) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport " - "after calling SetLocalDescription."); - } - - if (remote_description() && configuration.use_datagram_transport != - configuration_.use_datagram_transport) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport " - "after calling SetRemoteDescription."); - } - - if (local_description() && - configuration.use_datagram_transport_for_data_channels != - configuration_.use_datagram_transport_for_data_channels) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels " - "after calling SetLocalDescription."); - } - - if (remote_description() && - configuration.use_datagram_transport_for_data_channels != - configuration_.use_datagram_transport_for_data_channels) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels " - "after calling SetRemoteDescription."); - } - - if (local_description() && - configuration.use_datagram_transport_for_data_channels_receive_only != - configuration_ - .use_datagram_transport_for_data_channels_receive_only) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels_receive_only " - "after calling SetLocalDescription."); - } - - if (remote_description() && - configuration.use_datagram_transport_for_data_channels_receive_only != - configuration_ - .use_datagram_transport_for_data_channels_receive_only) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels_receive_only " - "after calling SetRemoteDescription."); - } - - if ((configuration.use_datagram_transport && - *configuration.use_datagram_transport) || - (configuration.use_datagram_transport_for_data_channels && - *configuration.use_datagram_transport_for_data_channels)) { - RTC_CHECK(configuration.bundle_policy == kBundlePolicyMaxBundle) - << "Media transport requires MaxBundle policy."; - } - - // The simplest (and most future-compatible) way to tell if the config was - // modified in an invalid way is to copy each property we do support - // modifying, then use operator==. There are far more properties we don't - // support modifying than those we do, and more could be added. - RTCConfiguration modified_config = configuration_; - modified_config.servers = configuration.servers; - modified_config.type = configuration.type; - modified_config.ice_candidate_pool_size = - configuration.ice_candidate_pool_size; - modified_config.prune_turn_ports = configuration.prune_turn_ports; - modified_config.turn_port_prune_policy = configuration.turn_port_prune_policy; - modified_config.surface_ice_candidates_on_ice_transport_type_changed = - configuration.surface_ice_candidates_on_ice_transport_type_changed; - modified_config.ice_check_min_interval = configuration.ice_check_min_interval; - modified_config.ice_check_interval_strong_connectivity = - configuration.ice_check_interval_strong_connectivity; - modified_config.ice_check_interval_weak_connectivity = - configuration.ice_check_interval_weak_connectivity; - modified_config.ice_unwritable_timeout = configuration.ice_unwritable_timeout; - modified_config.ice_unwritable_min_checks = - configuration.ice_unwritable_min_checks; - modified_config.ice_inactive_timeout = configuration.ice_inactive_timeout; - modified_config.stun_candidate_keepalive_interval = - configuration.stun_candidate_keepalive_interval; - modified_config.turn_customizer = configuration.turn_customizer; - modified_config.network_preference = configuration.network_preference; - modified_config.active_reset_srtp_params = - configuration.active_reset_srtp_params; - modified_config.use_datagram_transport = configuration.use_datagram_transport; - modified_config.use_datagram_transport_for_data_channels = - configuration.use_datagram_transport_for_data_channels; - modified_config.use_datagram_transport_for_data_channels_receive_only = - configuration.use_datagram_transport_for_data_channels_receive_only; - modified_config.turn_logging_id = configuration.turn_logging_id; - modified_config.allow_codec_switching = configuration.allow_codec_switching; - if (configuration != modified_config) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Modifying the configuration in an unsupported way."); - } - - // Validate the modified configuration. - RTCError validate_error = ValidateConfiguration(modified_config); - if (!validate_error.ok()) { - return validate_error; - } - - // Note that this isn't possible through chromium, since it's an unsigned - // short in WebIDL. - if (configuration.ice_candidate_pool_size < 0 || - configuration.ice_candidate_pool_size > static_cast(UINT16_MAX)) { - return RTCError(RTCErrorType::INVALID_RANGE); - } - - // Parse ICE servers before hopping to network thread. - cricket::ServerAddresses stun_servers; - std::vector turn_servers; - RTCErrorType parse_error = - ParseIceServers(configuration.servers, &stun_servers, &turn_servers); - if (parse_error != RTCErrorType::NONE) { - return RTCError(parse_error); - } - // Add the turn logging id to all turn servers - for (cricket::RelayServerConfig& turn_server : turn_servers) { - turn_server.turn_logging_id = configuration.turn_logging_id; - } - - // Note if STUN or TURN servers were supplied. - if (!stun_servers.empty()) { - NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); - } - if (!turn_servers.empty()) { - NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); - } - - // In theory this shouldn't fail. - if (!network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::ReconfigurePortAllocator_n, this, - stun_servers, turn_servers, modified_config.type, - modified_config.ice_candidate_pool_size, - modified_config.GetTurnPortPrunePolicy(), - modified_config.turn_customizer, - modified_config.stun_candidate_keepalive_interval, - static_cast(local_description())))) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to apply configuration to PortAllocator."); - } - - // As described in JSEP, calling setConfiguration with new ICE servers or - // candidate policy must set a "needs-ice-restart" bit so that the next offer - // triggers an ICE restart which will pick up the changes. - if (modified_config.servers != configuration_.servers || - modified_config.type != configuration_.type || - modified_config.GetTurnPortPrunePolicy() != - configuration_.GetTurnPortPrunePolicy()) { - transport_controller_->SetNeedsIceRestartFlag(); - } - - transport_controller_->SetIceConfig(ParseIceConfig(modified_config)); - - use_datagram_transport_ = datagram_transport_config_.enabled && - modified_config.use_datagram_transport.value_or( - datagram_transport_config_.default_value); - use_datagram_transport_for_data_channels_ = - datagram_transport_data_channel_config_.enabled && - modified_config.use_datagram_transport_for_data_channels.value_or( - datagram_transport_data_channel_config_.default_value); - use_datagram_transport_for_data_channels_receive_only_ = - modified_config.use_datagram_transport_for_data_channels_receive_only - .value_or(datagram_transport_data_channel_config_.receive_only); - transport_controller_->SetMediaTransportSettings( - use_datagram_transport_, use_datagram_transport_for_data_channels_, - use_datagram_transport_for_data_channels_receive_only_); - - if (configuration_.active_reset_srtp_params != - modified_config.active_reset_srtp_params) { - transport_controller_->SetActiveResetSrtpParams( - modified_config.active_reset_srtp_params); - } - - if (modified_config.allow_codec_switching.has_value()) { - cricket::VideoMediaChannel* video_channel = video_media_channel(); - if (video_channel) { - video_channel->SetVideoCodecSwitchingEnabled( - *modified_config.allow_codec_switching); + const PeerConnectionInterface::RTCConfiguration& configuration) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::SetConfiguration"); + if (IsClosed()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "SetConfiguration: TgPeerConnection is closed."); } - } - - configuration_ = modified_config; - return RTCError::OK(); + + // According to JSEP, after setLocalDescription, changing the candidate pool + // size is not allowed, and changing the set of ICE servers will not result + // in new candidates being gathered. + if (local_description() && configuration.ice_candidate_pool_size != + configuration_.ice_candidate_pool_size) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change candidate pool size after calling " + "SetLocalDescription."); + } + + if (local_description() && + configuration.crypto_options != configuration_.crypto_options) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change crypto_options after calling " + "SetLocalDescription."); + } + + if (local_description() && configuration.use_datagram_transport != + configuration_.use_datagram_transport) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport " + "after calling SetLocalDescription."); + } + + if (remote_description() && configuration.use_datagram_transport != + configuration_.use_datagram_transport) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport " + "after calling SetRemoteDescription."); + } + + if (local_description() && + configuration.use_datagram_transport_for_data_channels != + configuration_.use_datagram_transport_for_data_channels) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels " + "after calling SetLocalDescription."); + } + + if (remote_description() && + configuration.use_datagram_transport_for_data_channels != + configuration_.use_datagram_transport_for_data_channels) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels " + "after calling SetRemoteDescription."); + } + + if (local_description() && + configuration.use_datagram_transport_for_data_channels_receive_only != + configuration_ + .use_datagram_transport_for_data_channels_receive_only) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels_receive_only " + "after calling SetLocalDescription."); + } + + if (remote_description() && + configuration.use_datagram_transport_for_data_channels_receive_only != + configuration_ + .use_datagram_transport_for_data_channels_receive_only) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Can't change use_datagram_transport_for_data_channels_receive_only " + "after calling SetRemoteDescription."); + } + + if ((configuration.use_datagram_transport && + *configuration.use_datagram_transport) || + (configuration.use_datagram_transport_for_data_channels && + *configuration.use_datagram_transport_for_data_channels)) { + RTC_CHECK(configuration.bundle_policy == PeerConnectionInterface::kBundlePolicyMaxBundle) + << "Media transport requires MaxBundle policy."; + } + + // The simplest (and most future-compatible) way to tell if the config was + // modified in an invalid way is to copy each property we do support + // modifying, then use operator==. There are far more properties we don't + // support modifying than those we do, and more could be added. + PeerConnectionInterface::RTCConfiguration modified_config = configuration_; + modified_config.servers = configuration.servers; + modified_config.type = configuration.type; + modified_config.ice_candidate_pool_size = + configuration.ice_candidate_pool_size; + modified_config.prune_turn_ports = configuration.prune_turn_ports; + modified_config.turn_port_prune_policy = configuration.turn_port_prune_policy; + modified_config.surface_ice_candidates_on_ice_transport_type_changed = + configuration.surface_ice_candidates_on_ice_transport_type_changed; + modified_config.ice_check_min_interval = configuration.ice_check_min_interval; + modified_config.ice_check_interval_strong_connectivity = + configuration.ice_check_interval_strong_connectivity; + modified_config.ice_check_interval_weak_connectivity = + configuration.ice_check_interval_weak_connectivity; + modified_config.ice_unwritable_timeout = configuration.ice_unwritable_timeout; + modified_config.ice_unwritable_min_checks = + configuration.ice_unwritable_min_checks; + modified_config.ice_inactive_timeout = configuration.ice_inactive_timeout; + modified_config.stun_candidate_keepalive_interval = + configuration.stun_candidate_keepalive_interval; + modified_config.turn_customizer = configuration.turn_customizer; + modified_config.network_preference = configuration.network_preference; + modified_config.active_reset_srtp_params = + configuration.active_reset_srtp_params; + modified_config.use_datagram_transport = configuration.use_datagram_transport; + modified_config.use_datagram_transport_for_data_channels = + configuration.use_datagram_transport_for_data_channels; + modified_config.use_datagram_transport_for_data_channels_receive_only = + configuration.use_datagram_transport_for_data_channels_receive_only; + modified_config.turn_logging_id = configuration.turn_logging_id; + modified_config.allow_codec_switching = configuration.allow_codec_switching; + if (configuration != modified_config) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Modifying the configuration in an unsupported way."); + } + + // Validate the modified configuration. + RTCError validate_error = ValidateConfiguration(modified_config); + if (!validate_error.ok()) { + return validate_error; + } + + // Note that this isn't possible through chromium, since it's an unsigned + // short in WebIDL. + if (configuration.ice_candidate_pool_size < 0 || + configuration.ice_candidate_pool_size > static_cast(UINT16_MAX)) { + return RTCError(RTCErrorType::INVALID_RANGE); + } + + // Parse ICE servers before hopping to network thread. + cricket::ServerAddresses stun_servers; + std::vector turn_servers; + RTCErrorType parse_error = + ParseIceServers(configuration.servers, &stun_servers, &turn_servers); + if (parse_error != RTCErrorType::NONE) { + return RTCError(parse_error); + } + // Add the turn logging id to all turn servers + for (cricket::RelayServerConfig& turn_server : turn_servers) { + turn_server.turn_logging_id = configuration.turn_logging_id; + } + + // Note if STUN or TURN servers were supplied. + if (!stun_servers.empty()) { + NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); + } + if (!turn_servers.empty()) { + NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); + } + + // In theory this shouldn't fail. + if (!network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::ReconfigurePortAllocator_n, this, + stun_servers, turn_servers, modified_config.type, + modified_config.ice_candidate_pool_size, + modified_config.GetTurnPortPrunePolicy(), + modified_config.turn_customizer, + modified_config.stun_candidate_keepalive_interval, + static_cast(local_description())))) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Failed to apply configuration to PortAllocator."); + } + + // As described in JSEP, calling setConfiguration with new ICE servers or + // candidate policy must set a "needs-ice-restart" bit so that the next offer + // triggers an ICE restart which will pick up the changes. + if (modified_config.servers != configuration_.servers || + modified_config.type != configuration_.type || + modified_config.GetTurnPortPrunePolicy() != + configuration_.GetTurnPortPrunePolicy()) { + transport_controller_->SetNeedsIceRestartFlag(); + } + + transport_controller_->SetIceConfig(ParseIceConfig(modified_config)); + + use_datagram_transport_ = datagram_transport_config_.enabled && + modified_config.use_datagram_transport.value_or( + datagram_transport_config_.default_value); + use_datagram_transport_for_data_channels_ = + datagram_transport_data_channel_config_.enabled && + modified_config.use_datagram_transport_for_data_channels.value_or( + datagram_transport_data_channel_config_.default_value); + use_datagram_transport_for_data_channels_receive_only_ = + modified_config.use_datagram_transport_for_data_channels_receive_only + .value_or(datagram_transport_data_channel_config_.receive_only); + transport_controller_->SetMediaTransportSettings( + use_datagram_transport_, use_datagram_transport_for_data_channels_, + use_datagram_transport_for_data_channels_receive_only_); + + if (configuration_.active_reset_srtp_params != + modified_config.active_reset_srtp_params) { + transport_controller_->SetActiveResetSrtpParams( + modified_config.active_reset_srtp_params); + } + + if (modified_config.allow_codec_switching.has_value()) { + cricket::VideoMediaChannel* video_channel = video_media_channel(); + if (video_channel) { + video_channel->SetVideoCodecSwitchingEnabled( + *modified_config.allow_codec_switching); + } + } + + configuration_ = modified_config; + return RTCError::OK(); } bool TgPeerConnection::AddIceCandidate( - const IceCandidateInterface* ice_candidate) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::AddIceCandidate"); - if (IsClosed()) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: TgPeerConnection is closed."; - NoteAddIceCandidateResult(kAddIceCandidateFailClosed); - return false; - } - - if (!remote_description()) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added " - "without any remote session description."; - NoteAddIceCandidateResult(kAddIceCandidateFailNoRemoteDescription); - return false; - } - - if (!ice_candidate) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null."; - NoteAddIceCandidateResult(kAddIceCandidateFailNullCandidate); - return false; - } - - bool valid = false; - bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid); - if (!valid) { - NoteAddIceCandidateResult(kAddIceCandidateFailNotValid); - return false; - } - - // Add this candidate to the remote session description. - if (!mutable_remote_description()->AddCandidate(ice_candidate)) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used."; - NoteAddIceCandidateResult(kAddIceCandidateFailInAddition); - return false; - } - - if (ready) { - bool result = UseCandidate(ice_candidate); - if (result) { - NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED); - NoteAddIceCandidateResult(kAddIceCandidateSuccess); - } else { - NoteAddIceCandidateResult(kAddIceCandidateFailNotUsable); + const IceCandidateInterface* ice_candidate) { + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::AddIceCandidate"); + if (IsClosed()) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: TgPeerConnection is closed."; + NoteAddIceCandidateResult(kAddIceCandidateFailClosed); + return false; + } + + if (!remote_description()) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added " + "without any remote session description."; + NoteAddIceCandidateResult(kAddIceCandidateFailNoRemoteDescription); + return false; + } + + if (!ice_candidate) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null."; + NoteAddIceCandidateResult(kAddIceCandidateFailNullCandidate); + return false; + } + + bool valid = false; + bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid); + if (!valid) { + NoteAddIceCandidateResult(kAddIceCandidateFailNotValid); + return false; + } + + // Add this candidate to the remote session description. + if (!mutable_remote_description()->AddCandidate(ice_candidate)) { + RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used."; + NoteAddIceCandidateResult(kAddIceCandidateFailInAddition); + return false; + } + + if (ready) { + bool result = UseCandidate(ice_candidate); + if (result) { + NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED); + NoteAddIceCandidateResult(kAddIceCandidateSuccess); + } else { + NoteAddIceCandidateResult(kAddIceCandidateFailNotUsable); + } + return result; + } else { + RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate."; + NoteAddIceCandidateResult(kAddIceCandidateFailNotReady); + return true; } - return result; - } else { - RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate."; - NoteAddIceCandidateResult(kAddIceCandidateFailNotReady); - return true; - } } void TgPeerConnection::AddIceCandidate( - std::unique_ptr candidate, - std::function callback) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - candidate = std::move(candidate), callback = std::move(callback)]( - std::function operations_chain_callback) { + std::unique_ptr candidate, + std::function callback) { + RTC_DCHECK_RUN_ON(signaling_thread()); + // Chain this operation. If asynchronous operations are pending on the chain, + // this operation will be queued to be invoked, otherwise the contents of the + // lambda will execute immediately. + operations_chain_->ChainOperation( + [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), + candidate = std::move(candidate), callback = std::move(callback)]( + std::function operations_chain_callback) { if (!this_weak_ptr) { - operations_chain_callback(); - callback(RTCError( - RTCErrorType::INVALID_STATE, - "AddIceCandidate failed because the session was shut down")); - return; + operations_chain_callback(); + callback(RTCError( + RTCErrorType::INVALID_STATE, + "AddIceCandidate failed because the session was shut down")); + return; } if (!this_weak_ptr->AddIceCandidate(candidate.get())) { - operations_chain_callback(); - // Fail with an error type and message consistent with Chromium. - // TODO(hbos): Fail with error types according to spec. - callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Error processing ICE candidate")); - return; + operations_chain_callback(); + // Fail with an error type and message consistent with Chromium. + // TODO(hbos): Fail with error types according to spec. + callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Error processing ICE candidate")); + return; } operations_chain_callback(); callback(RTCError::OK()); - }); + }); } bool TgPeerConnection::RemoveIceCandidates( - const std::vector& candidates) { - TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveIceCandidates"); - RTC_DCHECK_RUN_ON(signaling_thread()); - if (IsClosed()) { - RTC_LOG(LS_ERROR) << "RemoveIceCandidates: TgPeerConnection is closed."; - return false; - } - - if (!remote_description()) { - RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed " - "without any remote session description."; - return false; - } - - if (candidates.empty()) { - RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty."; - return false; - } - - size_t number_removed = - mutable_remote_description()->RemoveCandidates(candidates); - if (number_removed != candidates.size()) { - RTC_LOG(LS_ERROR) + const std::vector& candidates) { + TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveIceCandidates"); + RTC_DCHECK_RUN_ON(signaling_thread()); + if (IsClosed()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: TgPeerConnection is closed."; + return false; + } + + if (!remote_description()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed " + "without any remote session description."; + return false; + } + + if (candidates.empty()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty."; + return false; + } + + size_t number_removed = + mutable_remote_description()->RemoveCandidates(candidates); + if (number_removed != candidates.size()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: Failed to remove candidates. Requested " << candidates.size() << " but only " << number_removed << " are removed."; - } - - // Remove the candidates from the transport controller. - RTCError error = transport_controller_->RemoveRemoteCandidates(candidates); - if (!error.ok()) { - RTC_LOG(LS_ERROR) + } + + // Remove the candidates from the transport controller. + RTCError error = transport_controller_->RemoveRemoteCandidates(candidates); + if (!error.ok()) { + RTC_LOG(LS_ERROR) << "RemoveIceCandidates: Error when removing remote candidates: " << error.message(); - } - return true; + } + return true; } RTCError TgPeerConnection::SetBitrate(const BitrateSettings& bitrate) { - if (!worker_thread()->IsCurrent()) { - return worker_thread()->Invoke( - RTC_FROM_HERE, [&]() { return SetBitrate(bitrate); }); - } - RTC_DCHECK_RUN_ON(worker_thread()); - - const bool has_min = bitrate.min_bitrate_bps.has_value(); - const bool has_start = bitrate.start_bitrate_bps.has_value(); - const bool has_max = bitrate.max_bitrate_bps.has_value(); - if (has_min && *bitrate.min_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "min_bitrate_bps <= 0"); - } - if (has_start) { - if (has_min && *bitrate.start_bitrate_bps < *bitrate.min_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "start_bitrate_bps < min_bitrate_bps"); - } else if (*bitrate.start_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "curent_bitrate_bps < 0"); + if (!worker_thread()->IsCurrent()) { + return worker_thread()->Invoke( + RTC_FROM_HERE, [&]() { return SetBitrate(bitrate); }); } - } - if (has_max) { - if (has_start && *bitrate.max_bitrate_bps < *bitrate.start_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < start_bitrate_bps"); - } else if (has_min && *bitrate.max_bitrate_bps < *bitrate.min_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < min_bitrate_bps"); - } else if (*bitrate.max_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < 0"); + RTC_DCHECK_RUN_ON(worker_thread()); + + const bool has_min = bitrate.min_bitrate_bps.has_value(); + const bool has_start = bitrate.start_bitrate_bps.has_value(); + const bool has_max = bitrate.max_bitrate_bps.has_value(); + if (has_min && *bitrate.min_bitrate_bps < 0) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "min_bitrate_bps <= 0"); } - } - - RTC_DCHECK(call_.get()); - call_->SetClientBitratePreferences(bitrate); - - return RTCError::OK(); + if (has_start) { + if (has_min && *bitrate.start_bitrate_bps < *bitrate.min_bitrate_bps) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "start_bitrate_bps < min_bitrate_bps"); + } else if (*bitrate.start_bitrate_bps < 0) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "curent_bitrate_bps < 0"); + } + } + if (has_max) { + if (has_start && *bitrate.max_bitrate_bps < *bitrate.start_bitrate_bps) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max_bitrate_bps < start_bitrate_bps"); + } else if (has_min && *bitrate.max_bitrate_bps < *bitrate.min_bitrate_bps) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max_bitrate_bps < min_bitrate_bps"); + } else if (*bitrate.max_bitrate_bps < 0) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max_bitrate_bps < 0"); + } + } + + RTC_DCHECK(call_.get()); + call_->SetClientBitratePreferences(bitrate); + + return RTCError::OK(); } void TgPeerConnection::SetAudioPlayout(bool playout) { - if (!worker_thread()->IsCurrent()) { - worker_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::SetAudioPlayout, this, playout)); - return; - } - auto audio_state = - factory_->channel_manager()->media_engine()->voice().GetAudioState(); - audio_state->SetPlayout(playout); + if (!worker_thread()->IsCurrent()) { + worker_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::SetAudioPlayout, this, playout)); + return; + } + auto audio_state = + factory_->channel_manager()->media_engine()->voice().GetAudioState(); + audio_state->SetPlayout(playout); } void TgPeerConnection::SetAudioRecording(bool recording) { - if (!worker_thread()->IsCurrent()) { - worker_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::SetAudioRecording, this, recording)); - return; - } - auto audio_state = - factory_->channel_manager()->media_engine()->voice().GetAudioState(); - audio_state->SetRecording(recording); + if (!worker_thread()->IsCurrent()) { + worker_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&TgPeerConnection::SetAudioRecording, this, recording)); + return; + } + auto audio_state = + factory_->channel_manager()->media_engine()->voice().GetAudioState(); + audio_state->SetRecording(recording); } std::unique_ptr TgPeerConnection::GetRemoteAudioSSLCertificate() { - std::unique_ptr chain = GetRemoteAudioSSLCertChain(); - if (!chain || !chain->GetSize()) { - return nullptr; - } - return chain->Get(0).Clone(); + std::unique_ptr chain = GetRemoteAudioSSLCertChain(); + if (!chain || !chain->GetSize()) { + return nullptr; + } + return chain->Get(0).Clone(); } std::unique_ptr TgPeerConnection::GetRemoteAudioSSLCertChain() { - RTC_DCHECK_RUN_ON(signaling_thread()); - auto audio_transceiver = GetFirstAudioTransceiver(); - if (!audio_transceiver || !audio_transceiver->internal()->channel()) { - return nullptr; - } - return transport_controller_->GetRemoteSSLCertChain( - audio_transceiver->internal()->channel()->transport_name()); + RTC_DCHECK_RUN_ON(signaling_thread()); + auto audio_transceiver = GetFirstAudioTransceiver(); + if (!audio_transceiver || !audio_transceiver->internal()->channel()) { + return nullptr; + } + return transport_controller_->GetRemoteSSLCertChain( + audio_transceiver->internal()->channel()->transport_name()); } rtc::scoped_refptr> TgPeerConnection::GetFirstAudioTransceiver() const { - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - return transceiver; + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + return transceiver; + } } - } - return nullptr; + return nullptr; } bool TgPeerConnection::StartRtcEventLog(std::unique_ptr output, - int64_t output_period_ms) { - return worker_thread()->Invoke( - RTC_FROM_HERE, - [this, output = std::move(output), output_period_ms]() mutable { + int64_t output_period_ms) { + return worker_thread()->Invoke( + RTC_FROM_HERE, + [this, output = std::move(output), output_period_ms]() mutable { return StartRtcEventLog_w(std::move(output), output_period_ms); - }); + }); } bool TgPeerConnection::StartRtcEventLog( - std::unique_ptr output) { - int64_t output_period_ms = webrtc::RtcEventLog::kImmediateOutput; - if (field_trial::IsEnabled("WebRTC-RtcEventLogNewFormat")) { - output_period_ms = 5000; - } - return StartRtcEventLog(std::move(output), output_period_ms); + std::unique_ptr output) { + int64_t output_period_ms = webrtc::RtcEventLog::kImmediateOutput; + if (field_trial::IsEnabled("WebRTC-RtcEventLogNewFormat")) { + output_period_ms = 5000; + } + return StartRtcEventLog(std::move(output), output_period_ms); } void TgPeerConnection::StopRtcEventLog() { - worker_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::StopRtcEventLog_w, this)); + worker_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::StopRtcEventLog_w, this)); } rtc::scoped_refptr TgPeerConnection::LookupDtlsTransportByMid(const std::string& mid) { - RTC_DCHECK_RUN_ON(signaling_thread()); - return transport_controller_->LookupDtlsTransportByMid(mid); + RTC_DCHECK_RUN_ON(signaling_thread()); + return transport_controller_->LookupDtlsTransportByMid(mid); } rtc::scoped_refptr TgPeerConnection::LookupDtlsTransportByMidInternal(const std::string& mid) { - RTC_DCHECK_RUN_ON(signaling_thread()); - return transport_controller_->LookupDtlsTransportByMid(mid); + RTC_DCHECK_RUN_ON(signaling_thread()); + return transport_controller_->LookupDtlsTransportByMid(mid); } rtc::scoped_refptr TgPeerConnection::GetSctpTransport() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!sctp_mid_) { - return nullptr; - } - return transport_controller_->GetSctpTransport(*sctp_mid_); +const { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!sctp_mid_) { + return nullptr; + } + return transport_controller_->GetSctpTransport(*sctp_mid_); } const SessionDescriptionInterface* TgPeerConnection::local_description() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_local_description_ ? pending_local_description_.get() - : current_local_description_.get(); + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_local_description_ ? pending_local_description_.get() + : current_local_description_.get(); } const SessionDescriptionInterface* TgPeerConnection::remote_description() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_remote_description_ ? pending_remote_description_.get() - : current_remote_description_.get(); + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_remote_description_ ? pending_remote_description_.get() + : current_remote_description_.get(); } const SessionDescriptionInterface* TgPeerConnection::current_local_description() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return current_local_description_.get(); +const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return current_local_description_.get(); } const SessionDescriptionInterface* TgPeerConnection::current_remote_description() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return current_remote_description_.get(); +const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return current_remote_description_.get(); } const SessionDescriptionInterface* TgPeerConnection::pending_local_description() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_local_description_.get(); +const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_local_description_.get(); } const SessionDescriptionInterface* TgPeerConnection::pending_remote_description() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_remote_description_.get(); +const { + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_remote_description_.get(); } void TgPeerConnection::Close() { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::Close"); - // Update stats here so that we have the most recent stats for tracks and - // streams before the channels are closed. - stats_->UpdateStats(kStatsOutputLevelStandard); - - ChangeSignalingState(PeerConnectionInterface::kClosed); - NoteUsageEvent(UsageEvent::CLOSE_CALLED); - - for (const auto& transceiver : transceivers_) { - transceiver->Stop(); - } - - // Ensure that all asynchronous stats requests are completed before destroying - // the transport controller below. - if (stats_collector_) { - stats_collector_->WaitForPendingRequest(); - } - - // Don't destroy BaseChannels until after stats has been cleaned up so that - // the last stats request can still read from the channels. - DestroyAllChannels(); - - // The event log is used in the transport controller, which must be outlived - // by the former. CreateOffer by the peer connection is implemented - // asynchronously and if the peer connection is closed without resetting the - // WebRTC session description factory, the session description factory would - // call the transport controller. - webrtc_session_desc_factory_.reset(); - transport_controller_.reset(); - - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, - port_allocator_.get())); - - worker_thread()->Invoke(RTC_FROM_HERE, [this] { - RTC_DCHECK_RUN_ON(worker_thread()); - call_.reset(); - // The event log must outlive call (and any other object that uses it). - event_log_.reset(); - }); - ReportUsagePattern(); - // The .h file says that observer can be discarded after close() returns. - // Make sure this is true. - observer_ = nullptr; + RTC_DCHECK_RUN_ON(signaling_thread()); + TRACE_EVENT0("webrtc", "TgPeerConnection::Close"); + + ChangeSignalingState(PeerConnectionInterface::kClosed); + NoteUsageEvent(UsageEvent::CLOSE_CALLED); + + for (const auto& transceiver : transceivers_) { + transceiver->Stop(); + } + + // Don't destroy BaseChannels until after stats has been cleaned up so that + // the last stats request can still read from the channels. + DestroyAllChannels(); + + // The event log is used in the transport controller, which must be outlived + // by the former. CreateOffer by the peer connection is implemented + // asynchronously and if the peer connection is closed without resetting the + // WebRTC session description factory, the session description factory would + // call the transport controller. + webrtc_session_desc_factory_.reset(); + transport_controller_.reset(); + + network_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, + port_allocator_.get())); + + worker_thread()->Invoke(RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(worker_thread()); + call_.reset(); + // The event log must outlive call (and any other object that uses it). + event_log_.reset(); + }); + ReportUsagePattern(); + // The .h file says that observer can be discarded after close() returns. + // Make sure this is true. + observer_ = nullptr; } void TgPeerConnection::OnMessage(rtc::Message* msg) { - RTC_DCHECK_RUN_ON(signaling_thread()); - switch (msg->message_id) { - case MSG_SET_SESSIONDESCRIPTION_SUCCESS: { - SetSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnSuccess(); - delete param; - break; + RTC_DCHECK_RUN_ON(signaling_thread()); + switch (msg->message_id) { + case MSG_SET_SESSIONDESCRIPTION_SUCCESS: { + SetSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnSuccess(); + delete param; + break; + } + case MSG_SET_SESSIONDESCRIPTION_FAILED: { + SetSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnFailure(std::move(param->error)); + delete param; + break; + } + case MSG_CREATE_SESSIONDESCRIPTION_FAILED: { + CreateSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnFailure(std::move(param->error)); + delete param; + break; + } + case MSG_REPORT_USAGE_PATTERN: { + ReportUsagePattern(); + break; + } + default: + RTC_NOTREACHED() << "Not implemented"; + break; } - case MSG_SET_SESSIONDESCRIPTION_FAILED: { - SetSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnFailure(std::move(param->error)); - delete param; - break; - } - case MSG_CREATE_SESSIONDESCRIPTION_FAILED: { - CreateSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnFailure(std::move(param->error)); - delete param; - break; - } - case MSG_GETSTATS: { - GetStatsMsg* param = static_cast(msg->pdata); - StatsReports reports; - stats_->GetStats(param->track, &reports); - param->observer->OnComplete(reports); - delete param; - break; - } - case MSG_REPORT_USAGE_PATTERN: { - ReportUsagePattern(); - break; - } - default: - RTC_NOTREACHED() << "Not implemented"; - break; - } } cricket::VoiceMediaChannel* TgPeerConnection::voice_media_channel() const { - RTC_DCHECK(!IsUnifiedPlan()); - auto* voice_channel = static_cast( - GetAudioTransceiver()->internal()->channel()); - if (voice_channel) { - return voice_channel->media_channel(); - } else { - return nullptr; - } + RTC_DCHECK(!IsUnifiedPlan()); + auto* voice_channel = static_cast( + GetAudioTransceiver()->internal()->channel()); + if (voice_channel) { + return voice_channel->media_channel(); + } else { + return nullptr; + } } cricket::VideoMediaChannel* TgPeerConnection::video_media_channel() const { - RTC_DCHECK(!IsUnifiedPlan()); - auto* video_channel = static_cast( - GetVideoTransceiver()->internal()->channel()); - if (video_channel) { - return video_channel->media_channel(); - } else { - return nullptr; - } + RTC_DCHECK(!IsUnifiedPlan()); + auto* video_channel = static_cast( + GetVideoTransceiver()->internal()->channel()); + if (video_channel) { + return video_channel->media_channel(); + } else { + return nullptr; + } } void TgPeerConnection::CreateAudioReceiver( - MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) { - std::vector> streams; - streams.push_back(rtc::scoped_refptr(stream)); - // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use - // the constructor taking stream IDs instead. - auto* audio_receiver = new AudioRtpReceiver( - worker_thread(), remote_sender_info.sender_id, streams); - audio_receiver->SetMediaChannel(voice_media_channel()); - if (remote_sender_info.sender_id == kDefaultAudioSenderId) { - audio_receiver->SetupUnsignaledMediaChannel(); - } else { - audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); - } - auto receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), audio_receiver); - GetAudioTransceiver()->internal()->AddReceiver(receiver); - Observer()->OnAddTrack(receiver, streams); - NoteUsageEvent(UsageEvent::AUDIO_ADDED); + MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) { + std::vector> streams; + streams.push_back(rtc::scoped_refptr(stream)); + // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use + // the constructor taking stream IDs instead. + auto* audio_receiver = new AudioRtpReceiver( + worker_thread(), remote_sender_info.sender_id, streams); + audio_receiver->SetMediaChannel(voice_media_channel()); + if (remote_sender_info.sender_id == kDefaultAudioSenderId) { + audio_receiver->SetupUnsignaledMediaChannel(); + } else { + audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); + } + auto receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), audio_receiver); + GetAudioTransceiver()->internal()->AddReceiver(receiver); + Observer()->OnAddTrack(receiver, streams); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); } void TgPeerConnection::CreateVideoReceiver( - MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) { - std::vector> streams; - streams.push_back(rtc::scoped_refptr(stream)); - // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use - // the constructor taking stream IDs instead. - auto* video_receiver = new VideoRtpReceiver( - worker_thread(), remote_sender_info.sender_id, streams); - video_receiver->SetMediaChannel(video_media_channel()); - if (remote_sender_info.sender_id == kDefaultVideoSenderId) { - video_receiver->SetupUnsignaledMediaChannel(); - } else { - video_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); - } - auto receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), video_receiver); - GetVideoTransceiver()->internal()->AddReceiver(receiver); - Observer()->OnAddTrack(receiver, streams); - NoteUsageEvent(UsageEvent::VIDEO_ADDED); + MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) { + std::vector> streams; + streams.push_back(rtc::scoped_refptr(stream)); + // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use + // the constructor taking stream IDs instead. + auto* video_receiver = new VideoRtpReceiver( + worker_thread(), remote_sender_info.sender_id, streams); + video_receiver->SetMediaChannel(video_media_channel()); + if (remote_sender_info.sender_id == kDefaultVideoSenderId) { + video_receiver->SetupUnsignaledMediaChannel(); + } else { + video_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); + } + auto receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), video_receiver); + GetVideoTransceiver()->internal()->AddReceiver(receiver); + Observer()->OnAddTrack(receiver, streams); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); } // TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote // description. rtc::scoped_refptr TgPeerConnection::RemoveAndStopReceiver( - const RtpSenderInfo& remote_sender_info) { - auto receiver = FindReceiverById(remote_sender_info.sender_id); - if (!receiver) { - RTC_LOG(LS_WARNING) << "RtpReceiver for track with id " - << remote_sender_info.sender_id << " doesn't exist."; - return nullptr; - } - if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - GetAudioTransceiver()->internal()->RemoveReceiver(receiver); - } else { - GetVideoTransceiver()->internal()->RemoveReceiver(receiver); - } - return receiver; + const RtpSenderInfo& remote_sender_info) { + auto receiver = FindReceiverById(remote_sender_info.sender_id); + if (!receiver) { + RTC_LOG(LS_WARNING) << "RtpReceiver for track with id " + << remote_sender_info.sender_id << " doesn't exist."; + return nullptr; + } + if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + GetAudioTransceiver()->internal()->RemoveReceiver(receiver); + } else { + GetVideoTransceiver()->internal()->RemoveReceiver(receiver); + } + return receiver; } void TgPeerConnection::AddAudioTrack(AudioTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - RTC_DCHECK(track); - RTC_DCHECK(stream); - auto sender = FindSenderForTrack(track); - if (sender) { - // We already have a sender for this track, so just change the stream_id - // so that it's correct in the next call to CreateOffer. - sender->internal()->set_stream_ids({stream->id()}); - return; - } - - // Normal case; we've never seen this track before. - auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(), track, - {stream->id()}, {}); - new_sender->internal()->SetMediaChannel(voice_media_channel()); - GetAudioTransceiver()->internal()->AddSender(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 - // occur if a local session description that contains the ID of the sender - // is set before AddStream is called. It can also occur if the local - // session description is not changed and RemoveStream is called, and - // later AddStream is called again with the same stream. - const RtpSenderInfo* sender_info = - FindSenderInfo(local_audio_sender_infos_, stream->id(), track->id()); - if (sender_info) { - new_sender->internal()->SetSsrc(sender_info->first_ssrc); - } + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + RTC_DCHECK(track); + RTC_DCHECK(stream); + auto sender = FindSenderForTrack(track); + if (sender) { + // We already have a sender for this track, so just change the stream_id + // so that it's correct in the next call to CreateOffer. + sender->internal()->set_stream_ids({stream->id()}); + return; + } + + // Normal case; we've never seen this track before. + auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(), track, + {stream->id()}, {}); + new_sender->internal()->SetMediaChannel(voice_media_channel()); + GetAudioTransceiver()->internal()->AddSender(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 + // occur if a local session description that contains the ID of the sender + // is set before AddStream is called. It can also occur if the local + // session description is not changed and RemoveStream is called, and + // later AddStream is called again with the same stream. + const RtpSenderInfo* sender_info = + FindSenderInfo(local_audio_sender_infos_, stream->id(), track->id()); + if (sender_info) { + new_sender->internal()->SetSsrc(sender_info->first_ssrc); + } } // TODO(deadbeef): Don't destroy RtpSenders here; they should be kept around // indefinitely, when we have unified plan SDP. void TgPeerConnection::RemoveAudioTrack(AudioTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - auto sender = FindSenderForTrack(track); - if (!sender) { - RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() - << " doesn't exist."; - return; - } - GetAudioTransceiver()->internal()->RemoveSender(sender); + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + auto sender = FindSenderForTrack(track); + if (!sender) { + RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() + << " doesn't exist."; + return; + } + GetAudioTransceiver()->internal()->RemoveSender(sender); } void TgPeerConnection::AddVideoTrack(VideoTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - RTC_DCHECK(track); - RTC_DCHECK(stream); - auto sender = FindSenderForTrack(track); - if (sender) { - // We already have a sender for this track, so just change the stream_id - // so that it's correct in the next call to CreateOffer. - sender->internal()->set_stream_ids({stream->id()}); - return; - } - - // Normal case; we've never seen this track before. - auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(), track, - {stream->id()}, {}); - new_sender->internal()->SetMediaChannel(video_media_channel()); - GetVideoTransceiver()->internal()->AddSender(new_sender); - const RtpSenderInfo* sender_info = - FindSenderInfo(local_video_sender_infos_, stream->id(), track->id()); - if (sender_info) { - new_sender->internal()->SetSsrc(sender_info->first_ssrc); - } + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + RTC_DCHECK(track); + RTC_DCHECK(stream); + auto sender = FindSenderForTrack(track); + if (sender) { + // We already have a sender for this track, so just change the stream_id + // so that it's correct in the next call to CreateOffer. + sender->internal()->set_stream_ids({stream->id()}); + return; + } + + // Normal case; we've never seen this track before. + auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(), track, + {stream->id()}, {}); + new_sender->internal()->SetMediaChannel(video_media_channel()); + GetVideoTransceiver()->internal()->AddSender(new_sender); + const RtpSenderInfo* sender_info = + FindSenderInfo(local_video_sender_infos_, stream->id(), track->id()); + if (sender_info) { + new_sender->internal()->SetSsrc(sender_info->first_ssrc); + } } void TgPeerConnection::RemoveVideoTrack(VideoTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - auto sender = FindSenderForTrack(track); - if (!sender) { - RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() - << " doesn't exist."; - return; - } - GetVideoTransceiver()->internal()->RemoveSender(sender); + MediaStreamInterface* stream) { + RTC_DCHECK(!IsClosed()); + auto sender = FindSenderForTrack(track); + if (!sender) { + RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() + << " doesn't exist."; + return; + } + GetVideoTransceiver()->internal()->RemoveSender(sender); } -void TgPeerConnection::SetIceConnectionState(IceConnectionState new_state) { - if (ice_connection_state_ == new_state) { - return; - } - - // After transitioning to "closed", ignore any additional states from - // TransportController (such as "disconnected"). - if (IsClosed()) { - return; - } - - RTC_LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_ - << " => " << new_state; - RTC_DCHECK(ice_connection_state_ != - PeerConnectionInterface::kIceConnectionClosed); - - ice_connection_state_ = new_state; - Observer()->OnIceConnectionChange(ice_connection_state_); +void TgPeerConnection::SetIceConnectionState(PeerConnectionInterface::IceConnectionState new_state) { + if (ice_connection_state_ == new_state) { + return; + } + + // After transitioning to "closed", ignore any additional states from + // TransportController (such as "disconnected"). + if (IsClosed()) { + return; + } + + RTC_LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_ + << " => " << new_state; + RTC_DCHECK(ice_connection_state_ != + PeerConnectionInterface::kIceConnectionClosed); + + ice_connection_state_ = new_state; + Observer()->OnIceConnectionChange(ice_connection_state_); } void TgPeerConnection::SetStandardizedIceConnectionState( - PeerConnectionInterface::IceConnectionState new_state) { - if (standardized_ice_connection_state_ == new_state) { - return; - } - - if (IsClosed()) { - return; - } - - RTC_LOG(LS_INFO) << "Changing standardized IceConnectionState " - << standardized_ice_connection_state_ << " => " << new_state; - - standardized_ice_connection_state_ = new_state; - Observer()->OnStandardizedIceConnectionChange(new_state); + PeerConnectionInterface::IceConnectionState new_state) { + if (standardized_ice_connection_state_ == new_state) { + return; + } + + if (IsClosed()) { + return; + } + + RTC_LOG(LS_INFO) << "Changing standardized IceConnectionState " + << standardized_ice_connection_state_ << " => " << new_state; + + standardized_ice_connection_state_ = new_state; + Observer()->OnStandardizedIceConnectionChange(new_state); } void TgPeerConnection::SetConnectionState( - PeerConnectionInterface::PeerConnectionState new_state) { - if (connection_state_ == new_state) - return; - if (IsClosed()) - return; - connection_state_ = new_state; - Observer()->OnConnectionChange(new_state); + PeerConnectionInterface::PeerConnectionState new_state) { + if (connection_state_ == new_state) + return; + if (IsClosed()) + return; + connection_state_ = new_state; + Observer()->OnConnectionChange(new_state); } void TgPeerConnection::OnIceGatheringChange( - PeerConnectionInterface::IceGatheringState new_state) { - if (IsClosed()) { - return; - } - ice_gathering_state_ = new_state; - Observer()->OnIceGatheringChange(ice_gathering_state_); + PeerConnectionInterface::IceGatheringState new_state) { + if (IsClosed()) { + return; + } + ice_gathering_state_ = new_state; + Observer()->OnIceGatheringChange(ice_gathering_state_); } void TgPeerConnection::OnIceCandidate( - std::unique_ptr candidate) { - if (IsClosed()) { - return; - } - ReportIceCandidateCollected(candidate->candidate()); - Observer()->OnIceCandidate(candidate.get()); + std::unique_ptr candidate) { + if (IsClosed()) { + return; + } + ReportIceCandidateCollected(candidate->candidate()); + Observer()->OnIceCandidate(candidate.get()); } void TgPeerConnection::OnIceCandidateError(const std::string& address, - int port, - const std::string& url, - int error_code, - const std::string& error_text) { - if (IsClosed()) { - return; - } - Observer()->OnIceCandidateError(address, port, url, error_code, error_text); - // Leftover not to break wpt test during migration to the new API. - Observer()->OnIceCandidateError(address + ":", url, error_code, error_text); + int port, + const std::string& url, + int error_code, + const std::string& error_text) { + if (IsClosed()) { + return; + } + Observer()->OnIceCandidateError(address, port, url, error_code, error_text); + // Leftover not to break wpt test during migration to the new API. + Observer()->OnIceCandidateError(address + ":", url, error_code, error_text); } void TgPeerConnection::OnIceCandidatesRemoved( - const std::vector& candidates) { - if (IsClosed()) { - return; - } - Observer()->OnIceCandidatesRemoved(candidates); + const std::vector& candidates) { + if (IsClosed()) { + return; + } + Observer()->OnIceCandidatesRemoved(candidates); } void TgPeerConnection::OnSelectedCandidatePairChanged( - const cricket::CandidatePairChangeEvent& event) { - if (IsClosed()) { - return; - } - - if (event.selected_candidate_pair.local_candidate().type() == - LOCAL_PORT_TYPE && - event.selected_candidate_pair.remote_candidate().type() == - LOCAL_PORT_TYPE) { - NoteUsageEvent(UsageEvent::DIRECT_CONNECTION_SELECTED); - } - - Observer()->OnIceSelectedCandidatePairChanged(event); + const cricket::CandidatePairChangeEvent& event) { + if (IsClosed()) { + return; + } + + if (event.selected_candidate_pair.local_candidate().type() == + LOCAL_PORT_TYPE && + event.selected_candidate_pair.remote_candidate().type() == + LOCAL_PORT_TYPE) { + NoteUsageEvent(UsageEvent::DIRECT_CONNECTION_SELECTED); + } + + Observer()->OnIceSelectedCandidatePairChanged(event); } void TgPeerConnection::ChangeSignalingState( - PeerConnectionInterface::SignalingState signaling_state) { - if (signaling_state_ == signaling_state) { - return; - } - RTC_LOG(LS_INFO) << "Session: " << session_id() << " Old state: " - << GetSignalingStateString(signaling_state_) - << " New state: " - << GetSignalingStateString(signaling_state); - signaling_state_ = signaling_state; - if (signaling_state == kClosed) { - ice_connection_state_ = kIceConnectionClosed; - Observer()->OnIceConnectionChange(ice_connection_state_); - standardized_ice_connection_state_ = - PeerConnectionInterface::IceConnectionState::kIceConnectionClosed; - connection_state_ = PeerConnectionInterface::PeerConnectionState::kClosed; - Observer()->OnConnectionChange(connection_state_); - if (ice_gathering_state_ != kIceGatheringComplete) { - ice_gathering_state_ = kIceGatheringComplete; - Observer()->OnIceGatheringChange(ice_gathering_state_); + PeerConnectionInterface::SignalingState signaling_state) { + if (signaling_state_ == signaling_state) { + return; } - } - Observer()->OnSignalingChange(signaling_state_); + RTC_LOG(LS_INFO) << "Session: " << session_id() << " Old state: " + << GetSignalingStateString(signaling_state_) + << " New state: " + << GetSignalingStateString(signaling_state); + signaling_state_ = signaling_state; + if (signaling_state == PeerConnectionInterface::kClosed) { + ice_connection_state_ = PeerConnectionInterface::kIceConnectionClosed; + Observer()->OnIceConnectionChange(ice_connection_state_); + standardized_ice_connection_state_ = + PeerConnectionInterface::IceConnectionState::kIceConnectionClosed; + connection_state_ = PeerConnectionInterface::PeerConnectionState::kClosed; + Observer()->OnConnectionChange(connection_state_); + if (ice_gathering_state_ != PeerConnectionInterface::kIceGatheringComplete) { + ice_gathering_state_ = PeerConnectionInterface::kIceGatheringComplete; + Observer()->OnIceGatheringChange(ice_gathering_state_); + } + } + Observer()->OnSignalingChange(signaling_state_); } void TgPeerConnection::OnAudioTrackAdded(AudioTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - AddAudioTrack(track, stream); - UpdateNegotiationNeeded(); + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + AddAudioTrack(track, stream); + UpdateNegotiationNeeded(); } void TgPeerConnection::OnAudioTrackRemoved(AudioTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - RemoveAudioTrack(track, stream); - UpdateNegotiationNeeded(); + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + RemoveAudioTrack(track, stream); + UpdateNegotiationNeeded(); } void TgPeerConnection::OnVideoTrackAdded(VideoTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - AddVideoTrack(track, stream); - UpdateNegotiationNeeded(); + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + AddVideoTrack(track, stream); + UpdateNegotiationNeeded(); } void TgPeerConnection::OnVideoTrackRemoved(VideoTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - RemoveVideoTrack(track, stream); - UpdateNegotiationNeeded(); + MediaStreamInterface* stream) { + if (IsClosed()) { + return; + } + RemoveVideoTrack(track, stream); + UpdateNegotiationNeeded(); } void TgPeerConnection::PostSetSessionDescriptionSuccess( - SetSessionDescriptionObserver* observer) { - SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); - signaling_thread()->Post(RTC_FROM_HERE, this, - MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg); + SetSessionDescriptionObserver* observer) { + SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); + signaling_thread()->Post(RTC_FROM_HERE, this, + MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg); } void TgPeerConnection::PostSetSessionDescriptionFailure( - SetSessionDescriptionObserver* observer, - RTCError&& error) { - RTC_DCHECK(!error.ok()); - SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); - msg->error = std::move(error); - signaling_thread()->Post(RTC_FROM_HERE, this, - MSG_SET_SESSIONDESCRIPTION_FAILED, msg); + SetSessionDescriptionObserver* observer, + RTCError&& error) { + RTC_DCHECK(!error.ok()); + SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); + msg->error = std::move(error); + signaling_thread()->Post(RTC_FROM_HERE, this, + MSG_SET_SESSIONDESCRIPTION_FAILED, msg); } void TgPeerConnection::PostCreateSessionDescriptionFailure( - CreateSessionDescriptionObserver* observer, - RTCError error) { - RTC_DCHECK(!error.ok()); - CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer); - msg->error = std::move(error); - signaling_thread()->Post(RTC_FROM_HERE, this, - MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg); + CreateSessionDescriptionObserver* observer, + RTCError error) { + RTC_DCHECK(!error.ok()); + CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer); + msg->error = std::move(error); + signaling_thread()->Post(RTC_FROM_HERE, this, + MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg); } void TgPeerConnection::GetOptionsForOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - ExtractSharedMediaSessionOptions(offer_answer_options, session_options); - - if (IsUnifiedPlan()) { + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + ExtractSharedMediaSessionOptions(offer_answer_options, session_options); + GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options); - } else { - GetOptionsForPlanBOffer(offer_answer_options, session_options); - } - - // Apply ICE restart flag and renomination flag. - bool ice_restart = offer_answer_options.ice_restart || - local_ice_credentials_to_replace_->HasIceCredentials(); - for (auto& options : session_options->media_description_options) { - options.transport_options.ice_restart = ice_restart; - options.transport_options.enable_ice_renomination = - configuration_.enable_ice_renomination; - } - - session_options->rtcp_cname = rtcp_cname_; - session_options->crypto_options = GetCryptoOptions(); - session_options->pooled_ice_credentials = - network_thread()->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, - port_allocator_.get())); - session_options->offer_extmap_allow_mixed = - configuration_.offer_extmap_allow_mixed; - - // If datagram transport is in use, add opaque transport parameters. - if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + + // Apply ICE restart flag and renomination flag. + bool ice_restart = offer_answer_options.ice_restart || + local_ice_credentials_to_replace_->HasIceCredentials(); for (auto& options : session_options->media_description_options) { - absl::optional params = - transport_controller_->GetTransportParameters(options.mid); - if (!params) { - continue; - } - options.transport_options.opaque_parameters = params; - if ((use_datagram_transport_ && - (options.type == cricket::MEDIA_TYPE_AUDIO || - options.type == cricket::MEDIA_TYPE_VIDEO)) || - (use_datagram_transport_for_data_channels_ && - options.type == cricket::MEDIA_TYPE_DATA)) { - options.alt_protocol = params->protocol; - } + options.transport_options.ice_restart = ice_restart; + options.transport_options.enable_ice_renomination = + configuration_.enable_ice_renomination; } - } - - // Allow fallback for using obsolete SCTP syntax. - // Note that the default in |session_options| is true, while - // the default in |options| is false. - session_options->use_obsolete_sctp_sdp = - offer_answer_options.use_obsolete_sctp_sdp; -} - -void TgPeerConnection::GetOptionsForPlanBOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - // Figure out transceiver directional preferences. - bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO); - bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO); - - // By default, generate sendrecv/recvonly m= sections. - bool recv_audio = true; - bool recv_video = true; - - // By default, only offer a new m= section if we have media to send with it. - bool offer_new_audio_description = send_audio; - bool offer_new_video_description = send_video; - bool offer_new_data_description = false; - - // The "offer_to_receive_X" options allow those defaults to be overridden. - if (offer_answer_options.offer_to_receive_audio != - RTCOfferAnswerOptions::kUndefined) { - recv_audio = (offer_answer_options.offer_to_receive_audio > 0); - offer_new_audio_description = - offer_new_audio_description || - (offer_answer_options.offer_to_receive_audio > 0); - } - if (offer_answer_options.offer_to_receive_video != - RTCOfferAnswerOptions::kUndefined) { - recv_video = (offer_answer_options.offer_to_receive_video > 0); - offer_new_video_description = - offer_new_video_description || - (offer_answer_options.offer_to_receive_video > 0); - } - - absl::optional audio_index; - absl::optional video_index; - absl::optional data_index; - // 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 (local_description()) { - GenerateMediaDescriptionOptions( - local_description(), - RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), - RtpTransceiverDirectionFromSendRecv(send_video, recv_video), - &audio_index, &video_index, &data_index, session_options); - } - - // Add audio/video/data m= sections to the end if needed. - if (!audio_index && offer_new_audio_description) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions( - cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO, - RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), - false)); - audio_index = session_options->media_description_options.size() - 1; - } - if (!video_index && offer_new_video_description) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions( - cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO, - RtpTransceiverDirectionFromSendRecv(send_video, recv_video), - false)); - video_index = session_options->media_description_options.size() - 1; - } - - cricket::MediaDescriptionOptions* audio_media_description_options = - !audio_index ? nullptr - : &session_options->media_description_options[*audio_index]; - cricket::MediaDescriptionOptions* video_media_description_options = - !video_index ? nullptr - : &session_options->media_description_options[*video_index]; - - AddPlanBRtpSenderOptions(GetSendersInternal(), - audio_media_description_options, - video_media_description_options, - offer_answer_options.num_simulcast_layers); + + session_options->rtcp_cname = rtcp_cname_; + session_options->crypto_options = GetCryptoOptions(); + session_options->pooled_ice_credentials = + network_thread()->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, + port_allocator_.get())); + session_options->offer_extmap_allow_mixed = + configuration_.offer_extmap_allow_mixed; + + // If datagram transport is in use, add opaque transport parameters. + if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + for (auto& options : session_options->media_description_options) { + absl::optional params = + transport_controller_->GetTransportParameters(options.mid); + if (!params) { + continue; + } + options.transport_options.opaque_parameters = params; + if ((use_datagram_transport_ && + (options.type == cricket::MEDIA_TYPE_AUDIO || + options.type == cricket::MEDIA_TYPE_VIDEO)) || + (use_datagram_transport_for_data_channels_ && + options.type == cricket::MEDIA_TYPE_DATA)) { + options.alt_protocol = params->protocol; + } + } + } + + // Allow fallback for using obsolete SCTP syntax. + // Note that the default in |session_options| is true, while + // the default in |options| is false. + session_options->use_obsolete_sctp_sdp = + offer_answer_options.use_obsolete_sctp_sdp; } static cricket::MediaDescriptionOptions GetMediaDescriptionOptionsForTransceiver( - rtc::scoped_refptr> - transceiver, - const std::string& mid) { - cricket::MediaDescriptionOptions media_description_options( - transceiver->media_type(), mid, transceiver->direction(), - transceiver->stopped()); - media_description_options.codec_preferences = - transceiver->codec_preferences(); - // This behavior is specified in JSEP. The gist is that: - // 1. The MSID is included if the RtpTransceiver's direction is sendonly or - // sendrecv. - // 2. If the MSID is included, then it must be included in any subsequent - // offer/answer exactly the same until the RtpTransceiver is stopped. - if (transceiver->stopped() || - (!RtpTransceiverDirectionHasSend(transceiver->direction()) && - !transceiver->internal()->has_ever_been_used_to_send())) { - return media_description_options; - } - - cricket::SenderOptions sender_options; - sender_options.track_id = transceiver->sender()->id(); - sender_options.stream_ids = transceiver->sender()->stream_ids(); - - // The following sets up RIDs and Simulcast. - // RIDs are included if Simulcast is requested or if any RID was specified. - RtpParameters send_parameters = - transceiver->internal()->sender_internal()->GetParametersInternal(); - bool has_rids = std::any_of(send_parameters.encodings.begin(), - send_parameters.encodings.end(), - [](const RtpEncodingParameters& encoding) { - return !encoding.rid.empty(); - }); - - std::vector send_rids; - SimulcastLayerList send_layers; - for (const RtpEncodingParameters& encoding : send_parameters.encodings) { - if (encoding.rid.empty()) { - continue; + rtc::scoped_refptr> + transceiver, + const std::string& mid) { + cricket::MediaDescriptionOptions media_description_options( + transceiver->media_type(), mid, transceiver->direction(), + transceiver->stopped()); + media_description_options.codec_preferences = + transceiver->codec_preferences(); + // This behavior is specified in JSEP. The gist is that: + // 1. The MSID is included if the RtpTransceiver's direction is sendonly or + // sendrecv. + // 2. If the MSID is included, then it must be included in any subsequent + // offer/answer exactly the same until the RtpTransceiver is stopped. + if (transceiver->stopped() || + (!RtpTransceiverDirectionHasSend(transceiver->direction()) && + !transceiver->internal()->has_ever_been_used_to_send())) { + return media_description_options; } - send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend)); - send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active)); - } - - if (has_rids) { - sender_options.rids = send_rids; - } - - sender_options.simulcast_layers = send_layers; - // When RIDs are configured, we must set num_sim_layers to 0 to. - // Otherwise, num_sim_layers must be 1 because either there is no - // simulcast, or simulcast is acheived by munging the SDP. - sender_options.num_sim_layers = has_rids ? 0 : 1; - media_description_options.sender_options.push_back(sender_options); - - return media_description_options; + + cricket::SenderOptions sender_options; + sender_options.track_id = transceiver->sender()->id(); + sender_options.stream_ids = transceiver->sender()->stream_ids(); + + // The following sets up RIDs and Simulcast. + // RIDs are included if Simulcast is requested or if any RID was specified. + RtpParameters send_parameters = + transceiver->internal()->sender_internal()->GetParametersInternal(); + bool has_rids = std::any_of(send_parameters.encodings.begin(), + send_parameters.encodings.end(), + [](const RtpEncodingParameters& encoding) { + return !encoding.rid.empty(); + }); + + std::vector send_rids; + SimulcastLayerList send_layers; + for (const RtpEncodingParameters& encoding : send_parameters.encodings) { + if (encoding.rid.empty()) { + continue; + } + send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend)); + send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active)); + } + + if (has_rids) { + sender_options.rids = send_rids; + } + + sender_options.simulcast_layers = send_layers; + // When RIDs are configured, we must set num_sim_layers to 0 to. + // Otherwise, num_sim_layers must be 1 because either there is no + // simulcast, or simulcast is acheived by munging the SDP. + sender_options.num_sim_layers = has_rids ? 0 : 1; + media_description_options.sender_options.push_back(sender_options); + + return media_description_options; } // Returns the ContentInfo at mline index |i|, or null if none exists. static const ContentInfo* GetContentByIndex( - const SessionDescriptionInterface* sdesc, - size_t i) { - if (!sdesc) { - return nullptr; - } - const ContentInfos& contents = sdesc->description()->contents(); - return (i < contents.size() ? &contents[i] : nullptr); + const SessionDescriptionInterface* sdesc, + size_t i) { + if (!sdesc) { + return nullptr; + } + const ContentInfos& contents = sdesc->description()->contents(); + return (i < contents.size() ? &contents[i] : nullptr); } void TgPeerConnection::GetOptionsForUnifiedPlanOffer( - const RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial - // Offers) and 5.2.2 (Subsequent Offers). - RTC_DCHECK_EQ(session_options->media_description_options.size(), 0); - const ContentInfos no_infos; - const ContentInfos& local_contents = - (local_description() ? local_description()->description()->contents() - : no_infos); - const ContentInfos& remote_contents = - (remote_description() ? remote_description()->description()->contents() - : no_infos); - // The mline indices that can be recycled. New transceivers should reuse these - // slots first. - std::queue recycleable_mline_indices; - // First, go through each media section that exists in either the local or - // remote description and generate a media section in this offer for the - // associated transceiver. If a media section can be recycled, generate a - // default, rejected media section here that can be later overwritten. - for (size_t i = 0; - i < std::max(local_contents.size(), remote_contents.size()); ++i) { - // Either |local_content| or |remote_content| is non-null. - const ContentInfo* local_content = + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial + // Offers) and 5.2.2 (Subsequent Offers). + RTC_DCHECK_EQ(session_options->media_description_options.size(), 0); + const ContentInfos no_infos; + const ContentInfos& local_contents = + (local_description() ? local_description()->description()->contents() + : no_infos); + const ContentInfos& remote_contents = + (remote_description() ? remote_description()->description()->contents() + : no_infos); + // The mline indices that can be recycled. New transceivers should reuse these + // slots first. + std::queue recycleable_mline_indices; + // First, go through each media section that exists in either the local or + // remote description and generate a media section in this offer for the + // associated transceiver. If a media section can be recycled, generate a + // default, rejected media section here that can be later overwritten. + for (size_t i = 0; + i < std::max(local_contents.size(), remote_contents.size()); ++i) { + // Either |local_content| or |remote_content| is non-null. + const ContentInfo* local_content = (i < local_contents.size() ? &local_contents[i] : nullptr); - const ContentInfo* current_local_content = + const ContentInfo* current_local_content = GetContentByIndex(current_local_description(), i); - const ContentInfo* remote_content = + const ContentInfo* remote_content = (i < remote_contents.size() ? &remote_contents[i] : nullptr); - const ContentInfo* current_remote_content = + const ContentInfo* current_remote_content = GetContentByIndex(current_remote_description(), i); - bool had_been_rejected = + bool had_been_rejected = (current_local_content && current_local_content->rejected) || (current_remote_content && current_remote_content->rejected); - const std::string& mid = + const std::string& mid = (local_content ? local_content->name : remote_content->name); - cricket::MediaType media_type = + cricket::MediaType media_type = (local_content ? local_content->media_description()->type() - : remote_content->media_description()->type()); - if (media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO) { - auto transceiver = GetAssociatedTransceiver(mid); - RTC_CHECK(transceiver); - // A media section is considered eligible for recycling if it is marked as - // rejected in either the current local or current remote description. - if (had_been_rejected && transceiver->stopped()) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions(transceiver->media_type(), mid, - RtpTransceiverDirection::kInactive, - /*stopped=*/true)); - recycleable_mline_indices.push(i); - } else { - session_options->media_description_options.push_back( - GetMediaDescriptionOptionsForTransceiver(transceiver, mid)); - // CreateOffer shouldn't really cause any state changes in - // TgPeerConnection, but we need a way to match new transceivers to new - // media sections in SetLocalDescription and JSEP specifies this is done - // by recording the index of the media section generated for the - // transceiver in the offer. - transceiver->internal()->set_mline_index(i); - } + : remote_content->media_description()->type()); + if (media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO) { + auto transceiver = GetAssociatedTransceiver(mid); + RTC_CHECK(transceiver); + // A media section is considered eligible for recycling if it is marked as + // rejected in either the current local or current remote description. + if (had_been_rejected && transceiver->stopped()) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions(transceiver->media_type(), mid, + RtpTransceiverDirection::kInactive, + /*stopped=*/true)); + recycleable_mline_indices.push(i); + } else { + session_options->media_description_options.push_back( + GetMediaDescriptionOptionsForTransceiver(transceiver, mid)); + // CreateOffer shouldn't really cause any state changes in + // TgPeerConnection, but we need a way to match new transceivers to new + // media sections in SetLocalDescription and JSEP specifies this is done + // by recording the index of the media section generated for the + // transceiver in the offer. + transceiver->internal()->set_mline_index(i); + } + } } - } - - // Next, look for transceivers that are newly added (that is, are not stopped - // and not associated). Reuse media sections marked as recyclable first, - // otherwise append to the end of the offer. New media sections should be - // added in the order they were added to the TgPeerConnection. - for (const auto& transceiver : transceivers_) { - if (transceiver->mid() || transceiver->stopped()) { - continue; + + // Next, look for transceivers that are newly added (that is, are not stopped + // and not associated). Reuse media sections marked as recyclable first, + // otherwise append to the end of the offer. New media sections should be + // added in the order they were added to the TgPeerConnection. + for (const auto& transceiver : transceivers_) { + if (transceiver->mid() || transceiver->stopped()) { + continue; + } + size_t mline_index; + if (!recycleable_mline_indices.empty()) { + mline_index = recycleable_mline_indices.front(); + recycleable_mline_indices.pop(); + session_options->media_description_options[mline_index] = + GetMediaDescriptionOptionsForTransceiver(transceiver, + mid_generator_()); + } else { + mline_index = session_options->media_description_options.size(); + session_options->media_description_options.push_back( + GetMediaDescriptionOptionsForTransceiver(transceiver, + mid_generator_())); + } + // See comment above for why CreateOffer changes the transceiver's state. + transceiver->internal()->set_mline_index(mline_index); } - size_t mline_index; - if (!recycleable_mline_indices.empty()) { - mline_index = recycleable_mline_indices.front(); - recycleable_mline_indices.pop(); - session_options->media_description_options[mline_index] = - GetMediaDescriptionOptionsForTransceiver(transceiver, - mid_generator_()); - } else { - mline_index = session_options->media_description_options.size(); - session_options->media_description_options.push_back( - GetMediaDescriptionOptionsForTransceiver(transceiver, - mid_generator_())); - } - // See comment above for why CreateOffer changes the transceiver's state. - transceiver->internal()->set_mline_index(mline_index); - } } void TgPeerConnection::GetOptionsForAnswer( - const RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - ExtractSharedMediaSessionOptions(offer_answer_options, session_options); - - if (IsUnifiedPlan()) { + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + ExtractSharedMediaSessionOptions(offer_answer_options, session_options); + GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options); - } else { - GetOptionsForPlanBAnswer(offer_answer_options, session_options); - } - - // Apply ICE renomination flag. - for (auto& options : session_options->media_description_options) { - options.transport_options.enable_ice_renomination = - configuration_.enable_ice_renomination; - } - - session_options->rtcp_cname = rtcp_cname_; - session_options->crypto_options = GetCryptoOptions(); - session_options->pooled_ice_credentials = - network_thread()->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, - port_allocator_.get())); - - // If datagram transport is in use, add opaque transport parameters. - if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + + // Apply ICE renomination flag. for (auto& options : session_options->media_description_options) { - absl::optional params = - transport_controller_->GetTransportParameters(options.mid); - if (!params) { - continue; - } - options.transport_options.opaque_parameters = params; - if ((use_datagram_transport_ && - (options.type == cricket::MEDIA_TYPE_AUDIO || - options.type == cricket::MEDIA_TYPE_VIDEO)) || - (use_datagram_transport_for_data_channels_ && - options.type == cricket::MEDIA_TYPE_DATA)) { - options.alt_protocol = params->protocol; - } + options.transport_options.enable_ice_renomination = + configuration_.enable_ice_renomination; + } + + session_options->rtcp_cname = rtcp_cname_; + session_options->crypto_options = GetCryptoOptions(); + session_options->pooled_ice_credentials = + network_thread()->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, + port_allocator_.get())); + + // If datagram transport is in use, add opaque transport parameters. + if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { + for (auto& options : session_options->media_description_options) { + absl::optional params = + transport_controller_->GetTransportParameters(options.mid); + if (!params) { + continue; + } + options.transport_options.opaque_parameters = params; + if ((use_datagram_transport_ && + (options.type == cricket::MEDIA_TYPE_AUDIO || + options.type == cricket::MEDIA_TYPE_VIDEO)) || + (use_datagram_transport_for_data_channels_ && + options.type == cricket::MEDIA_TYPE_DATA)) { + options.alt_protocol = params->protocol; + } + } } - } -} - -void TgPeerConnection::GetOptionsForPlanBAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - // Figure out transceiver directional preferences. - bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO); - bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO); - - // By default, generate sendrecv/recvonly m= sections. The direction is also - // restricted by the direction in the offer. - bool recv_audio = true; - bool recv_video = true; - - // The "offer_to_receive_X" options allow those defaults to be overridden. - if (offer_answer_options.offer_to_receive_audio != - RTCOfferAnswerOptions::kUndefined) { - recv_audio = (offer_answer_options.offer_to_receive_audio > 0); - } - if (offer_answer_options.offer_to_receive_video != - RTCOfferAnswerOptions::kUndefined) { - recv_video = (offer_answer_options.offer_to_receive_video > 0); - } - - absl::optional audio_index; - absl::optional video_index; - absl::optional data_index; - - // Generate m= sections that match those in the offer. - // Note that mediasession.cc will handle intersection our preferred - // direction with the offered direction. - GenerateMediaDescriptionOptions( - remote_description(), - RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), - RtpTransceiverDirectionFromSendRecv(send_video, recv_video), &audio_index, - &video_index, &data_index, session_options); - - cricket::MediaDescriptionOptions* audio_media_description_options = - !audio_index ? nullptr - : &session_options->media_description_options[*audio_index]; - cricket::MediaDescriptionOptions* video_media_description_options = - !video_index ? nullptr - : &session_options->media_description_options[*video_index]; - - AddPlanBRtpSenderOptions(GetSendersInternal(), - audio_media_description_options, - video_media_description_options, - offer_answer_options.num_simulcast_layers); } void TgPeerConnection::GetOptionsForUnifiedPlanAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial - // Answers) and 5.3.2 (Subsequent Answers). - RTC_DCHECK(remote_description()); - RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer); - for (const ContentInfo& content : - remote_description()->description()->contents()) { - cricket::MediaType media_type = content.media_description()->type(); - if (media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO) { - auto transceiver = GetAssociatedTransceiver(content.name); - RTC_CHECK(transceiver); - session_options->media_description_options.push_back( - GetMediaDescriptionOptionsForTransceiver(transceiver, content.name)); + const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, + cricket::MediaSessionOptions* session_options) { + // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial + // Answers) and 5.3.2 (Subsequent Answers). + RTC_DCHECK(remote_description()); + RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer); + for (const ContentInfo& content : + remote_description()->description()->contents()) { + cricket::MediaType media_type = content.media_description()->type(); + if (media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO) { + auto transceiver = GetAssociatedTransceiver(content.name); + RTC_CHECK(transceiver); + session_options->media_description_options.push_back( + GetMediaDescriptionOptionsForTransceiver(transceiver, content.name)); + } } - } } void TgPeerConnection::GenerateMediaDescriptionOptions( - const SessionDescriptionInterface* session_desc, - RtpTransceiverDirection audio_direction, - RtpTransceiverDirection video_direction, - absl::optional* audio_index, - absl::optional* video_index, - absl::optional* data_index, - cricket::MediaSessionOptions* session_options) { - for (const cricket::ContentInfo& content : - session_desc->description()->contents()) { - if (IsAudioContent(&content)) { - // If we already have an audio m= section, reject this extra one. - if (*audio_index) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions( - cricket::MEDIA_TYPE_AUDIO, content.name, - RtpTransceiverDirection::kInactive, /*stopped=*/true)); - } else { - bool stopped = (audio_direction == RtpTransceiverDirection::kInactive); - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO, - content.name, audio_direction, - stopped)); - *audio_index = session_options->media_description_options.size() - 1; - } - } else if (IsVideoContent(&content)) { - // If we already have an video m= section, reject this extra one. - if (*video_index) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions( - cricket::MEDIA_TYPE_VIDEO, content.name, - RtpTransceiverDirection::kInactive, /*stopped=*/true)); - } else { - bool stopped = (video_direction == RtpTransceiverDirection::kInactive); - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO, - content.name, video_direction, - stopped)); - *video_index = session_options->media_description_options.size() - 1; - } + const SessionDescriptionInterface* session_desc, + RtpTransceiverDirection audio_direction, + RtpTransceiverDirection video_direction, + absl::optional* audio_index, + absl::optional* video_index, + absl::optional* data_index, + cricket::MediaSessionOptions* session_options) { + for (const cricket::ContentInfo& content : + session_desc->description()->contents()) { + if (IsAudioContent(&content)) { + // If we already have an audio m= section, reject this extra one. + if (*audio_index) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions( + cricket::MEDIA_TYPE_AUDIO, content.name, + RtpTransceiverDirection::kInactive, /*stopped=*/true)); + } else { + bool stopped = (audio_direction == RtpTransceiverDirection::kInactive); + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO, + content.name, audio_direction, + stopped)); + *audio_index = session_options->media_description_options.size() - 1; + } + } else if (IsVideoContent(&content)) { + // If we already have an video m= section, reject this extra one. + if (*video_index) { + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions( + cricket::MEDIA_TYPE_VIDEO, content.name, + RtpTransceiverDirection::kInactive, /*stopped=*/true)); + } else { + bool stopped = (video_direction == RtpTransceiverDirection::kInactive); + session_options->media_description_options.push_back( + cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO, + content.name, video_direction, + stopped)); + *video_index = session_options->media_description_options.size() - 1; + } + } } - } } void TgPeerConnection::RemoveSenders(cricket::MediaType media_type) { - UpdateLocalSenders(std::vector(), media_type); - UpdateRemoteSendersList(std::vector(), false, - media_type, nullptr); + UpdateLocalSenders(std::vector(), media_type); + UpdateRemoteSendersList(std::vector(), false, + media_type, nullptr); } void TgPeerConnection::UpdateRemoteSendersList( - const cricket::StreamParamsVec& streams, - bool default_sender_needed, - cricket::MediaType media_type, - StreamCollection* new_streams) { - RTC_DCHECK(!IsUnifiedPlan()); - - std::vector* current_senders = - GetRemoteSenderInfos(media_type); - - // Find removed senders. I.e., senders where the sender id or ssrc don't match - // the new StreamParam. - for (auto sender_it = current_senders->begin(); - sender_it != current_senders->end(); - /* incremented manually */) { - const RtpSenderInfo& info = *sender_it; - const cricket::StreamParams* params = + const cricket::StreamParamsVec& streams, + bool default_sender_needed, + cricket::MediaType media_type, + StreamCollection* new_streams) { + RTC_DCHECK(!IsUnifiedPlan()); + + std::vector* current_senders = + GetRemoteSenderInfos(media_type); + + // Find removed senders. I.e., senders where the sender id or ssrc don't match + // the new StreamParam. + for (auto sender_it = current_senders->begin(); + sender_it != current_senders->end(); + /* incremented manually */) { + const RtpSenderInfo& info = *sender_it; + const cricket::StreamParams* params = cricket::GetStreamBySsrc(streams, info.first_ssrc); - std::string params_stream_id; - if (params) { - params_stream_id = - (!params->first_stream_id().empty() ? params->first_stream_id() - : kDefaultStreamId); + std::string params_stream_id; + if (params) { + params_stream_id = + (!params->first_stream_id().empty() ? params->first_stream_id() + : kDefaultStreamId); + } + bool sender_exists = params && params->id == info.sender_id && + params_stream_id == info.stream_id; + // If this is a default track, and we still need it, don't remove it. + if ((info.stream_id == kDefaultStreamId && default_sender_needed) || + sender_exists) { + ++sender_it; + } else { + OnRemoteSenderRemoved(info, media_type); + sender_it = current_senders->erase(sender_it); + } } - bool sender_exists = params && params->id == info.sender_id && - params_stream_id == info.stream_id; - // If this is a default track, and we still need it, don't remove it. - if ((info.stream_id == kDefaultStreamId && default_sender_needed) || - sender_exists) { - ++sender_it; - } else { - OnRemoteSenderRemoved(info, media_type); - sender_it = current_senders->erase(sender_it); - } - } - - // Find new and active senders. - for (const cricket::StreamParams& params : streams) { - if (!params.has_ssrcs()) { - // The remote endpoint has streams, but didn't signal ssrcs. For an active - // sender, this means it is coming from a Unified Plan endpoint,so we just - // create a default. - default_sender_needed = true; - break; - } - - // |params.id| is the sender id and the stream id uses the first of - // |params.stream_ids|. The remote description could come from a Unified - // Plan endpoint, with multiple or no stream_ids() signaled. Since this is - // not supported in Plan B, we just take the first here and create the - // default stream ID if none is specified. - const std::string& stream_id = + + // Find new and active senders. + for (const cricket::StreamParams& params : streams) { + if (!params.has_ssrcs()) { + // The remote endpoint has streams, but didn't signal ssrcs. For an active + // sender, this means it is coming from a Unified Plan endpoint,so we just + // create a default. + default_sender_needed = true; + break; + } + + // |params.id| is the sender id and the stream id uses the first of + // |params.stream_ids|. The remote description could come from a Unified + // Plan endpoint, with multiple or no stream_ids() signaled. Since this is + // not supported in Plan B, we just take the first here and create the + // default stream ID if none is specified. + const std::string& stream_id = (!params.first_stream_id().empty() ? params.first_stream_id() - : kDefaultStreamId); - const std::string& sender_id = params.id; - uint32_t ssrc = params.first_ssrc(); - - rtc::scoped_refptr stream = + : kDefaultStreamId); + const std::string& sender_id = params.id; + uint32_t ssrc = params.first_ssrc(); + + rtc::scoped_refptr stream = remote_streams_->find(stream_id); - if (!stream) { - // This is a new MediaStream. Create a new remote MediaStream. - stream = MediaStreamProxy::Create(rtc::Thread::Current(), - MediaStream::Create(stream_id)); - remote_streams_->AddStream(stream); - new_streams->AddStream(stream); - } - - const RtpSenderInfo* sender_info = + if (!stream) { + // This is a new MediaStream. Create a new remote MediaStream. + stream = MediaStreamProxy::Create(rtc::Thread::Current(), + MediaStream::Create(stream_id)); + remote_streams_->AddStream(stream); + new_streams->AddStream(stream); + } + + const RtpSenderInfo* sender_info = FindSenderInfo(*current_senders, stream_id, sender_id); - if (!sender_info) { - current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); - OnRemoteSenderAdded(current_senders->back(), media_type); + if (!sender_info) { + current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); + OnRemoteSenderAdded(current_senders->back(), media_type); + } } - } - - // Add default sender if necessary. - if (default_sender_needed) { - rtc::scoped_refptr default_stream = + + // Add default sender if necessary. + if (default_sender_needed) { + rtc::scoped_refptr default_stream = remote_streams_->find(kDefaultStreamId); - if (!default_stream) { - // Create the new default MediaStream. - default_stream = MediaStreamProxy::Create( - rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId)); - remote_streams_->AddStream(default_stream); - new_streams->AddStream(default_stream); - } - std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO) - ? kDefaultAudioSenderId - : kDefaultVideoSenderId; - const RtpSenderInfo* default_sender_info = + if (!default_stream) { + // Create the new default MediaStream. + default_stream = MediaStreamProxy::Create( + rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId)); + remote_streams_->AddStream(default_stream); + new_streams->AddStream(default_stream); + } + std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO) + ? kDefaultAudioSenderId + : kDefaultVideoSenderId; + const RtpSenderInfo* default_sender_info = FindSenderInfo(*current_senders, kDefaultStreamId, default_sender_id); - if (!default_sender_info) { - current_senders->push_back( - RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0)); - OnRemoteSenderAdded(current_senders->back(), media_type); + if (!default_sender_info) { + current_senders->push_back( + RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0)); + OnRemoteSenderAdded(current_senders->back(), media_type); + } } - } } void TgPeerConnection::OnRemoteSenderAdded(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - RTC_LOG(LS_INFO) << "Creating " << cricket::MediaTypeToString(media_type) - << " receiver for track_id=" << sender_info.sender_id - << " and stream_id=" << sender_info.stream_id; - - MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - CreateAudioReceiver(stream, sender_info); - } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - CreateVideoReceiver(stream, sender_info); - } else { - RTC_NOTREACHED() << "Invalid media type"; - } + cricket::MediaType media_type) { + RTC_LOG(LS_INFO) << "Creating " << cricket::MediaTypeToString(media_type) + << " receiver for track_id=" << sender_info.sender_id + << " and stream_id=" << sender_info.stream_id; + + MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + CreateAudioReceiver(stream, sender_info); + } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { + CreateVideoReceiver(stream, sender_info); + } else { + RTC_NOTREACHED() << "Invalid media type"; + } } void TgPeerConnection::OnRemoteSenderRemoved(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - RTC_LOG(LS_INFO) << "Removing " << cricket::MediaTypeToString(media_type) - << " receiver for track_id=" << sender_info.sender_id - << " and stream_id=" << sender_info.stream_id; - - MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); - - rtc::scoped_refptr receiver; - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - // When the MediaEngine audio channel is destroyed, the RemoteAudioSource - // will be notified which will end the AudioRtpReceiver::track(). - receiver = RemoveAndStopReceiver(sender_info); - rtc::scoped_refptr audio_track = + cricket::MediaType media_type) { + RTC_LOG(LS_INFO) << "Removing " << cricket::MediaTypeToString(media_type) + << " receiver for track_id=" << sender_info.sender_id + << " and stream_id=" << sender_info.stream_id; + + MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); + + rtc::scoped_refptr receiver; + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + // When the MediaEngine audio channel is destroyed, the RemoteAudioSource + // will be notified which will end the AudioRtpReceiver::track(). + receiver = RemoveAndStopReceiver(sender_info); + rtc::scoped_refptr audio_track = stream->FindAudioTrack(sender_info.sender_id); - if (audio_track) { - stream->RemoveTrack(audio_track); - } - } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - // Stopping or destroying a VideoRtpReceiver will end the - // VideoRtpReceiver::track(). - receiver = RemoveAndStopReceiver(sender_info); - rtc::scoped_refptr video_track = + if (audio_track) { + stream->RemoveTrack(audio_track); + } + } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { + // Stopping or destroying a VideoRtpReceiver will end the + // VideoRtpReceiver::track(). + receiver = RemoveAndStopReceiver(sender_info); + rtc::scoped_refptr video_track = stream->FindVideoTrack(sender_info.sender_id); - if (video_track) { - // There's no guarantee the track is still available, e.g. the track may - // have been removed from the stream by an application. - stream->RemoveTrack(video_track); + if (video_track) { + // There's no guarantee the track is still available, e.g. the track may + // have been removed from the stream by an application. + stream->RemoveTrack(video_track); + } + } else { + RTC_NOTREACHED() << "Invalid media type"; + } + if (receiver) { + Observer()->OnRemoveTrack(receiver); } - } else { - RTC_NOTREACHED() << "Invalid media type"; - } - if (receiver) { - Observer()->OnRemoveTrack(receiver); - } } void TgPeerConnection::UpdateEndedRemoteMediaStreams() { - std::vector> streams_to_remove; - for (size_t i = 0; i < remote_streams_->count(); ++i) { - MediaStreamInterface* stream = remote_streams_->at(i); - if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) { - streams_to_remove.push_back(stream); + std::vector> streams_to_remove; + for (size_t i = 0; i < remote_streams_->count(); ++i) { + MediaStreamInterface* stream = remote_streams_->at(i); + if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) { + streams_to_remove.push_back(stream); + } + } + + for (auto& stream : streams_to_remove) { + remote_streams_->RemoveStream(stream); + Observer()->OnRemoveStream(std::move(stream)); } - } - - for (auto& stream : streams_to_remove) { - remote_streams_->RemoveStream(stream); - Observer()->OnRemoveStream(std::move(stream)); - } } void TgPeerConnection::UpdateLocalSenders( - const std::vector& streams, - cricket::MediaType media_type) { - std::vector* current_senders = GetLocalSenderInfos(media_type); - - // Find removed tracks. I.e., tracks where the track id, stream id or ssrc - // don't match the new StreamParam. - for (auto sender_it = current_senders->begin(); - sender_it != current_senders->end(); - /* incremented manually */) { - const RtpSenderInfo& info = *sender_it; - const cricket::StreamParams* params = + const std::vector& streams, + cricket::MediaType media_type) { + std::vector* current_senders = GetLocalSenderInfos(media_type); + + // Find removed tracks. I.e., tracks where the track id, stream id or ssrc + // don't match the new StreamParam. + for (auto sender_it = current_senders->begin(); + sender_it != current_senders->end(); + /* incremented manually */) { + const RtpSenderInfo& info = *sender_it; + const cricket::StreamParams* params = cricket::GetStreamBySsrc(streams, info.first_ssrc); - if (!params || params->id != info.sender_id || - params->first_stream_id() != info.stream_id) { - OnLocalSenderRemoved(info, media_type); - sender_it = current_senders->erase(sender_it); - } else { - ++sender_it; + if (!params || params->id != info.sender_id || + params->first_stream_id() != info.stream_id) { + OnLocalSenderRemoved(info, media_type); + sender_it = current_senders->erase(sender_it); + } else { + ++sender_it; + } } - } - - // Find new and active senders. - for (const cricket::StreamParams& params : streams) { - // The sync_label is the MediaStream label and the |stream.id| is the - // sender id. - const std::string& stream_id = params.first_stream_id(); - const std::string& sender_id = params.id; - uint32_t ssrc = params.first_ssrc(); - const RtpSenderInfo* sender_info = + + // Find new and active senders. + for (const cricket::StreamParams& params : streams) { + // The sync_label is the MediaStream label and the |stream.id| is the + // sender id. + const std::string& stream_id = params.first_stream_id(); + const std::string& sender_id = params.id; + uint32_t ssrc = params.first_ssrc(); + const RtpSenderInfo* sender_info = FindSenderInfo(*current_senders, stream_id, sender_id); - if (!sender_info) { - current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); - OnLocalSenderAdded(current_senders->back(), media_type); + if (!sender_info) { + current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); + OnLocalSenderAdded(current_senders->back(), media_type); + } } - } } void TgPeerConnection::OnLocalSenderAdded(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - RTC_DCHECK(!IsUnifiedPlan()); - auto sender = FindSenderById(sender_info.sender_id); - if (!sender) { - RTC_LOG(LS_WARNING) << "An unknown RtpSender with id " - << sender_info.sender_id - << " has been configured in the local description."; - return; - } - - if (sender->media_type() != media_type) { - RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" - " description with an unexpected media type."; - return; - } - - sender->internal()->set_stream_ids({sender_info.stream_id}); - sender->internal()->SetSsrc(sender_info.first_ssrc); + cricket::MediaType media_type) { + RTC_DCHECK(!IsUnifiedPlan()); + auto sender = FindSenderById(sender_info.sender_id); + if (!sender) { + RTC_LOG(LS_WARNING) << "An unknown RtpSender with id " + << sender_info.sender_id + << " has been configured in the local description."; + return; + } + + if (sender->media_type() != media_type) { + RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" + " description with an unexpected media type."; + return; + } + + sender->internal()->set_stream_ids({sender_info.stream_id}); + sender->internal()->SetSsrc(sender_info.first_ssrc); } void TgPeerConnection::OnLocalSenderRemoved(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - auto sender = FindSenderById(sender_info.sender_id); - if (!sender) { - // This is the normal case. I.e., RemoveStream has been called and the - // SessionDescriptions has been renegotiated. - return; - } - - // A sender has been removed from the SessionDescription but it's still - // associated with the TgPeerConnection. This only occurs if the SDP doesn't - // match with the calls to CreateSender, AddStream and RemoveStream. - if (sender->media_type() != media_type) { - RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" - " description with an unexpected media type."; - return; - } - - sender->internal()->SetSsrc(0); + cricket::MediaType media_type) { + auto sender = FindSenderById(sender_info.sender_id); + if (!sender) { + // This is the normal case. I.e., RemoveStream has been called and the + // SessionDescriptions has been renegotiated. + return; + } + + // A sender has been removed from the SessionDescription but it's still + // associated with the TgPeerConnection. This only occurs if the SDP doesn't + // match with the calls to CreateSender, AddStream and RemoveStream. + if (sender->media_type() != media_type) { + RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" + " description with an unexpected media type."; + return; + } + + sender->internal()->SetSsrc(0); } rtc::scoped_refptr> TgPeerConnection::GetAudioTransceiver() const { - // This method only works with Plan B SDP, where there is a single - // audio/video transceiver. - RTC_DCHECK(!IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - return transceiver; + // This method only works with Plan B SDP, where there is a single + // audio/video transceiver. + RTC_DCHECK(!IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { + return transceiver; + } } - } - RTC_NOTREACHED(); - return nullptr; + RTC_NOTREACHED(); + return nullptr; } rtc::scoped_refptr> TgPeerConnection::GetVideoTransceiver() const { - // This method only works with Plan B SDP, where there is a single - // audio/video transceiver. - RTC_DCHECK(!IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { - return transceiver; + // This method only works with Plan B SDP, where there is a single + // audio/video transceiver. + RTC_DCHECK(!IsUnifiedPlan()); + for (auto transceiver : transceivers_) { + if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { + return transceiver; + } } - } - RTC_NOTREACHED(); - return nullptr; + RTC_NOTREACHED(); + return nullptr; } // TODO(bugs.webrtc.org/7600): Remove this when multiple transceivers with // individual transceiver directions are supported. bool TgPeerConnection::HasRtpSender(cricket::MediaType type) const { - switch (type) { - case cricket::MEDIA_TYPE_AUDIO: - return !GetAudioTransceiver()->internal()->senders().empty(); - case cricket::MEDIA_TYPE_VIDEO: - return !GetVideoTransceiver()->internal()->senders().empty(); - case cricket::MEDIA_TYPE_DATA: - return false; - } - RTC_NOTREACHED(); - return false; + switch (type) { + case cricket::MEDIA_TYPE_AUDIO: + return !GetAudioTransceiver()->internal()->senders().empty(); + case cricket::MEDIA_TYPE_VIDEO: + return !GetVideoTransceiver()->internal()->senders().empty(); + case cricket::MEDIA_TYPE_DATA: + return false; + } + RTC_NOTREACHED(); + return false; } -rtc::scoped_refptr> +rtc::scoped_refptr> TgPeerConnection::FindSenderForTrack(MediaStreamTrackInterface* track) const { - for (const auto& transceiver : transceivers_) { - for (auto sender : transceiver->internal()->senders()) { - if (sender->track() == track) { - return sender; - } + for (const auto& transceiver : transceivers_) { + for (auto sender : transceiver->internal()->senders()) { + if (sender->track() == track) { + return sender; + } + } } - } - return nullptr; + return nullptr; } -rtc::scoped_refptr> +rtc::scoped_refptr> TgPeerConnection::FindSenderById(const std::string& sender_id) const { - for (const auto& transceiver : transceivers_) { - for (auto sender : transceiver->internal()->senders()) { - if (sender->id() == sender_id) { - return sender; - } + for (const auto& transceiver : transceivers_) { + for (auto sender : transceiver->internal()->senders()) { + if (sender->id() == sender_id) { + return sender; + } + } } - } - return nullptr; + return nullptr; } rtc::scoped_refptr> TgPeerConnection::FindReceiverById(const std::string& receiver_id) const { - for (const auto& transceiver : transceivers_) { - for (auto receiver : transceiver->internal()->receivers()) { - if (receiver->id() == receiver_id) { - return receiver; - } + for (const auto& transceiver : transceivers_) { + for (auto receiver : transceiver->internal()->receivers()) { + if (receiver->id() == receiver_id) { + return receiver; + } + } } - } - return nullptr; + return nullptr; } std::vector* TgPeerConnection::GetRemoteSenderInfos(cricket::MediaType media_type) { - RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO); - return (media_type == cricket::MEDIA_TYPE_AUDIO) - ? &remote_audio_sender_infos_ - : &remote_video_sender_infos_; + RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO); + return (media_type == cricket::MEDIA_TYPE_AUDIO) + ? &remote_audio_sender_infos_ + : &remote_video_sender_infos_; } std::vector* TgPeerConnection::GetLocalSenderInfos( - cricket::MediaType media_type) { - RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO); - return (media_type == cricket::MEDIA_TYPE_AUDIO) ? &local_audio_sender_infos_ - : &local_video_sender_infos_; + cricket::MediaType media_type) { + RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO); + return (media_type == cricket::MEDIA_TYPE_AUDIO) ? &local_audio_sender_infos_ + : &local_video_sender_infos_; } const TgPeerConnection::RtpSenderInfo* TgPeerConnection::FindSenderInfo( - const std::vector& infos, - const std::string& stream_id, - const std::string sender_id) const { - for (const RtpSenderInfo& sender_info : infos) { - if (sender_info.stream_id == stream_id && - sender_info.sender_id == sender_id) { - return &sender_info; + const std::vector& infos, + const std::string& stream_id, + const std::string sender_id) const { + for (const RtpSenderInfo& sender_info : infos) { + if (sender_info.stream_id == stream_id && + sender_info.sender_id == sender_id) { + return &sender_info; + } } - } - return nullptr; + return nullptr; } TgPeerConnection::InitializePortAllocatorResult TgPeerConnection::InitializePortAllocator_n( - const cricket::ServerAddresses& stun_servers, - const std::vector& turn_servers, - const RTCConfiguration& configuration) { - RTC_DCHECK_RUN_ON(network_thread()); - - port_allocator_->Initialize(); - // To handle both internal and externally created port allocator, we will - // enable BUNDLE here. - int port_allocator_flags = port_allocator_->flags(); - port_allocator_flags |= cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | - cricket::PORTALLOCATOR_ENABLE_IPV6 | - cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI; - // If the disable-IPv6 flag was specified, we'll not override it - // by experiment. - if (configuration.disable_ipv6) { - port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); - } else if (webrtc::field_trial::FindFullName("WebRTC-IPv6Default") - .find("Disabled") == 0) { - port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); - } - - if (configuration.disable_ipv6_on_wifi) { - port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI); - RTC_LOG(LS_INFO) << "IPv6 candidates on Wi-Fi are disabled."; - } - - if (configuration.tcp_candidate_policy == kTcpCandidatePolicyDisabled) { - port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_TCP; - RTC_LOG(LS_INFO) << "TCP candidates are disabled."; - } - - if (configuration.candidate_network_policy == - kCandidateNetworkPolicyLowCost) { - port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS; - RTC_LOG(LS_INFO) << "Do not gather candidates on high-cost networks"; - } - - if (configuration.disable_link_local_networks) { - port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS; - RTC_LOG(LS_INFO) << "Disable candidates on link-local network interfaces."; - } - - port_allocator_->set_flags(port_allocator_flags); - // No step delay is used while allocating ports. - port_allocator_->set_step_delay(cricket::kMinimumStepDelay); - port_allocator_->SetCandidateFilter( - ConvertIceTransportTypeToCandidateFilter(configuration.type)); - port_allocator_->set_max_ipv6_networks(configuration.max_ipv6_networks); - - auto turn_servers_copy = turn_servers; - for (auto& turn_server : turn_servers_copy) { - turn_server.tls_cert_verifier = tls_cert_verifier_.get(); - } - // Call this last since it may create pooled allocator sessions using the - // properties set above. - port_allocator_->SetConfiguration( - stun_servers, std::move(turn_servers_copy), - configuration.ice_candidate_pool_size, - configuration.GetTurnPortPrunePolicy(), configuration.turn_customizer, - configuration.stun_candidate_keepalive_interval); - - InitializePortAllocatorResult res; - res.enable_ipv6 = port_allocator_flags & cricket::PORTALLOCATOR_ENABLE_IPV6; - return res; + const cricket::ServerAddresses& stun_servers, + const std::vector& turn_servers, + const PeerConnectionInterface::RTCConfiguration& configuration) { + RTC_DCHECK_RUN_ON(network_thread()); + + port_allocator_->Initialize(); + // To handle both internal and externally created port allocator, we will + // enable BUNDLE here. + int port_allocator_flags = port_allocator_->flags(); + port_allocator_flags |= cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_ENABLE_IPV6 | + cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI; + // If the disable-IPv6 flag was specified, we'll not override it + // by experiment. + if (configuration.disable_ipv6) { + port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); + } else if (webrtc::field_trial::FindFullName("WebRTC-IPv6Default") + .find("Disabled") == 0) { + port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); + } + + if (configuration.disable_ipv6_on_wifi) { + port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI); + RTC_LOG(LS_INFO) << "IPv6 candidates on Wi-Fi are disabled."; + } + + if (configuration.tcp_candidate_policy == PeerConnectionInterface::kTcpCandidatePolicyDisabled) { + port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_TCP; + RTC_LOG(LS_INFO) << "TCP candidates are disabled."; + } + + if (configuration.candidate_network_policy == + PeerConnectionInterface::kCandidateNetworkPolicyLowCost) { + port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS; + RTC_LOG(LS_INFO) << "Do not gather candidates on high-cost networks"; + } + + if (configuration.disable_link_local_networks) { + port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS; + RTC_LOG(LS_INFO) << "Disable candidates on link-local network interfaces."; + } + + port_allocator_->set_flags(port_allocator_flags); + // No step delay is used while allocating ports. + port_allocator_->set_step_delay(cricket::kMinimumStepDelay); + port_allocator_->SetCandidateFilter( + ConvertIceTransportTypeToCandidateFilter(configuration.type)); + port_allocator_->set_max_ipv6_networks(configuration.max_ipv6_networks); + + auto turn_servers_copy = turn_servers; + for (auto& turn_server : turn_servers_copy) { + turn_server.tls_cert_verifier = tls_cert_verifier_.get(); + } + // Call this last since it may create pooled allocator sessions using the + // properties set above. + port_allocator_->SetConfiguration( + stun_servers, std::move(turn_servers_copy), + configuration.ice_candidate_pool_size, + configuration.GetTurnPortPrunePolicy(), configuration.turn_customizer, + configuration.stun_candidate_keepalive_interval); + + InitializePortAllocatorResult res; + res.enable_ipv6 = port_allocator_flags & cricket::PORTALLOCATOR_ENABLE_IPV6; + return res; } bool TgPeerConnection::ReconfigurePortAllocator_n( - const cricket::ServerAddresses& stun_servers, - const std::vector& turn_servers, - IceTransportsType type, - int candidate_pool_size, - PortPrunePolicy turn_port_prune_policy, - webrtc::TurnCustomizer* turn_customizer, - absl::optional stun_candidate_keepalive_interval, - bool have_local_description) { - port_allocator_->SetCandidateFilter( - ConvertIceTransportTypeToCandidateFilter(type)); - // According to JSEP, after setLocalDescription, changing the candidate pool - // size is not allowed, and changing the set of ICE servers will not result - // in new candidates being gathered. - if (have_local_description) { - port_allocator_->FreezeCandidatePool(); - } - // Add the custom tls turn servers if they exist. - auto turn_servers_copy = turn_servers; - for (auto& turn_server : turn_servers_copy) { - turn_server.tls_cert_verifier = tls_cert_verifier_.get(); - } - // Call this last since it may create pooled allocator sessions using the - // candidate filter set above. - return port_allocator_->SetConfiguration( - stun_servers, std::move(turn_servers_copy), candidate_pool_size, - turn_port_prune_policy, turn_customizer, - stun_candidate_keepalive_interval); + const cricket::ServerAddresses& stun_servers, + const std::vector& turn_servers, + PeerConnectionInterface::IceTransportsType type, + int candidate_pool_size, + PortPrunePolicy turn_port_prune_policy, + webrtc::TurnCustomizer* turn_customizer, + absl::optional stun_candidate_keepalive_interval, + bool have_local_description) { + port_allocator_->SetCandidateFilter( + ConvertIceTransportTypeToCandidateFilter(type)); + // According to JSEP, after setLocalDescription, changing the candidate pool + // size is not allowed, and changing the set of ICE servers will not result + // in new candidates being gathered. + if (have_local_description) { + port_allocator_->FreezeCandidatePool(); + } + // Add the custom tls turn servers if they exist. + auto turn_servers_copy = turn_servers; + for (auto& turn_server : turn_servers_copy) { + turn_server.tls_cert_verifier = tls_cert_verifier_.get(); + } + // Call this last since it may create pooled allocator sessions using the + // candidate filter set above. + return port_allocator_->SetConfiguration( + stun_servers, std::move(turn_servers_copy), candidate_pool_size, + turn_port_prune_policy, turn_customizer, + stun_candidate_keepalive_interval); } cricket::ChannelManager* TgPeerConnection::channel_manager() const { - return factory_->channel_manager(); + return factory_->channel_manager(); } bool TgPeerConnection::StartRtcEventLog_w( - std::unique_ptr output, - int64_t output_period_ms) { - RTC_DCHECK_RUN_ON(worker_thread()); - if (!event_log_) { - return false; - } - return event_log_->StartLogging(std::move(output), output_period_ms); + std::unique_ptr output, + int64_t output_period_ms) { + RTC_DCHECK_RUN_ON(worker_thread()); + if (!event_log_) { + return false; + } + return event_log_->StartLogging(std::move(output), output_period_ms); } void TgPeerConnection::StopRtcEventLog_w() { - RTC_DCHECK_RUN_ON(worker_thread()); - if (event_log_) { - event_log_->StopLogging(); - } + RTC_DCHECK_RUN_ON(worker_thread()); + if (event_log_) { + event_log_->StopLogging(); + } } cricket::ChannelInterface* TgPeerConnection::GetChannel( - const std::string& content_name) { - for (const auto& transceiver : transceivers_) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && channel->content_name() == content_name) { - return channel; + const std::string& content_name) { + for (const auto& transceiver : transceivers_) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel && channel->content_name() == content_name) { + return channel; + } } - } - if (rtp_data_channel() && - rtp_data_channel()->content_name() == content_name) { - return rtp_data_channel(); - } - return nullptr; + if (rtp_data_channel() && + rtp_data_channel()->content_name() == content_name) { + return rtp_data_channel(); + } + return nullptr; } bool TgPeerConnection::GetSctpSslRole(rtc::SSLRole* role) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!local_description() || !remote_description()) { - RTC_LOG(LS_INFO) + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!local_description() || !remote_description()) { + RTC_LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " - "SSL Role of the SCTP transport."; - return false; - } - - absl::optional dtls_role; - if (sctp_mid_) { - dtls_role = transport_controller_->GetDtlsRole(*sctp_mid_); - if (!dtls_role && is_caller_.has_value()) { - dtls_role = *is_caller_ ? rtc::SSL_SERVER : rtc::SSL_CLIENT; + "SSL Role of the SCTP transport."; + return false; } - *role = *dtls_role; - return true; - } - return false; + + absl::optional dtls_role; + if (sctp_mid_) { + dtls_role = transport_controller_->GetDtlsRole(*sctp_mid_); + if (!dtls_role && is_caller_.has_value()) { + dtls_role = *is_caller_ ? rtc::SSL_SERVER : rtc::SSL_CLIENT; + } + *role = *dtls_role; + return true; + } + return false; } bool TgPeerConnection::GetSslRole(const std::string& content_name, - rtc::SSLRole* role) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!local_description() || !remote_description()) { - RTC_LOG(LS_INFO) + rtc::SSLRole* role) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!local_description() || !remote_description()) { + RTC_LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " - "SSL Role of the session."; + "SSL Role of the session."; + return false; + } + + auto dtls_role = transport_controller_->GetDtlsRole(content_name); + if (dtls_role) { + *role = *dtls_role; + return true; + } return false; - } - - auto dtls_role = transport_controller_->GetDtlsRole(content_name); - if (dtls_role) { - *role = *dtls_role; - return true; - } - return false; } void TgPeerConnection::SetSessionError(SessionError error, - const std::string& error_desc) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (error != session_error_) { - session_error_ = error; - session_error_desc_ = error_desc; - } + const std::string& error_desc) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (error != session_error_) { + session_error_ = error; + session_error_desc_ = error_desc; + } } RTCError TgPeerConnection::UpdateSessionState( - SdpType type, - cricket::ContentSource source, - const cricket::SessionDescription* description) { - RTC_DCHECK_RUN_ON(signaling_thread()); - - // 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(session_error() == SessionError::kNone); - - // If this is answer-ish we're ready to let media flow. - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - EnableSending(); - } - - // Update the signaling state according to the specified state machine (see - // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum). - if (type == SdpType::kOffer) { - ChangeSignalingState(source == cricket::CS_LOCAL + SdpType type, + cricket::ContentSource source, + const cricket::SessionDescription* description) { + RTC_DCHECK_RUN_ON(signaling_thread()); + + // 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(session_error() == SessionError::kNone); + + // If this is answer-ish we're ready to let media flow. + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + EnableSending(); + } + + // Update the signaling state according to the specified state machine (see + // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum). + if (type == SdpType::kOffer) { + ChangeSignalingState(source == cricket::CS_LOCAL ? PeerConnectionInterface::kHaveLocalOffer : PeerConnectionInterface::kHaveRemoteOffer); - } else if (type == SdpType::kPrAnswer) { - ChangeSignalingState(source == cricket::CS_LOCAL + } else if (type == SdpType::kPrAnswer) { + ChangeSignalingState(source == cricket::CS_LOCAL ? PeerConnectionInterface::kHaveLocalPrAnswer : PeerConnectionInterface::kHaveRemotePrAnswer); - } else { - RTC_DCHECK(type == SdpType::kAnswer); - ChangeSignalingState(PeerConnectionInterface::kStable); - transceiver_stable_states_by_transceivers_.clear(); - } - - // Update internal objects according to the session description's media - // descriptions. - RTCError error = PushdownMediaDescription(type, source); - if (!error.ok()) { - return error; - } - - return RTCError::OK(); + } else { + RTC_DCHECK(type == SdpType::kAnswer); + ChangeSignalingState(PeerConnectionInterface::kStable); + transceiver_stable_states_by_transceivers_.clear(); + } + + // Update internal objects according to the session description's media + // descriptions. + RTCError error = PushdownMediaDescription(type, source); + if (!error.ok()) { + return error; + } + + return RTCError::OK(); } RTCError TgPeerConnection::PushdownMediaDescription( - SdpType type, - cricket::ContentSource source) { - const SessionDescriptionInterface* sdesc = - (source == cricket::CS_LOCAL ? local_description() - : remote_description()); - RTC_DCHECK(sdesc); - - // Push down the new SDP media section for each audio/video transceiver. - for (const auto& transceiver : transceivers_) { - const ContentInfo* content_info = + SdpType type, + cricket::ContentSource source) { + const SessionDescriptionInterface* sdesc = + (source == cricket::CS_LOCAL ? local_description() + : remote_description()); + RTC_DCHECK(sdesc); + + // Push down the new SDP media section for each audio/video transceiver. + for (const auto& transceiver : transceivers_) { + const ContentInfo* content_info = FindMediaSectionForTransceiver(transceiver, sdesc); - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (!channel || !content_info || content_info->rejected) { - continue; - } - const MediaContentDescription* content_desc = + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (!channel || !content_info || content_info->rejected) { + continue; + } + const MediaContentDescription* content_desc = content_info->media_description(); - if (!content_desc) { - continue; + if (!content_desc) { + continue; + } + std::string error; + bool success = (source == cricket::CS_LOCAL) + ? channel->SetLocalContent(content_desc, type, &error) + : channel->SetRemoteContent(content_desc, type, &error); + if (!success) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error); + } } - std::string error; - bool success = (source == cricket::CS_LOCAL) - ? channel->SetLocalContent(content_desc, type, &error) - : channel->SetRemoteContent(content_desc, type, &error); - if (!success) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error); - } - } - - // 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_mid_ && local_description() && remote_description()) { - rtc::scoped_refptr sctp_transport = + + // 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_mid_ && local_description() && remote_description()) { + rtc::scoped_refptr sctp_transport = transport_controller_->GetSctpTransport(*sctp_mid_); - auto local_sctp_description = cricket::GetFirstSctpDataContentDescription( - local_description()->description()); - auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription( - remote_description()->description()); - if (sctp_transport && local_sctp_description && remote_sctp_description) { - int max_message_size; - // A remote max message size of zero means "any size supported". - // We configure the connection with our own max message size. - if (remote_sctp_description->max_message_size() == 0) { - max_message_size = local_sctp_description->max_message_size(); - } else { - max_message_size = - std::min(local_sctp_description->max_message_size(), - remote_sctp_description->max_message_size()); - } - sctp_transport->Start(local_sctp_description->port(), - remote_sctp_description->port(), max_message_size); + auto local_sctp_description = cricket::GetFirstSctpDataContentDescription( + local_description()->description()); + auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription( + remote_description()->description()); + if (sctp_transport && local_sctp_description && remote_sctp_description) { + int max_message_size; + // A remote max message size of zero means "any size supported". + // We configure the connection with our own max message size. + if (remote_sctp_description->max_message_size() == 0) { + max_message_size = local_sctp_description->max_message_size(); + } else { + max_message_size = + std::min(local_sctp_description->max_message_size(), + remote_sctp_description->max_message_size()); + } + sctp_transport->Start(local_sctp_description->port(), + remote_sctp_description->port(), max_message_size); + } } - } - - return RTCError::OK(); + + return RTCError::OK(); } RTCError TgPeerConnection::PushdownTransportDescription( - cricket::ContentSource source, - SdpType type) { - RTC_DCHECK_RUN_ON(signaling_thread()); - - if (source == cricket::CS_LOCAL) { - const SessionDescriptionInterface* sdesc = local_description(); - RTC_DCHECK(sdesc); - return transport_controller_->SetLocalDescription(type, - sdesc->description()); - } else { - const SessionDescriptionInterface* sdesc = remote_description(); - RTC_DCHECK(sdesc); - return transport_controller_->SetRemoteDescription(type, - sdesc->description()); - } + cricket::ContentSource source, + SdpType type) { + RTC_DCHECK_RUN_ON(signaling_thread()); + + if (source == cricket::CS_LOCAL) { + const SessionDescriptionInterface* sdesc = local_description(); + RTC_DCHECK(sdesc); + return transport_controller_->SetLocalDescription(type, + sdesc->description()); + } else { + const SessionDescriptionInterface* sdesc = remote_description(); + RTC_DCHECK(sdesc); + return transport_controller_->SetRemoteDescription(type, + sdesc->description()); + } } bool TgPeerConnection::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; + 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; } cricket::IceConfig TgPeerConnection::ParseIceConfig( - const PeerConnectionInterface::RTCConfiguration& config) const { - cricket::ContinualGatheringPolicy gathering_policy; - 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 = RTCConfigurationToIceConfigOptionalInt( - 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 = - RTCConfigurationToIceConfigOptionalInt( - 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.surface_ice_candidates_on_ice_transport_type_changed = - config.surface_ice_candidates_on_ice_transport_type_changed; - ice_config.ice_check_interval_strong_connectivity = - config.ice_check_interval_strong_connectivity; - ice_config.ice_check_interval_weak_connectivity = - config.ice_check_interval_weak_connectivity; - ice_config.ice_check_min_interval = config.ice_check_min_interval; - ice_config.ice_unwritable_timeout = config.ice_unwritable_timeout; - ice_config.ice_unwritable_min_checks = config.ice_unwritable_min_checks; - ice_config.ice_inactive_timeout = config.ice_inactive_timeout; - ice_config.stun_keepalive_interval = config.stun_candidate_keepalive_interval; - ice_config.regather_all_networks_interval_range = - config.ice_regather_interval_range; - ice_config.network_preference = config.network_preference; - return ice_config; + const PeerConnectionInterface::RTCConfiguration& config) const { + cricket::ContinualGatheringPolicy gathering_policy; + 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 = RTCConfigurationToIceConfigOptionalInt( + 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 = + RTCConfigurationToIceConfigOptionalInt( + 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.surface_ice_candidates_on_ice_transport_type_changed = + config.surface_ice_candidates_on_ice_transport_type_changed; + ice_config.ice_check_interval_strong_connectivity = + config.ice_check_interval_strong_connectivity; + ice_config.ice_check_interval_weak_connectivity = + config.ice_check_interval_weak_connectivity; + ice_config.ice_check_min_interval = config.ice_check_min_interval; + ice_config.ice_unwritable_timeout = config.ice_unwritable_timeout; + ice_config.ice_unwritable_min_checks = config.ice_unwritable_min_checks; + ice_config.ice_inactive_timeout = config.ice_inactive_timeout; + ice_config.stun_keepalive_interval = config.stun_candidate_keepalive_interval; + ice_config.regather_all_networks_interval_range = + config.ice_regather_interval_range; + ice_config.network_preference = config.network_preference; + return ice_config; } absl::optional TgPeerConnection::sctp_transport_name() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (sctp_mid_ && transport_controller_) { - auto dtls_transport = transport_controller_->GetDtlsTransport(*sctp_mid_); - if (dtls_transport) { - return dtls_transport->transport_name(); + RTC_DCHECK_RUN_ON(signaling_thread()); + if (sctp_mid_ && transport_controller_) { + auto dtls_transport = transport_controller_->GetDtlsTransport(*sctp_mid_); + if (dtls_transport) { + return dtls_transport->transport_name(); + } + return absl::optional(); } return absl::optional(); - } - return absl::optional(); } cricket::CandidateStatsList TgPeerConnection::GetPooledCandidateStats() const { - cricket::CandidateStatsList candidate_states_list; - network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&cricket::PortAllocator::GetCandidateStatsFromPooledSessions, - port_allocator_.get(), &candidate_states_list)); - return candidate_states_list; + cricket::CandidateStatsList candidate_states_list; + network_thread()->Invoke( + RTC_FROM_HERE, + rtc::Bind(&cricket::PortAllocator::GetCandidateStatsFromPooledSessions, + port_allocator_.get(), &candidate_states_list)); + return candidate_states_list; } std::map TgPeerConnection::GetTransportNamesByMid() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - std::map transport_names_by_mid; - for (const auto& transceiver : transceivers_) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel) { - transport_names_by_mid[channel->content_name()] = - channel->transport_name(); +const { + RTC_DCHECK_RUN_ON(signaling_thread()); + std::map transport_names_by_mid; + for (const auto& transceiver : transceivers_) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel) { + transport_names_by_mid[channel->content_name()] = + channel->transport_name(); + } } - } - return transport_names_by_mid; + return transport_names_by_mid; } std::map TgPeerConnection::GetTransportStatsByNames( - const std::set& transport_names) { - if (!network_thread()->IsCurrent()) { - return network_thread() + const std::set& transport_names) { + if (!network_thread()->IsCurrent()) { + return network_thread() ->Invoke>( - RTC_FROM_HERE, - [&] { return GetTransportStatsByNames(transport_names); }); - } - RTC_DCHECK_RUN_ON(network_thread()); - std::map transport_stats_by_name; - for (const std::string& transport_name : transport_names) { - cricket::TransportStats transport_stats; - bool success = - transport_controller_->GetStats(transport_name, &transport_stats); - if (success) { - transport_stats_by_name[transport_name] = std::move(transport_stats); - } else { - RTC_LOG(LS_ERROR) << "Failed to get transport stats for transport_name=" - << transport_name; + RTC_FROM_HERE, + [&] { return GetTransportStatsByNames(transport_names); }); } - } - return transport_stats_by_name; + RTC_DCHECK_RUN_ON(network_thread()); + std::map transport_stats_by_name; + for (const std::string& transport_name : transport_names) { + cricket::TransportStats transport_stats; + bool success = + transport_controller_->GetStats(transport_name, &transport_stats); + if (success) { + transport_stats_by_name[transport_name] = std::move(transport_stats); + } else { + RTC_LOG(LS_ERROR) << "Failed to get transport stats for transport_name=" + << transport_name; + } + } + return transport_stats_by_name; } bool TgPeerConnection::GetLocalCertificate( - const std::string& transport_name, - rtc::scoped_refptr* certificate) { - if (!certificate) { - return false; - } - *certificate = transport_controller_->GetLocalCertificate(transport_name); - return *certificate != nullptr; + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + if (!certificate) { + return false; + } + *certificate = transport_controller_->GetLocalCertificate(transport_name); + return *certificate != nullptr; } std::unique_ptr TgPeerConnection::GetRemoteSSLCertChain( - const std::string& transport_name) { - return transport_controller_->GetRemoteSSLCertChain(transport_name); + const std::string& transport_name) { + return transport_controller_->GetRemoteSSLCertChain(transport_name); } bool TgPeerConnection::IceRestartPending(const std::string& content_name) const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_ice_restarts_.find(content_name) != - pending_ice_restarts_.end(); + RTC_DCHECK_RUN_ON(signaling_thread()); + return pending_ice_restarts_.find(content_name) != + pending_ice_restarts_.end(); } bool TgPeerConnection::NeedsIceRestart(const std::string& content_name) const { - return transport_controller_->NeedsIceRestart(content_name); + return transport_controller_->NeedsIceRestart(content_name); } void TgPeerConnection::OnCertificateReady( - const rtc::scoped_refptr& certificate) { - transport_controller_->SetLocalCertificate(certificate); + const rtc::scoped_refptr& certificate) { + transport_controller_->SetLocalCertificate(certificate); } void TgPeerConnection::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) { - SetSessionError(SessionError::kTransport, - rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); + SetSessionError(SessionError::kTransport, + rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); } void TgPeerConnection::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: - RTC_LOG(LS_INFO) << "Changing to ICE connected state because " - "all transports are writable."; - SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); - NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); - break; - case cricket::kIceConnectionCompleted: - RTC_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); - NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); - ReportTransportStats(); - break; - default: - RTC_NOTREACHED(); - } + 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: + RTC_LOG(LS_INFO) << "Changing to ICE connected state because " + "all transports are writable."; + SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); + NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); + break; + case cricket::kIceConnectionCompleted: + RTC_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); + NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); + ReportTransportStats(); + break; + default: + RTC_NOTREACHED(); + } } void TgPeerConnection::OnTransportControllerCandidatesGathered( - const std::string& transport_name, - const cricket::Candidates& candidates) { - int sdp_mline_index; - if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { - RTC_LOG(LS_ERROR) + const std::string& transport_name, + const cricket::Candidates& candidates) { + int sdp_mline_index; + if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { + RTC_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()); + 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)); } - OnIceCandidate(std::move(candidate)); - } } void TgPeerConnection::OnTransportControllerCandidateError( - const cricket::IceCandidateErrorEvent& event) { - OnIceCandidateError(event.address, event.port, event.url, event.error_code, - event.error_text); + const cricket::IceCandidateErrorEvent& event) { + OnIceCandidateError(event.address, event.port, event.url, event.error_code, + event.error_text); } void TgPeerConnection::OnTransportControllerCandidatesRemoved( - const std::vector& candidates) { - // Sanity check. - for (const cricket::Candidate& candidate : candidates) { - if (candidate.transport_name().empty()) { - RTC_LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " - "empty content name in candidate " - << candidate.ToString(); - return; + const std::vector& candidates) { + // Sanity check. + for (const cricket::Candidate& candidate : candidates) { + if (candidate.transport_name().empty()) { + RTC_LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " + "empty content name in candidate " + << candidate.ToString(); + return; + } } - } - - if (local_description()) { - mutable_local_description()->RemoveCandidates(candidates); - } - OnIceCandidatesRemoved(candidates); + + if (local_description()) { + mutable_local_description()->RemoveCandidates(candidates); + } + OnIceCandidatesRemoved(candidates); } void TgPeerConnection::OnTransportControllerCandidateChanged( - const cricket::CandidatePairChangeEvent& event) { - OnSelectedCandidatePairChanged(event); + const cricket::CandidatePairChangeEvent& event) { + OnSelectedCandidatePairChanged(event); } void TgPeerConnection::OnTransportControllerDtlsHandshakeError( - rtc::SSLHandshakeError error) { - RTC_HISTOGRAM_ENUMERATION( - "WebRTC.TgPeerConnection.DtlsHandshakeError", static_cast(error), - static_cast(rtc::SSLHandshakeError::MAX_VALUE)); + rtc::SSLHandshakeError error) { + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.TgPeerConnection.DtlsHandshakeError", static_cast(error), + static_cast(rtc::SSLHandshakeError::MAX_VALUE)); } void TgPeerConnection::EnableSending() { - for (const auto& transceiver : transceivers_) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && !channel->enabled()) { - channel->Enable(true); + for (const auto& transceiver : transceivers_) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel && !channel->enabled()) { + channel->Enable(true); + } } - } } // Returns the media index for a local ice candidate given the content name. bool TgPeerConnection::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; + const std::string& content_name, + int* sdp_mline_index) { + if (!local_description() || !sdp_mline_index) { + return false; } - } - return content_found; + + 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 TgPeerConnection::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) { - RTC_LOG(LS_INFO) - << "UseCandidatesInSessionDescription: Not ready to use " - "candidate."; - } - continue; - } - ret = UseCandidate(candidate); - if (!ret) { - break; - } + const SessionDescriptionInterface* remote_desc) { + if (!remote_desc) { + return true; } - } - return ret; + 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) { + RTC_LOG(LS_INFO) + << "UseCandidatesInSessionDescription: Not ready to use " + "candidate."; + } + continue; + } + ret = UseCandidate(candidate); + if (!ret) { + break; + } + } + } + return ret; } bool TgPeerConnection::UseCandidate(const IceCandidateInterface* candidate) { - RTCErrorOr result = - FindContentInfo(remote_description(), candidate); - if (!result.ok()) { - RTC_LOG(LS_ERROR) << "UseCandidate: Invalid candidate. " - << result.error().message(); - return false; - } - std::vector candidates; - candidates.push_back(candidate->candidate()); - // Invoking BaseSession method to handle remote candidates. - RTCError error = transport_controller_->AddRemoteCandidates( - result.value()->name, candidates); - if (error.ok()) { - ReportRemoteIceCandidateAdded(candidate->candidate()); - // 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); + RTCErrorOr result = + FindContentInfo(remote_description(), candidate); + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "UseCandidate: Invalid candidate. " + << result.error().message(); + return false; } - // TODO(bemasc): If state is Completed, go back to Connected. - } else { - RTC_LOG(LS_WARNING) << error.message(); - } - return true; + std::vector candidates; + candidates.push_back(candidate->candidate()); + // Invoking BaseSession method to handle remote candidates. + RTCError error = transport_controller_->AddRemoteCandidates( + result.value()->name, candidates); + if (error.ok()) { + ReportRemoteIceCandidateAdded(candidate->candidate()); + // 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 { + RTC_LOG(LS_WARNING) << error.message(); + } + return true; } RTCErrorOr TgPeerConnection::FindContentInfo( - const SessionDescriptionInterface* description, - const IceCandidateInterface* candidate) { - if (candidate->sdp_mline_index() >= 0) { - size_t mediacontent_index = + const SessionDescriptionInterface* description, + const IceCandidateInterface* candidate) { + if (candidate->sdp_mline_index() >= 0) { + size_t mediacontent_index = static_cast(candidate->sdp_mline_index()); - size_t content_size = description->description()->contents().size(); - if (mediacontent_index < content_size) { - return &description->description()->contents()[mediacontent_index]; - } else { - return RTCError(RTCErrorType::INVALID_RANGE, - "Media line index (" + - rtc::ToString(candidate->sdp_mline_index()) + - ") out of range (number of mlines: " + - rtc::ToString(content_size) + ")."); - } - } else if (!candidate->sdp_mid().empty()) { - auto& contents = description->description()->contents(); - auto it = absl::c_find_if( - contents, [candidate](const cricket::ContentInfo& content_info) { - return content_info.mid() == candidate->sdp_mid(); + size_t content_size = description->description()->contents().size(); + if (mediacontent_index < content_size) { + return &description->description()->contents()[mediacontent_index]; + } else { + return RTCError(RTCErrorType::INVALID_RANGE, + "Media line index (" + + rtc::ToString(candidate->sdp_mline_index()) + + ") out of range (number of mlines: " + + rtc::ToString(content_size) + ")."); + } + } else if (!candidate->sdp_mid().empty()) { + auto& contents = description->description()->contents(); + auto it = absl::c_find_if( + contents, [candidate](const cricket::ContentInfo& content_info) { + return content_info.mid() == candidate->sdp_mid(); }); - if (it == contents.end()) { - return RTCError( - RTCErrorType::INVALID_PARAMETER, - "Mid " + candidate->sdp_mid() + - " specified but no media section with that mid found."); - } else { - return &*it; + if (it == contents.end()) { + return RTCError( + RTCErrorType::INVALID_PARAMETER, + "Mid " + candidate->sdp_mid() + + " specified but no media section with that mid found."); + } else { + return &*it; + } } - } - - return RTCError(RTCErrorType::INVALID_PARAMETER, - "Neither sdp_mline_index nor sdp_mid specified."); + + return RTCError(RTCErrorType::INVALID_PARAMETER, + "Neither sdp_mline_index nor sdp_mid specified."); } void TgPeerConnection::RemoveUnusedChannels(const SessionDescription* desc) { - // 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) { - DestroyTransceiverChannel(GetVideoTransceiver()); - } - - const cricket::ContentInfo* audio_info = cricket::GetFirstAudioContent(desc); - if (!audio_info || audio_info->rejected) { - DestroyTransceiverChannel(GetAudioTransceiver()); - } + // 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) { + DestroyTransceiverChannel(GetVideoTransceiver()); + } + + const cricket::ContentInfo* audio_info = cricket::GetFirstAudioContent(desc); + if (!audio_info || audio_info->rejected) { + DestroyTransceiverChannel(GetAudioTransceiver()); + } } RTCErrorOr TgPeerConnection::GetEarlyBundleGroup( - const SessionDescription& desc) const { - const cricket::ContentGroup* bundle_group = nullptr; - if (configuration_.bundle_policy == - PeerConnectionInterface::kBundlePolicyMaxBundle) { - bundle_group = desc.GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - if (!bundle_group) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max-bundle configured but session description " - "has no BUNDLE group"); + const SessionDescription& desc) const { + const cricket::ContentGroup* bundle_group = nullptr; + if (configuration_.bundle_policy == + PeerConnectionInterface::kBundlePolicyMaxBundle) { + bundle_group = desc.GetGroupByName(cricket::GROUP_TYPE_BUNDLE); + if (!bundle_group) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "max-bundle configured but session description " + "has no BUNDLE group"); + } } - } - return bundle_group; + return bundle_group; } RTCError TgPeerConnection::CreateChannels(const SessionDescription& desc) { - // Creating the media channels. Transports should already have been created - // at this point. - const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc); - if (voice && !voice->rejected && - !GetAudioTransceiver()->internal()->channel()) { - cricket::VoiceChannel* voice_channel = CreateVoiceChannel(voice->name); - if (!voice_channel) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create voice channel."); + // Creating the media channels. Transports should already have been created + // at this point. + const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc); + if (voice && !voice->rejected && + !GetAudioTransceiver()->internal()->channel()) { + cricket::VoiceChannel* voice_channel = CreateVoiceChannel(voice->name); + if (!voice_channel) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Failed to create voice channel."); + } + GetAudioTransceiver()->internal()->SetChannel(voice_channel); } - GetAudioTransceiver()->internal()->SetChannel(voice_channel); - } - - const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc); - if (video && !video->rejected && - !GetVideoTransceiver()->internal()->channel()) { - cricket::VideoChannel* video_channel = CreateVideoChannel(video->name); - if (!video_channel) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create video channel."); + + const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc); + if (video && !video->rejected && + !GetVideoTransceiver()->internal()->channel()) { + cricket::VideoChannel* video_channel = CreateVideoChannel(video->name); + if (!video_channel) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Failed to create video channel."); + } + GetVideoTransceiver()->internal()->SetChannel(video_channel); } - GetVideoTransceiver()->internal()->SetChannel(video_channel); - } - - return RTCError::OK(); + + return RTCError::OK(); } // TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. cricket::VoiceChannel* TgPeerConnection::CreateVoiceChannel( - const std::string& mid) { - RtpTransportInternal* rtp_transport = GetRtpTransport(mid); - MediaTransportConfig media_transport_config = - transport_controller_->GetMediaTransportConfig(mid); - - cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel( - call_ptr_, configuration_.media_config, rtp_transport, - media_transport_config, signaling_thread(), mid, SrtpRequired(), - GetCryptoOptions(), &ssrc_generator_, audio_options_); - if (!voice_channel) { - return nullptr; - } - voice_channel->SignalDtlsSrtpSetupFailure.connect( - this, &TgPeerConnection::OnDtlsSrtpSetupFailure); - voice_channel->SignalSentPacket.connect(this, - &TgPeerConnection::OnSentPacket_w); - voice_channel->SetRtpTransport(rtp_transport); - - return voice_channel; + const std::string& mid) { + RtpTransportInternal* rtp_transport = GetRtpTransport(mid); + MediaTransportConfig media_transport_config = + transport_controller_->GetMediaTransportConfig(mid); + + cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel( + call_ptr_, configuration_.media_config, rtp_transport, + media_transport_config, signaling_thread(), mid, SrtpRequired(), + GetCryptoOptions(), &ssrc_generator_, audio_options_); + if (!voice_channel) { + return nullptr; + } + voice_channel->SignalDtlsSrtpSetupFailure.connect( + this, &TgPeerConnection::OnDtlsSrtpSetupFailure); + voice_channel->SignalSentPacket.connect(this, + &TgPeerConnection::OnSentPacket_w); + voice_channel->SetRtpTransport(rtp_transport); + + return voice_channel; } // TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. cricket::VideoChannel* TgPeerConnection::CreateVideoChannel( - const std::string& mid) { - RtpTransportInternal* rtp_transport = GetRtpTransport(mid); - MediaTransportConfig media_transport_config = - transport_controller_->GetMediaTransportConfig(mid); - - cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel( - call_ptr_, configuration_.media_config, rtp_transport, - media_transport_config, signaling_thread(), mid, SrtpRequired(), - GetCryptoOptions(), &ssrc_generator_, video_options_, - video_bitrate_allocator_factory_.get()); - if (!video_channel) { - return nullptr; - } - video_channel->SignalDtlsSrtpSetupFailure.connect( - this, &TgPeerConnection::OnDtlsSrtpSetupFailure); - video_channel->SignalSentPacket.connect(this, - &TgPeerConnection::OnSentPacket_w); - video_channel->SetRtpTransport(rtp_transport); - - return video_channel; + const std::string& mid) { + RtpTransportInternal* rtp_transport = GetRtpTransport(mid); + MediaTransportConfig media_transport_config = + transport_controller_->GetMediaTransportConfig(mid); + + cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel( + call_ptr_, configuration_.media_config, rtp_transport, + media_transport_config, signaling_thread(), mid, SrtpRequired(), + GetCryptoOptions(), &ssrc_generator_, video_options_, + video_bitrate_allocator_factory_.get()); + if (!video_channel) { + return nullptr; + } + video_channel->SignalDtlsSrtpSetupFailure.connect( + this, &TgPeerConnection::OnDtlsSrtpSetupFailure); + video_channel->SignalSentPacket.connect(this, + &TgPeerConnection::OnSentPacket_w); + video_channel->SetRtpTransport(rtp_transport); + + return video_channel; } Call::Stats TgPeerConnection::GetCallStats() { - if (!worker_thread()->IsCurrent()) { - return worker_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::GetCallStats, this)); - } - RTC_DCHECK_RUN_ON(worker_thread()); - if (call_) { - return call_->GetStats(); - } else { - return Call::Stats(); - } + if (!worker_thread()->IsCurrent()) { + return worker_thread()->Invoke( + RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::GetCallStats, this)); + } + RTC_DCHECK_RUN_ON(worker_thread()); + if (call_) { + return call_->GetStats(); + } else { + return Call::Stats(); + } } // Returns false if bundle is enabled and rtcp_mux is disabled. bool TgPeerConnection::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 == MediaProtocolType::kRtp) { - if (!HasRtcpMuxEnabled(content)) - return false; + 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 == MediaProtocolType::kRtp) { + if (!HasRtcpMuxEnabled(content)) + return false; + } } - } - // RTCP-MUX is enabled in all the contents. - return true; + // RTCP-MUX is enabled in all the contents. + return true; } bool TgPeerConnection::HasRtcpMuxEnabled(const cricket::ContentInfo* content) { - return content->media_description()->rtcp_mux(); + return content->media_description()->rtcp_mux(); } static RTCError ValidateMids(const cricket::SessionDescription& description) { - std::set mids; - for (const cricket::ContentInfo& content : description.contents()) { - if (content.name.empty()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "A media section is missing a MID attribute."); + std::set mids; + for (const cricket::ContentInfo& content : description.contents()) { + if (content.name.empty()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "A media section is missing a MID attribute."); + } + if (!mids.insert(content.name).second) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Duplicate a=mid value '" + content.name + "'."); + } } - if (!mids.insert(content.name).second) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Duplicate a=mid value '" + content.name + "'."); - } - } - return RTCError::OK(); + return RTCError::OK(); } RTCError TgPeerConnection::ValidateSessionDescription( - const SessionDescriptionInterface* sdesc, - cricket::ContentSource source) { - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } - - if (!sdesc || !sdesc->description()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); - } - - SdpType type = sdesc->GetType(); - if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) || - (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_STATE, - "Called in wrong state: " + GetSignalingStateString(signaling_state())); - } - - RTCError error = ValidateMids(*sdesc->description()); - if (!error.ok()) { - return error; - } - - // Verify crypto settings. - std::string crypto_error; - if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || - dtls_enabled_) { - RTCError crypto_error = VerifyCrypto(sdesc->description(), dtls_enabled_); - if (!crypto_error.ok()) { - return crypto_error; + const SessionDescriptionInterface* sdesc, + cricket::ContentSource source) { + if (session_error() != SessionError::kNone) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); } - } - - // Verify ice-ufrag and ice-pwd. - if (!VerifyIceUfragPwdPresent(sdesc->description())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kSdpWithoutIceUfragPwd); - } - - if (!ValidateBundleSettings(sdesc->description())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kBundleWithoutRtcpMux); - } - - // 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 (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // With an answer we want to compare the new answer session description with - // the offer's session description from the current negotiation. - const cricket::SessionDescription* offer_desc = - (source == cricket::CS_LOCAL) ? remote_description()->description() - : local_description()->description(); - if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) || - !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(), - type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInAnswer); + + if (!sdesc || !sdesc->description()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); } - } else { - // The re-offers should respect the order of m= sections in current - // description. See RFC3264 Section 8 paragraph 4 for more details. - // With a re-offer, either the current local or current remote descriptions - // could be the most up to date, so we would like to check against both of - // them if they exist. It could be the case that one of them has a 0 port - // for a media section, but the other does not. This is important to check - // against in the case that we are recycling an m= section. - const cricket::SessionDescription* current_desc = nullptr; - const cricket::SessionDescription* secondary_current_desc = nullptr; - if (local_description()) { - current_desc = local_description()->description(); - if (remote_description()) { - secondary_current_desc = remote_description()->description(); - } - } else if (remote_description()) { - current_desc = remote_description()->description(); + + SdpType type = sdesc->GetType(); + if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) || + (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_STATE, + "Called in wrong state: " + GetSignalingStateString(signaling_state())); } - if (current_desc && - !MediaSectionsInSameOrder(*current_desc, secondary_current_desc, - *sdesc->description(), type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInSubsequentOffer); + + RTCError error = ValidateMids(*sdesc->description()); + if (!error.ok()) { + return error; } - } - - if (IsUnifiedPlan()) { - // Ensure that each audio and video media section has at most one - // "StreamParams". This will return an error if receiving a session - // description from a "Plan B" endpoint which adds multiple tracks of the - // same type. With Unified Plan, there can only be at most one track per - // media section. - for (const ContentInfo& content : sdesc->description()->contents()) { - const MediaContentDescription& desc = *content.media_description(); - if ((desc.type() == cricket::MEDIA_TYPE_AUDIO || - desc.type() == cricket::MEDIA_TYPE_VIDEO) && - desc.streams().size() > 1u) { + + // Verify crypto settings. + std::string crypto_error; + if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || + dtls_enabled_) { + RTCError crypto_error = VerifyCrypto(sdesc->description(), dtls_enabled_); + if (!crypto_error.ok()) { + return crypto_error; + } + } + + // Verify ice-ufrag and ice-pwd. + if (!VerifyIceUfragPwdPresent(sdesc->description())) { LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Media section has more than one track specified " - "with a=ssrc lines which is not supported with " - "Unified Plan."); - } + kSdpWithoutIceUfragPwd); } - } - - return RTCError::OK(); + + if (!ValidateBundleSettings(sdesc->description())) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + kBundleWithoutRtcpMux); + } + + // 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 (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + // With an answer we want to compare the new answer session description with + // the offer's session description from the current negotiation. + const cricket::SessionDescription* offer_desc = + (source == cricket::CS_LOCAL) ? remote_description()->description() + : local_description()->description(); + if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) || + !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(), + type)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + kMlineMismatchInAnswer); + } + } else { + // The re-offers should respect the order of m= sections in current + // description. See RFC3264 Section 8 paragraph 4 for more details. + // With a re-offer, either the current local or current remote descriptions + // could be the most up to date, so we would like to check against both of + // them if they exist. It could be the case that one of them has a 0 port + // for a media section, but the other does not. This is important to check + // against in the case that we are recycling an m= section. + const cricket::SessionDescription* current_desc = nullptr; + const cricket::SessionDescription* secondary_current_desc = nullptr; + if (local_description()) { + current_desc = local_description()->description(); + if (remote_description()) { + secondary_current_desc = remote_description()->description(); + } + } else if (remote_description()) { + current_desc = remote_description()->description(); + } + if (current_desc && + !MediaSectionsInSameOrder(*current_desc, secondary_current_desc, + *sdesc->description(), type)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + kMlineMismatchInSubsequentOffer); + } + } + + if (IsUnifiedPlan()) { + // Ensure that each audio and video media section has at most one + // "StreamParams". This will return an error if receiving a session + // description from a "Plan B" endpoint which adds multiple tracks of the + // same type. With Unified Plan, there can only be at most one track per + // media section. + for (const ContentInfo& content : sdesc->description()->contents()) { + const MediaContentDescription& desc = *content.media_description(); + if ((desc.type() == cricket::MEDIA_TYPE_AUDIO || + desc.type() == cricket::MEDIA_TYPE_VIDEO) && + desc.streams().size() > 1u) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Media section has more than one track specified " + "with a=ssrc lines which is not supported with " + "Unified Plan."); + } + } + } + + return RTCError::OK(); } bool TgPeerConnection::ExpectSetLocalDescription(SdpType type) { - PeerConnectionInterface::SignalingState state = signaling_state(); - if (type == SdpType::kOffer) { - return (state == PeerConnectionInterface::kStable) || - (state == PeerConnectionInterface::kHaveLocalOffer); - } else { - RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); - return (state == PeerConnectionInterface::kHaveRemoteOffer) || - (state == PeerConnectionInterface::kHaveLocalPrAnswer); - } + PeerConnectionInterface::SignalingState state = signaling_state(); + if (type == SdpType::kOffer) { + return (state == PeerConnectionInterface::kStable) || + (state == PeerConnectionInterface::kHaveLocalOffer); + } else { + RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); + return (state == PeerConnectionInterface::kHaveRemoteOffer) || + (state == PeerConnectionInterface::kHaveLocalPrAnswer); + } } bool TgPeerConnection::ExpectSetRemoteDescription(SdpType type) { - PeerConnectionInterface::SignalingState state = signaling_state(); - if (type == SdpType::kOffer) { - return (state == PeerConnectionInterface::kStable) || - (state == PeerConnectionInterface::kHaveRemoteOffer); - } else { - RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); - return (state == PeerConnectionInterface::kHaveLocalOffer) || - (state == PeerConnectionInterface::kHaveRemotePrAnswer); - } + PeerConnectionInterface::SignalingState state = signaling_state(); + if (type == SdpType::kOffer) { + return (state == PeerConnectionInterface::kStable) || + (state == PeerConnectionInterface::kHaveRemoteOffer); + } else { + RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); + return (state == PeerConnectionInterface::kHaveLocalOffer) || + (state == PeerConnectionInterface::kHaveRemotePrAnswer); + } } const char* TgPeerConnection::SessionErrorToString(SessionError error) const { - switch (error) { - case SessionError::kNone: - return "ERROR_NONE"; - case SessionError::kContent: - return "ERROR_CONTENT"; - case SessionError::kTransport: - return "ERROR_TRANSPORT"; - } - RTC_NOTREACHED(); - return ""; + switch (error) { + case SessionError::kNone: + return "ERROR_NONE"; + case SessionError::kContent: + return "ERROR_CONTENT"; + case SessionError::kTransport: + return "ERROR_TRANSPORT"; + } + RTC_NOTREACHED(); + return ""; } std::string TgPeerConnection::GetSessionErrorMsg() { - rtc::StringBuilder desc; - desc << kSessionError << SessionErrorToString(session_error()) << ". "; - desc << kSessionErrorDesc << session_error_desc() << "."; - return desc.Release(); + rtc::StringBuilder desc; + desc << kSessionError << SessionErrorToString(session_error()) << ". "; + desc << kSessionErrorDesc << session_error_desc() << "."; + return desc.Release(); } void TgPeerConnection::ReportSdpFormatReceived( - const SessionDescriptionInterface& remote_offer) { - int num_audio_mlines = 0; - int num_video_mlines = 0; - int num_audio_tracks = 0; - int num_video_tracks = 0; - for (const ContentInfo& content : remote_offer.description()->contents()) { - cricket::MediaType media_type = content.media_description()->type(); - int num_tracks = std::max( - 1, static_cast(content.media_description()->streams().size())); - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - num_audio_mlines += 1; - num_audio_tracks += num_tracks; - } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - num_video_mlines += 1; - num_video_tracks += num_tracks; + const SessionDescriptionInterface& remote_offer) { + int num_audio_mlines = 0; + int num_video_mlines = 0; + int num_audio_tracks = 0; + int num_video_tracks = 0; + for (const ContentInfo& content : remote_offer.description()->contents()) { + cricket::MediaType media_type = content.media_description()->type(); + int num_tracks = std::max( + 1, static_cast(content.media_description()->streams().size())); + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + num_audio_mlines += 1; + num_audio_tracks += num_tracks; + } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { + num_video_mlines += 1; + num_video_tracks += num_tracks; + } } - } - SdpFormatReceived format = kSdpFormatReceivedNoTracks; - if (num_audio_mlines > 1 || num_video_mlines > 1) { - format = kSdpFormatReceivedComplexUnifiedPlan; - } else if (num_audio_tracks > 1 || num_video_tracks > 1) { - format = kSdpFormatReceivedComplexPlanB; - } else if (num_audio_tracks > 0 || num_video_tracks > 0) { - format = kSdpFormatReceivedSimple; - } - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpFormatReceived", format, - kSdpFormatReceivedMax); + SdpFormatReceived format = kSdpFormatReceivedNoTracks; + if (num_audio_mlines > 1 || num_video_mlines > 1) { + format = kSdpFormatReceivedComplexUnifiedPlan; + } else if (num_audio_tracks > 0 || num_video_tracks > 0) { + format = kSdpFormatReceivedSimple; + } + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpFormatReceived", format, + kSdpFormatReceivedMax); } void TgPeerConnection::ReportIceCandidateCollected( - const cricket::Candidate& candidate) { - NoteUsageEvent(UsageEvent::CANDIDATE_COLLECTED); - if (candidate.address().IsPrivateIP()) { - NoteUsageEvent(UsageEvent::PRIVATE_CANDIDATE_COLLECTED); - } - if (candidate.address().IsUnresolvedIP()) { - NoteUsageEvent(UsageEvent::MDNS_CANDIDATE_COLLECTED); - } - if (candidate.address().family() == AF_INET6) { - NoteUsageEvent(UsageEvent::IPV6_CANDIDATE_COLLECTED); - } + const cricket::Candidate& candidate) { + NoteUsageEvent(UsageEvent::CANDIDATE_COLLECTED); + if (candidate.address().IsPrivateIP()) { + NoteUsageEvent(UsageEvent::PRIVATE_CANDIDATE_COLLECTED); + } + if (candidate.address().IsUnresolvedIP()) { + NoteUsageEvent(UsageEvent::MDNS_CANDIDATE_COLLECTED); + } + if (candidate.address().family() == AF_INET6) { + NoteUsageEvent(UsageEvent::IPV6_CANDIDATE_COLLECTED); + } } void TgPeerConnection::ReportRemoteIceCandidateAdded( - const cricket::Candidate& candidate) { - NoteUsageEvent(UsageEvent::REMOTE_CANDIDATE_ADDED); - if (candidate.address().IsPrivateIP()) { - NoteUsageEvent(UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED); - } - if (candidate.address().IsUnresolvedIP()) { - NoteUsageEvent(UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED); - } - if (candidate.address().family() == AF_INET6) { - NoteUsageEvent(UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED); - } + const cricket::Candidate& candidate) { + NoteUsageEvent(UsageEvent::REMOTE_CANDIDATE_ADDED); + if (candidate.address().IsPrivateIP()) { + NoteUsageEvent(UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED); + } + if (candidate.address().IsUnresolvedIP()) { + NoteUsageEvent(UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED); + } + if (candidate.address().family() == AF_INET6) { + NoteUsageEvent(UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED); + } } void TgPeerConnection::NoteUsageEvent(UsageEvent event) { - RTC_DCHECK_RUN_ON(signaling_thread()); - usage_event_accumulator_ |= static_cast(event); + RTC_DCHECK_RUN_ON(signaling_thread()); + usage_event_accumulator_ |= static_cast(event); } void TgPeerConnection::ReportUsagePattern() const { - RTC_DLOG(LS_INFO) << "Usage signature is " << usage_event_accumulator_; - RTC_HISTOGRAM_ENUMERATION_SPARSE("WebRTC.TgPeerConnection.UsagePattern", - usage_event_accumulator_, - static_cast(UsageEvent::MAX_VALUE)); - const int bad_bits = - static_cast(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED) | - static_cast(UsageEvent::CANDIDATE_COLLECTED); - const int good_bits = - static_cast(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED) | - static_cast(UsageEvent::REMOTE_CANDIDATE_ADDED) | - static_cast(UsageEvent::ICE_STATE_CONNECTED); - if ((usage_event_accumulator_ & bad_bits) == bad_bits && - (usage_event_accumulator_ & good_bits) == 0) { - // If called after close(), we can't report, because observer may have - // been deallocated, and therefore pointer is null. Write to log instead. - if (observer_) { - Observer()->OnInterestingUsage(usage_event_accumulator_); - } else { - RTC_LOG(LS_INFO) << "Interesting usage signature " - << usage_event_accumulator_ - << " observed after observer shutdown"; + RTC_DLOG(LS_INFO) << "Usage signature is " << usage_event_accumulator_; + RTC_HISTOGRAM_ENUMERATION_SPARSE("WebRTC.TgPeerConnection.UsagePattern", + usage_event_accumulator_, + static_cast(UsageEvent::MAX_VALUE)); + const int bad_bits = + static_cast(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED) | + static_cast(UsageEvent::CANDIDATE_COLLECTED); + const int good_bits = + static_cast(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED) | + static_cast(UsageEvent::REMOTE_CANDIDATE_ADDED) | + static_cast(UsageEvent::ICE_STATE_CONNECTED); + if ((usage_event_accumulator_ & bad_bits) == bad_bits && + (usage_event_accumulator_ & good_bits) == 0) { + // If called after close(), we can't report, because observer may have + // been deallocated, and therefore pointer is null. Write to log instead. + if (observer_) { + Observer()->OnInterestingUsage(usage_event_accumulator_); + } else { + RTC_LOG(LS_INFO) << "Interesting usage signature " + << usage_event_accumulator_ + << " observed after observer shutdown"; + } } - } } void TgPeerConnection::ReportNegotiatedSdpSemantics( - const SessionDescriptionInterface& answer) { - SdpSemanticNegotiated semantics_negotiated; - switch (answer.description()->msid_signaling()) { - case 0: - semantics_negotiated = kSdpSemanticNegotiatedNone; - break; - case cricket::kMsidSignalingMediaSection: - semantics_negotiated = kSdpSemanticNegotiatedUnifiedPlan; - break; - case cricket::kMsidSignalingSsrcAttribute: - semantics_negotiated = kSdpSemanticNegotiatedPlanB; - break; - case cricket::kMsidSignalingMediaSection | - cricket::kMsidSignalingSsrcAttribute: - semantics_negotiated = kSdpSemanticNegotiatedMixed; - break; - default: - RTC_NOTREACHED(); - } - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpSemanticNegotiated", - semantics_negotiated, kSdpSemanticNegotiatedMax); + const SessionDescriptionInterface& answer) { + SdpSemanticNegotiated semantics_negotiated; + switch (answer.description()->msid_signaling()) { + case 0: + semantics_negotiated = kSdpSemanticNegotiatedNone; + break; + case cricket::kMsidSignalingMediaSection: + semantics_negotiated = kSdpSemanticNegotiatedUnifiedPlan; + break; + case cricket::kMsidSignalingSsrcAttribute: + semantics_negotiated = kSdpSemanticNegotiatedPlanB; + break; + case cricket::kMsidSignalingMediaSection | + cricket::kMsidSignalingSsrcAttribute: + semantics_negotiated = kSdpSemanticNegotiatedMixed; + break; + default: + RTC_NOTREACHED(); + } + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpSemanticNegotiated", + semantics_negotiated, kSdpSemanticNegotiatedMax); } // We need to check the local/remote description for the Transport instead of @@ -6680,554 +6302,542 @@ void TgPeerConnection::ReportNegotiatedSdpSemantics( // Not doing so may trigger the auto generation of transport description and // mess up DTLS identity information, ICE credential, etc. bool TgPeerConnection::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; - } - - RTCErrorOr result = - FindContentInfo(current_remote_desc, candidate); - if (!result.ok()) { - RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. " - << result.error().message(); - - *valid = false; - return false; - } - - std::string transport_name = GetTransportName(result.value()->name); - return !transport_name.empty(); + 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; + } + + RTCErrorOr result = + FindContentInfo(current_remote_desc, candidate); + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. " + << result.error().message(); + + *valid = false; + return false; + } + + std::string transport_name = GetTransportName(result.value()->name); + return !transport_name.empty(); } bool TgPeerConnection::SrtpRequired() const { - return !use_datagram_transport_ && - (dtls_enabled_ || - webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED); + return !use_datagram_transport_ && + (dtls_enabled_ || + webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED); } void TgPeerConnection::OnTransportControllerGatheringState( - cricket::IceGatheringState state) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - if (state == cricket::kIceGatheringGathering) { - OnIceGatheringChange(PeerConnectionInterface::kIceGatheringGathering); - } else if (state == cricket::kIceGatheringComplete) { - OnIceGatheringChange(PeerConnectionInterface::kIceGatheringComplete); - } + cricket::IceGatheringState state) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + if (state == cricket::kIceGatheringGathering) { + OnIceGatheringChange(PeerConnectionInterface::kIceGatheringGathering); + } else if (state == cricket::kIceGatheringComplete) { + OnIceGatheringChange(PeerConnectionInterface::kIceGatheringComplete); + } } void TgPeerConnection::ReportTransportStats() { - std::map> - media_types_by_transport_name; - for (const auto& transceiver : transceivers_) { - if (transceiver->internal()->channel()) { - const std::string& transport_name = - transceiver->internal()->channel()->transport_name(); - media_types_by_transport_name[transport_name].insert( - transceiver->media_type()); + std::map> + media_types_by_transport_name; + for (const auto& transceiver : transceivers_) { + if (transceiver->internal()->channel()) { + const std::string& transport_name = + transceiver->internal()->channel()->transport_name(); + media_types_by_transport_name[transport_name].insert( + transceiver->media_type()); + } } - } - if (rtp_data_channel()) { - media_types_by_transport_name[rtp_data_channel()->transport_name()].insert( - cricket::MEDIA_TYPE_DATA); - } - - absl::optional transport_name = sctp_transport_name(); - if (transport_name) { - media_types_by_transport_name[*transport_name].insert( - cricket::MEDIA_TYPE_DATA); - } - - for (const auto& entry : media_types_by_transport_name) { - const std::string& transport_name = entry.first; - const std::set media_types = entry.second; - cricket::TransportStats stats; - if (transport_controller_->GetStats(transport_name, &stats)) { - ReportBestConnectionState(stats); - ReportNegotiatedCiphers(stats, media_types); + if (rtp_data_channel()) { + media_types_by_transport_name[rtp_data_channel()->transport_name()].insert( + cricket::MEDIA_TYPE_DATA); + } + + absl::optional transport_name = sctp_transport_name(); + if (transport_name) { + media_types_by_transport_name[*transport_name].insert( + cricket::MEDIA_TYPE_DATA); + } + + for (const auto& entry : media_types_by_transport_name) { + const std::string& transport_name = entry.first; + const std::set media_types = entry.second; + cricket::TransportStats stats; + if (transport_controller_->GetStats(transport_name, &stats)) { + ReportBestConnectionState(stats); + ReportNegotiatedCiphers(stats, media_types); + } } - } } // Walk through the ConnectionInfos to gather best connection usage // for IPv4 and IPv6. void TgPeerConnection::ReportBestConnectionState( - const cricket::TransportStats& stats) { - for (const cricket::TransportChannelStats& channel_stats : - stats.channel_stats) { - for (const cricket::ConnectionInfo& connection_info : - channel_stats.ice_transport_stats.connection_infos) { - if (!connection_info.best_connection) { - continue; - } - - const cricket::Candidate& local = connection_info.local_candidate; - const cricket::Candidate& remote = connection_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)) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_TCP", - GetIceCandidatePairCounter(local, remote), - kIceCandidatePairMax); - } else if (local.protocol() == cricket::UDP_PROTOCOL_NAME) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_UDP", - GetIceCandidatePairCounter(local, remote), - kIceCandidatePairMax); - } else { - RTC_CHECK(0); - } - - // Increment the counter for IP type. - if (local.address().family() == AF_INET) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", - kBestConnections_IPv4, - kPeerConnectionAddressFamilyCounter_Max); - } else if (local.address().family() == AF_INET6) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", - kBestConnections_IPv6, - kPeerConnectionAddressFamilyCounter_Max); - } else { - RTC_CHECK(!local.address().hostname().empty() && - local.address().IsUnresolvedIP()); - } - - return; + const cricket::TransportStats& stats) { + for (const cricket::TransportChannelStats& channel_stats : + stats.channel_stats) { + for (const cricket::ConnectionInfo& connection_info : + channel_stats.ice_transport_stats.connection_infos) { + if (!connection_info.best_connection) { + continue; + } + + const cricket::Candidate& local = connection_info.local_candidate; + const cricket::Candidate& remote = connection_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)) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_TCP", + GetIceCandidatePairCounter(local, remote), + kIceCandidatePairMax); + } else if (local.protocol() == cricket::UDP_PROTOCOL_NAME) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_UDP", + GetIceCandidatePairCounter(local, remote), + kIceCandidatePairMax); + } else { + RTC_CHECK(0); + } + + // Increment the counter for IP type. + if (local.address().family() == AF_INET) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", + kBestConnections_IPv4, + kPeerConnectionAddressFamilyCounter_Max); + } else if (local.address().family() == AF_INET6) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", + kBestConnections_IPv6, + kPeerConnectionAddressFamilyCounter_Max); + } else { + RTC_CHECK(!local.address().hostname().empty() && + local.address().IsUnresolvedIP()); + } + + return; + } } - } } void TgPeerConnection::ReportNegotiatedCiphers( - const cricket::TransportStats& stats, - const std::set& media_types) { - 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; - } - - if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE) { - for (cricket::MediaType media_type : media_types) { - switch (media_type) { - case cricket::MEDIA_TYPE_AUDIO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SrtpCryptoSuite.Audio", srtp_crypto_suite, - rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_VIDEO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SrtpCryptoSuite.Video", srtp_crypto_suite, - rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_DATA: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SrtpCryptoSuite.Data", srtp_crypto_suite, - rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); - break; - default: - RTC_NOTREACHED(); - continue; - } + const cricket::TransportStats& stats, + const std::set& media_types) { + if (!dtls_enabled_ || stats.channel_stats.empty()) { + return; } - } - - if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL) { - for (cricket::MediaType media_type : media_types) { - switch (media_type) { - case cricket::MEDIA_TYPE_AUDIO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SslCipherSuite.Audio", ssl_cipher_suite, - rtc::SSL_CIPHER_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_VIDEO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SslCipherSuite.Video", ssl_cipher_suite, - rtc::SSL_CIPHER_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_DATA: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SslCipherSuite.Data", ssl_cipher_suite, - rtc::SSL_CIPHER_SUITE_MAX_VALUE); - break; - default: - RTC_NOTREACHED(); - continue; - } + + 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; + } + + if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE) { + for (cricket::MediaType media_type : media_types) { + switch (media_type) { + case cricket::MEDIA_TYPE_AUDIO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SrtpCryptoSuite.Audio", srtp_crypto_suite, + rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_VIDEO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SrtpCryptoSuite.Video", srtp_crypto_suite, + rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_DATA: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SrtpCryptoSuite.Data", srtp_crypto_suite, + rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); + break; + default: + RTC_NOTREACHED(); + continue; + } + } + } + + if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL) { + for (cricket::MediaType media_type : media_types) { + switch (media_type) { + case cricket::MEDIA_TYPE_AUDIO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SslCipherSuite.Audio", ssl_cipher_suite, + rtc::SSL_CIPHER_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_VIDEO: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SslCipherSuite.Video", ssl_cipher_suite, + rtc::SSL_CIPHER_SUITE_MAX_VALUE); + break; + case cricket::MEDIA_TYPE_DATA: + RTC_HISTOGRAM_ENUMERATION_SPARSE( + "WebRTC.PeerConnection.SslCipherSuite.Data", ssl_cipher_suite, + rtc::SSL_CIPHER_SUITE_MAX_VALUE); + break; + default: + RTC_NOTREACHED(); + continue; + } + } } - } } void TgPeerConnection::OnSentPacket_w(const rtc::SentPacket& sent_packet) { - RTC_DCHECK_RUN_ON(worker_thread()); - RTC_DCHECK(call_); - call_->OnSentPacket(sent_packet); + RTC_DCHECK_RUN_ON(worker_thread()); + RTC_DCHECK(call_); + call_->OnSentPacket(sent_packet); } const std::string TgPeerConnection::GetTransportName( - const std::string& content_name) { - cricket::ChannelInterface* channel = GetChannel(content_name); - if (channel) { - return channel->transport_name(); - } - // Return an empty string if failed to retrieve the transport name. - return ""; + const std::string& content_name) { + cricket::ChannelInterface* channel = GetChannel(content_name); + if (channel) { + return channel->transport_name(); + } + // Return an empty string if failed to retrieve the transport name. + return ""; } void TgPeerConnection::DestroyTransceiverChannel( - rtc::scoped_refptr> - transceiver) { - RTC_DCHECK(transceiver); - - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel) { - transceiver->internal()->SetChannel(nullptr); - DestroyChannelInterface(channel); - } + rtc::scoped_refptr> + transceiver) { + RTC_DCHECK(transceiver); + + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel) { + transceiver->internal()->SetChannel(nullptr); + DestroyChannelInterface(channel); + } } void TgPeerConnection::DestroyChannelInterface( - cricket::ChannelInterface* channel) { - RTC_DCHECK(channel); - switch (channel->media_type()) { - case cricket::MEDIA_TYPE_AUDIO: - channel_manager()->DestroyVoiceChannel( - static_cast(channel)); - break; - case cricket::MEDIA_TYPE_VIDEO: - channel_manager()->DestroyVideoChannel( - static_cast(channel)); - break; - case cricket::MEDIA_TYPE_DATA: - channel_manager()->DestroyRtpDataChannel( - static_cast(channel)); - break; - default: - RTC_NOTREACHED() << "Unknown media type: " << channel->media_type(); - break; - } + cricket::ChannelInterface* channel) { + RTC_DCHECK(channel); + switch (channel->media_type()) { + case cricket::MEDIA_TYPE_AUDIO: + channel_manager()->DestroyVoiceChannel( + static_cast(channel)); + break; + case cricket::MEDIA_TYPE_VIDEO: + channel_manager()->DestroyVideoChannel( + static_cast(channel)); + break; + case cricket::MEDIA_TYPE_DATA: + channel_manager()->DestroyRtpDataChannel( + static_cast(channel)); + break; + default: + RTC_NOTREACHED() << "Unknown media type: " << channel->media_type(); + break; + } } bool TgPeerConnection::OnTransportChanged( - const std::string& mid, - RtpTransportInternal* rtp_transport, - rtc::scoped_refptr dtls_transport, - DataChannelTransportInterface* data_channel_transport) { - RTC_DCHECK_RUN_ON(network_thread()); - bool ret = true; - auto base_channel = GetChannel(mid); - if (base_channel) { - ret = base_channel->SetRtpTransport(rtp_transport); - } - return ret; + const std::string& mid, + RtpTransportInternal* rtp_transport, + rtc::scoped_refptr dtls_transport, + DataChannelTransportInterface* data_channel_transport) { + RTC_DCHECK_RUN_ON(network_thread()); + bool ret = true; + auto base_channel = GetChannel(mid); + if (base_channel) { + ret = base_channel->SetRtpTransport(rtp_transport); + } + return ret; } void TgPeerConnection::OnSetStreams() { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (IsUnifiedPlan()) - UpdateNegotiationNeeded(); + RTC_DCHECK_RUN_ON(signaling_thread()); + if (IsUnifiedPlan()) + UpdateNegotiationNeeded(); } PeerConnectionObserver* TgPeerConnection::Observer() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(observer_); - return observer_; + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(observer_); + return observer_; } CryptoOptions TgPeerConnection::GetCryptoOptions() { - // TODO(bugs.webrtc.org/9891) - Remove PeerConnectionFactory::CryptoOptions - // after it has been removed. - return configuration_.crypto_options.has_value() - ? *configuration_.crypto_options - : factory_->options().crypto_options; + // TODO(bugs.webrtc.org/9891) - Remove PeerConnectionFactory::CryptoOptions + // after it has been removed. + return configuration_.crypto_options.has_value() + ? *configuration_.crypto_options + : factory_->options().crypto_options; } void TgPeerConnection::ClearStatsCache() { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (stats_collector_) { - stats_collector_->ClearCachedStatsReport(); - } } void TgPeerConnection::RequestUsagePatternReportForTesting() { - signaling_thread()->Post(RTC_FROM_HERE, this, MSG_REPORT_USAGE_PATTERN, - nullptr); + signaling_thread()->Post(RTC_FROM_HERE, this, MSG_REPORT_USAGE_PATTERN, + nullptr); } void TgPeerConnection::UpdateNegotiationNeeded() { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!IsUnifiedPlan()) { + RTC_DCHECK_RUN_ON(signaling_thread()); + if (!IsUnifiedPlan()) { + Observer()->OnRenegotiationNeeded(); + return; + } + + // If connection's [[IsClosed]] slot is true, abort these steps. + if (IsClosed()) + return; + + // If connection's signaling state is not "stable", abort these steps. + if (signaling_state() != PeerConnectionInterface::kStable) + return; + + // NOTE + // The negotiation-needed flag will be updated once the state transitions to + // "stable", as part of the steps for setting an RTCSessionDescription. + + // If the result of checking if negotiation is needed is false, clear the + // negotiation-needed flag by setting connection's [[NegotiationNeeded]] slot + // to false, and abort these steps. + bool is_negotiation_needed = CheckIfNegotiationIsNeeded(); + if (!is_negotiation_needed) { + is_negotiation_needed_ = false; + return; + } + + // If connection's [[NegotiationNeeded]] slot is already true, abort these + // steps. + if (is_negotiation_needed_) + return; + + // Set connection's [[NegotiationNeeded]] slot to true. + is_negotiation_needed_ = true; + + // Queue a task that runs the following steps: + // If connection's [[IsClosed]] slot is true, abort these steps. + // If connection's [[NegotiationNeeded]] slot is false, abort these steps. + // Fire an event named negotiationneeded at connection. Observer()->OnRenegotiationNeeded(); - return; - } - - // If connection's [[IsClosed]] slot is true, abort these steps. - if (IsClosed()) - return; - - // If connection's signaling state is not "stable", abort these steps. - if (signaling_state() != kStable) - return; - - // NOTE - // The negotiation-needed flag will be updated once the state transitions to - // "stable", as part of the steps for setting an RTCSessionDescription. - - // If the result of checking if negotiation is needed is false, clear the - // negotiation-needed flag by setting connection's [[NegotiationNeeded]] slot - // to false, and abort these steps. - bool is_negotiation_needed = CheckIfNegotiationIsNeeded(); - if (!is_negotiation_needed) { - is_negotiation_needed_ = false; - return; - } - - // If connection's [[NegotiationNeeded]] slot is already true, abort these - // steps. - if (is_negotiation_needed_) - return; - - // Set connection's [[NegotiationNeeded]] slot to true. - is_negotiation_needed_ = true; - - // Queue a task that runs the following steps: - // If connection's [[IsClosed]] slot is true, abort these steps. - // If connection's [[NegotiationNeeded]] slot is false, abort these steps. - // Fire an event named negotiationneeded at connection. - Observer()->OnRenegotiationNeeded(); } bool TgPeerConnection::CheckIfNegotiationIsNeeded() { - RTC_DCHECK_RUN_ON(signaling_thread()); - // 1. If any implementation-specific negotiation is required, as described at - // the start of this section, return true. - - // 2. If connection's [[RestartIce]] internal slot is true, return true. - if (local_ice_credentials_to_replace_->HasIceCredentials()) { - return true; - } - - // 3. Let description be connection.[[CurrentLocalDescription]]. - const SessionDescriptionInterface* description = current_local_description(); - if (!description) - return true; - - // 5. For each transceiver in connection's set of transceivers, perform the - // following checks: - for (const auto& transceiver : transceivers_) { - const ContentInfo* current_local_msection = + RTC_DCHECK_RUN_ON(signaling_thread()); + // 1. If any implementation-specific negotiation is required, as described at + // the start of this section, return true. + + // 2. If connection's [[RestartIce]] internal slot is true, return true. + if (local_ice_credentials_to_replace_->HasIceCredentials()) { + return true; + } + + // 3. Let description be connection.[[CurrentLocalDescription]]. + const SessionDescriptionInterface* description = current_local_description(); + if (!description) + return true; + + // 5. For each transceiver in connection's set of transceivers, perform the + // following checks: + for (const auto& transceiver : transceivers_) { + const ContentInfo* current_local_msection = FindTransceiverMSection(transceiver.get(), description); - - const ContentInfo* current_remote_msection = FindTransceiverMSection( - transceiver.get(), current_remote_description()); - - // 5.3 If transceiver is stopped and is associated with an m= section, - // but the associated m= section is not yet rejected in - // connection.[[CurrentLocalDescription]] or - // connection.[[CurrentRemoteDescription]], return true. - if (transceiver->stopped()) { - if (current_local_msection && !current_local_msection->rejected && - ((current_remote_msection && !current_remote_msection->rejected) || - !current_remote_msection)) { - return true; - } - continue; - } - - // 5.1 If transceiver isn't stopped and isn't yet associated with an m= - // section in description, return true. - if (!current_local_msection) - return true; - - const MediaContentDescription* current_local_media_description = + + const ContentInfo* current_remote_msection = FindTransceiverMSection( + transceiver.get(), current_remote_description()); + + // 5.3 If transceiver is stopped and is associated with an m= section, + // but the associated m= section is not yet rejected in + // connection.[[CurrentLocalDescription]] or + // connection.[[CurrentRemoteDescription]], return true. + if (transceiver->stopped()) { + if (current_local_msection && !current_local_msection->rejected && + ((current_remote_msection && !current_remote_msection->rejected) || + !current_remote_msection)) { + return true; + } + continue; + } + + // 5.1 If transceiver isn't stopped and isn't yet associated with an m= + // section in description, return true. + if (!current_local_msection) + return true; + + const MediaContentDescription* current_local_media_description = current_local_msection->media_description(); - // 5.2 If transceiver isn't stopped and is associated with an m= section - // in description then perform the following checks: - - // 5.2.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the - // associated m= section in description either doesn't contain a single - // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this - // m= section, or the MSID values themselves, differ from what is in - // transceiver.sender.[[AssociatedMediaStreamIds]], return true. - if (RtpTransceiverDirectionHasSend(transceiver->direction())) { - if (current_local_media_description->streams().size() == 0) - return true; - - std::vector msection_msids; - for (const auto& stream : current_local_media_description->streams()) { - for (const std::string& msid : stream.stream_ids()) - msection_msids.push_back(msid); - } - - std::vector transceiver_msids = - transceiver->sender()->stream_ids(); - if (msection_msids.size() != transceiver_msids.size()) - return true; - - absl::c_sort(transceiver_msids); - absl::c_sort(msection_msids); - if (transceiver_msids != msection_msids) - return true; + // 5.2 If transceiver isn't stopped and is associated with an m= section + // in description then perform the following checks: + + // 5.2.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the + // associated m= section in description either doesn't contain a single + // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this + // m= section, or the MSID values themselves, differ from what is in + // transceiver.sender.[[AssociatedMediaStreamIds]], return true. + if (RtpTransceiverDirectionHasSend(transceiver->direction())) { + if (current_local_media_description->streams().size() == 0) + return true; + + std::vector msection_msids; + for (const auto& stream : current_local_media_description->streams()) { + for (const std::string& msid : stream.stream_ids()) + msection_msids.push_back(msid); + } + + std::vector transceiver_msids = + transceiver->sender()->stream_ids(); + if (msection_msids.size() != transceiver_msids.size()) + return true; + + absl::c_sort(transceiver_msids); + absl::c_sort(msection_msids); + if (transceiver_msids != msection_msids) + return true; + } + + // 5.2.2 If description is of type "offer", and the direction of the + // associated m= section in neither connection.[[CurrentLocalDescription]] + // nor connection.[[CurrentRemoteDescription]] matches + // transceiver.[[Direction]], return true. + if (description->GetType() == SdpType::kOffer) { + if (!current_remote_description()) + return true; + + if (!current_remote_msection) + return true; + + RtpTransceiverDirection current_local_direction = + current_local_media_description->direction(); + RtpTransceiverDirection current_remote_direction = + current_remote_msection->media_description()->direction(); + if (transceiver->direction() != current_local_direction && + transceiver->direction() != + RtpTransceiverDirectionReversed(current_remote_direction)) { + return true; + } + } + + // 5.2.3 If description is of type "answer", and the direction of the + // associated m= section in the description does not match + // transceiver.[[Direction]] intersected with the offered direction (as + // described in [JSEP] (section 5.3.1.)), return true. + if (description->GetType() == SdpType::kAnswer) { + if (!remote_description()) + return true; + + const ContentInfo* offered_remote_msection = + FindTransceiverMSection(transceiver.get(), remote_description()); + + RtpTransceiverDirection offered_direction = + offered_remote_msection + ? offered_remote_msection->media_description()->direction() + : RtpTransceiverDirection::kInactive; + + if (current_local_media_description->direction() != + (RtpTransceiverDirectionIntersection( + transceiver->direction(), + RtpTransceiverDirectionReversed(offered_direction)))) { + return true; + } + } } - - // 5.2.2 If description is of type "offer", and the direction of the - // associated m= section in neither connection.[[CurrentLocalDescription]] - // nor connection.[[CurrentRemoteDescription]] matches - // transceiver.[[Direction]], return true. - if (description->GetType() == SdpType::kOffer) { - if (!current_remote_description()) - return true; - - if (!current_remote_msection) - return true; - - RtpTransceiverDirection current_local_direction = - current_local_media_description->direction(); - RtpTransceiverDirection current_remote_direction = - current_remote_msection->media_description()->direction(); - if (transceiver->direction() != current_local_direction && - transceiver->direction() != - RtpTransceiverDirectionReversed(current_remote_direction)) { - return true; - } - } - - // 5.2.3 If description is of type "answer", and the direction of the - // associated m= section in the description does not match - // transceiver.[[Direction]] intersected with the offered direction (as - // described in [JSEP] (section 5.3.1.)), return true. - if (description->GetType() == SdpType::kAnswer) { - if (!remote_description()) - return true; - - const ContentInfo* offered_remote_msection = - FindTransceiverMSection(transceiver.get(), remote_description()); - - RtpTransceiverDirection offered_direction = - offered_remote_msection - ? offered_remote_msection->media_description()->direction() - : RtpTransceiverDirection::kInactive; - - if (current_local_media_description->direction() != - (RtpTransceiverDirectionIntersection( - transceiver->direction(), - RtpTransceiverDirectionReversed(offered_direction)))) { - return true; - } - } - } - - // If all the preceding checks were performed and true was not returned, - // nothing remains to be negotiated; return false. - return false; + + // If all the preceding checks were performed and true was not returned, + // nothing remains to be negotiated; return false. + return false; } RTCError TgPeerConnection::Rollback(SdpType sdp_type) { - auto state = signaling_state(); - if (state != PeerConnectionInterface::kHaveLocalOffer && - state != PeerConnectionInterface::kHaveRemoteOffer) { - return RTCError(RTCErrorType::INVALID_STATE, - "Called in wrong signalingState: " + + auto state = signaling_state(); + if (state != PeerConnectionInterface::kHaveLocalOffer && + state != PeerConnectionInterface::kHaveRemoteOffer) { + return RTCError(RTCErrorType::INVALID_STATE, + "Called in wrong signalingState: " + GetSignalingStateString(signaling_state())); - } - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(IsUnifiedPlan()); - std::vector mids; - std::vector> all_added_streams; - std::vector> all_removed_streams; - std::vector> removed_receivers; - - for (auto&& transceivers_stable_state_pair : - transceiver_stable_states_by_transceivers_) { - auto transceiver = transceivers_stable_state_pair.first; - auto state = transceivers_stable_state_pair.second; - - if (state.remote_stream_ids()) { - std::vector> added_streams; - std::vector> removed_streams; - SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), - state.remote_stream_ids().value(), - &added_streams, &removed_streams); - all_added_streams.insert(all_added_streams.end(), added_streams.begin(), - added_streams.end()); - all_removed_streams.insert(all_removed_streams.end(), - removed_streams.begin(), - removed_streams.end()); - if (!state.has_m_section() && !state.newly_created()) { - continue; - } } - - RTC_DCHECK(transceiver->internal()->mid().has_value()); - std::string mid = transceiver->internal()->mid().value(); - mids.push_back(mid); - DestroyTransceiverChannel(transceiver); - - if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer && - transceiver->receiver()) { - removed_receivers.push_back(transceiver->receiver()); - } - if (state.newly_created()) { - if (transceiver->internal()->reused_for_addtrack()) { - transceiver->internal()->set_created_by_addtrack(true); - } else { - int remaining_transceiver_count = 0; - for (auto&& t : transceivers_) { - if (t != transceiver) { - transceivers_[remaining_transceiver_count++] = t; - } + RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(IsUnifiedPlan()); + std::vector mids; + std::vector> all_added_streams; + std::vector> all_removed_streams; + std::vector> removed_receivers; + + for (auto&& transceivers_stable_state_pair : + transceiver_stable_states_by_transceivers_) { + auto transceiver = transceivers_stable_state_pair.first; + auto state = transceivers_stable_state_pair.second; + + if (state.remote_stream_ids()) { + std::vector> added_streams; + std::vector> removed_streams; + SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), + state.remote_stream_ids().value(), + &added_streams, &removed_streams); + all_added_streams.insert(all_added_streams.end(), added_streams.begin(), + added_streams.end()); + all_removed_streams.insert(all_removed_streams.end(), + removed_streams.begin(), + removed_streams.end()); + if (!state.has_m_section() && !state.newly_created()) { + continue; + } } - transceivers_.resize(remaining_transceiver_count); - } + + RTC_DCHECK(transceiver->internal()->mid().has_value()); + std::string mid = transceiver->internal()->mid().value(); + mids.push_back(mid); + DestroyTransceiverChannel(transceiver); + + if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer && + transceiver->receiver()) { + removed_receivers.push_back(transceiver->receiver()); + } + if (state.newly_created()) { + if (transceiver->internal()->reused_for_addtrack()) { + transceiver->internal()->set_created_by_addtrack(true); + } else { + int remaining_transceiver_count = 0; + for (auto&& t : transceivers_) { + if (t != transceiver) { + transceivers_[remaining_transceiver_count++] = t; + } + } + transceivers_.resize(remaining_transceiver_count); + } + } + transceiver->internal()->sender_internal()->set_transport(nullptr); + transceiver->internal()->receiver_internal()->set_transport(nullptr); + transceiver->internal()->set_mid(state.mid()); + transceiver->internal()->set_mline_index(state.mline_index()); } - transceiver->internal()->sender_internal()->set_transport(nullptr); - transceiver->internal()->receiver_internal()->set_transport(nullptr); - transceiver->internal()->set_mid(state.mid()); - transceiver->internal()->set_mline_index(state.mline_index()); - } - transport_controller_->RollbackTransportForMids(mids); - transceiver_stable_states_by_transceivers_.clear(); - pending_local_description_.reset(); - pending_remote_description_.reset(); - ChangeSignalingState(PeerConnectionInterface::kStable); - - // Once all processing has finished, fire off callbacks. - for (const auto& receiver : removed_receivers) { - Observer()->OnRemoveTrack(receiver); - } - for (const auto& stream : all_added_streams) { - Observer()->OnAddStream(stream); - } - for (const auto& stream : all_removed_streams) { - Observer()->OnRemoveStream(stream); - } - - // The assumption is that in case of implicit rollback UpdateNegotiationNeeded - // gets called in SetRemoteDescription. - if (sdp_type == SdpType::kRollback) { - UpdateNegotiationNeeded(); - if (is_negotiation_needed_) { - Observer()->OnRenegotiationNeeded(); + transport_controller_->RollbackTransportForMids(mids); + transceiver_stable_states_by_transceivers_.clear(); + pending_local_description_.reset(); + pending_remote_description_.reset(); + ChangeSignalingState(PeerConnectionInterface::kStable); + + // Once all processing has finished, fire off callbacks. + for (const auto& receiver : removed_receivers) { + Observer()->OnRemoveTrack(receiver); + } + for (const auto& stream : all_added_streams) { + Observer()->OnAddStream(stream); + } + for (const auto& stream : all_removed_streams) { + Observer()->OnRemoveStream(stream); + } + + // The assumption is that in case of implicit rollback UpdateNegotiationNeeded + // gets called in SetRemoteDescription. + if (sdp_type == SdpType::kRollback) { + UpdateNegotiationNeeded(); + if (is_negotiation_needed_) { + Observer()->OnRenegotiationNeeded(); + } } - } - return RTCError::OK(); -} - -rtc::scoped_refptr TgPeerConnection::CreateDataChannel(const std::string& label, const DataChannelInit* config) { - return nullptr; -} - -RTCError TgPeerConnection::UpdateDataChannel(cricket::ContentSource source, const cricket::ContentInfo& content, const cricket::ContentGroup* bundle_group) { return RTCError::OK(); } diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h index 0b4b0a1c3d..ff658e84c1 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h @@ -31,7 +31,7 @@ #include "pc/sctp_transport.h" #include "pc/stats_collector.h" #include "pc/stream_collection.h" -#include "pc/webrtc_session_description_factory.h" +#include "tg_webrtc_session_description_factory.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/operations_chain.h" #include "rtc_base/race_checker.h" @@ -40,6 +40,8 @@ #include "tg_jsep_transport_controller.h" #include "tg_peer_connection_factory.h" +#include "tg_webrtc_session_description_factory.h" +#include "tg_rtp_sender.h" namespace webrtc { @@ -61,11 +63,11 @@ class RtcEventLog; // - Generating offers and answers based on the current state. // - The ICE state machine. // - Generating stats. -class TgPeerConnection : public PeerConnectionInternal, - public TgJsepTransportController::Observer, - public RtpSenderBase::SetStreamsObserver, - public rtc::MessageHandler, - public sigslot::has_slots<> { +class TgPeerConnection : public rtc::RefCountInterface, +public TgJsepTransportController::Observer, +public TgRtpSenderBase::SetStreamsObserver, +public rtc::MessageHandler, +public sigslot::has_slots<> { public: // A bit in the usage pattern is registered when its defining event occurs at // least once. @@ -122,28 +124,28 @@ class TgPeerConnection : public PeerConnectionInternal, const PeerConnectionInterface::RTCConfiguration& configuration, PeerConnectionDependencies dependencies); - rtc::scoped_refptr local_streams() override; - rtc::scoped_refptr remote_streams() override; - bool AddStream(MediaStreamInterface* local_stream) override; - void RemoveStream(MediaStreamInterface* local_stream) override; + rtc::scoped_refptr local_streams(); + rtc::scoped_refptr remote_streams(); + bool AddStream(MediaStreamInterface* local_stream); + void RemoveStream(MediaStreamInterface* local_stream); RTCErrorOr> AddTrack( rtc::scoped_refptr track, - const std::vector& stream_ids) override; - bool RemoveTrack(RtpSenderInterface* sender) override; + const std::vector& stream_ids); + bool RemoveTrack(RtpSenderInterface* sender); RTCError RemoveTrackNew( - rtc::scoped_refptr sender) override; + rtc::scoped_refptr sender); RTCErrorOr> AddTransceiver( - rtc::scoped_refptr track) override; + rtc::scoped_refptr track); RTCErrorOr> AddTransceiver( rtc::scoped_refptr track, - const RtpTransceiverInit& init) override; + const RtpTransceiverInit& init); RTCErrorOr> AddTransceiver( - cricket::MediaType media_type) override; + cricket::MediaType media_type); RTCErrorOr> AddTransceiver( cricket::MediaType media_type, - const RtpTransceiverInit& init) override; + const RtpTransceiverInit& init); // Gets the DTLS SSL certificate associated with the audio transport on the // remote side. This will become populated once the DTLS connection with the @@ -159,154 +161,153 @@ class TgPeerConnection : public PeerConnectionInternal, rtc::scoped_refptr CreateSender( const std::string& kind, - const std::string& stream_id) override; + const std::string& stream_id); std::vector> GetSenders() - const override; + const; std::vector> GetReceivers() - const override; + const; std::vector> GetTransceivers() - const override; + const; rtc::scoped_refptr CreateDataChannel( const std::string& label, - const DataChannelInit* config) override; + const DataChannelInit* config); // WARNING: LEGACY. See peerconnectioninterface.h bool GetStats(StatsObserver* observer, webrtc::MediaStreamTrackInterface* track, - StatsOutputLevel level) override; + PeerConnectionInterface::StatsOutputLevel level); // Spec-complaint GetStats(). See peerconnectioninterface.h - void GetStats(RTCStatsCollectorCallback* callback) override; + void GetStats(RTCStatsCollectorCallback* callback); void GetStats( rtc::scoped_refptr selector, - rtc::scoped_refptr callback) override; + rtc::scoped_refptr callback); void GetStats( rtc::scoped_refptr selector, - rtc::scoped_refptr callback) override; - void ClearStatsCache() override; + rtc::scoped_refptr callback); + void ClearStatsCache(); - SignalingState signaling_state() override; + PeerConnectionInterface::SignalingState signaling_state(); - IceConnectionState ice_connection_state() override; - IceConnectionState standardized_ice_connection_state() override; - PeerConnectionState peer_connection_state() override; - IceGatheringState ice_gathering_state() override; + PeerConnectionInterface::IceConnectionState ice_connection_state(); + PeerConnectionInterface::IceConnectionState standardized_ice_connection_state(); + PeerConnectionInterface::PeerConnectionState peer_connection_state(); + PeerConnectionInterface::IceGatheringState ice_gathering_state(); - const SessionDescriptionInterface* local_description() const override; - const SessionDescriptionInterface* remote_description() const override; - const SessionDescriptionInterface* current_local_description() const override; + const SessionDescriptionInterface* local_description() const; + const SessionDescriptionInterface* remote_description() const; + const SessionDescriptionInterface* current_local_description() const; const SessionDescriptionInterface* current_remote_description() - const override; - const SessionDescriptionInterface* pending_local_description() const override; + const; + const SessionDescriptionInterface* pending_local_description() const; const SessionDescriptionInterface* pending_remote_description() - const override; + const; - void RestartIce() override; + void RestartIce(); // JSEP01 void CreateOffer(CreateSessionDescriptionObserver* observer, - const RTCOfferAnswerOptions& options) override; + const PeerConnectionInterface::RTCOfferAnswerOptions& options); void CreateAnswer(CreateSessionDescriptionObserver* observer, - const RTCOfferAnswerOptions& options) override; + const PeerConnectionInterface::RTCOfferAnswerOptions& options); void SetLocalDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc) override; - void SetLocalDescription(SetSessionDescriptionObserver* observer) override; + SessionDescriptionInterface* desc); + void SetLocalDescription(SetSessionDescriptionObserver* observer); void SetRemoteDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc) override; + SessionDescriptionInterface* desc); void SetRemoteDescription( std::unique_ptr desc, - rtc::scoped_refptr observer) - override; - PeerConnectionInterface::RTCConfiguration GetConfiguration() override; + rtc::scoped_refptr observer); + PeerConnectionInterface::RTCConfiguration GetConfiguration(); RTCError SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& configuration) override; - bool AddIceCandidate(const IceCandidateInterface* candidate) override; + const PeerConnectionInterface::RTCConfiguration& configuration); + bool AddIceCandidate(const IceCandidateInterface* candidate); void AddIceCandidate(std::unique_ptr candidate, - std::function callback) override; + std::function callback); bool RemoveIceCandidates( - const std::vector& candidates) override; + const std::vector& candidates); - RTCError SetBitrate(const BitrateSettings& bitrate) override; + RTCError SetBitrate(const BitrateSettings& bitrate); - void SetAudioPlayout(bool playout) override; - void SetAudioRecording(bool recording) override; + void SetAudioPlayout(bool playout); + void SetAudioRecording(bool recording); rtc::scoped_refptr LookupDtlsTransportByMid( - const std::string& mid) override; + const std::string& mid); rtc::scoped_refptr LookupDtlsTransportByMidInternal( const std::string& mid); - rtc::scoped_refptr GetSctpTransport() const override; + rtc::scoped_refptr GetSctpTransport() const; bool StartRtcEventLog(std::unique_ptr output, - int64_t output_period_ms) override; - bool StartRtcEventLog(std::unique_ptr output) override; - void StopRtcEventLog() override; + int64_t output_period_ms); + bool StartRtcEventLog(std::unique_ptr output); + void StopRtcEventLog(); - void Close() override; + void Close(); // PeerConnectionInternal implementation. - rtc::Thread* network_thread() const final { + rtc::Thread* network_thread() const { return factory_->network_thread(); } - rtc::Thread* worker_thread() const final { return factory_->worker_thread(); } - rtc::Thread* signaling_thread() const final { + rtc::Thread* worker_thread() const { return factory_->worker_thread(); } + rtc::Thread* signaling_thread() const { return factory_->signaling_thread(); } - std::string session_id() const override { + std::string session_id() const { RTC_DCHECK_RUN_ON(signaling_thread()); return session_id_; } - bool initial_offerer() const override { + bool initial_offerer() const { RTC_DCHECK_RUN_ON(signaling_thread()); return transport_controller_ && transport_controller_->initial_offerer(); } std::vector< rtc::scoped_refptr>> - GetTransceiversInternal() const override { + GetTransceiversInternal() const { RTC_DCHECK_RUN_ON(signaling_thread()); return transceivers_; } - sigslot::signal1& SignalDataChannelCreated() override { + sigslot::signal1& SignalDataChannelCreated() { RTC_DCHECK_RUN_ON(signaling_thread()); return SignalDataChannelCreated_; } - cricket::RtpDataChannel* rtp_data_channel() const override { + cricket::RtpDataChannel* rtp_data_channel() const { return nullptr; } std::vector> sctp_data_channels() - const override { + const { RTC_DCHECK_RUN_ON(signaling_thread()); return std::vector>(); } - absl::optional sctp_content_name() const override { + absl::optional sctp_content_name() const { RTC_DCHECK_RUN_ON(signaling_thread()); return sctp_mid_; } - absl::optional sctp_transport_name() const override; + absl::optional sctp_transport_name() const; - cricket::CandidateStatsList GetPooledCandidateStats() const override; - std::map GetTransportNamesByMid() const override; + cricket::CandidateStatsList GetPooledCandidateStats() const; + std::map GetTransportNamesByMid() const; std::map GetTransportStatsByNames( - const std::set& transport_names) override; - Call::Stats GetCallStats() override; + const std::set& transport_names); + Call::Stats GetCallStats(); bool GetLocalCertificate( const std::string& transport_name, - rtc::scoped_refptr* certificate) override; + rtc::scoped_refptr* certificate); std::unique_ptr GetRemoteSSLCertChain( - const std::string& transport_name) override; - bool IceRestartPending(const std::string& content_name) const override; - bool NeedsIceRestart(const std::string& content_name) const override; - bool GetSslRole(const std::string& content_name, rtc::SSLRole* role) override; + const std::string& transport_name); + bool IceRestartPending(const std::string& content_name) const; + bool NeedsIceRestart(const std::string& content_name) const; + bool GetSslRole(const std::string& content_name, rtc::SSLRole* role); // Functions needed by DataChannelController void NoteDataAddedEvent() { NoteUsageEvent(UsageEvent::DATA_ADDED); } @@ -457,7 +458,7 @@ class TgPeerConnection : public PeerConnectionInternal, cricket::VideoMediaChannel* video_media_channel() const RTC_RUN_ON(signaling_thread()); - std::vector>> + std::vector>> GetSendersInternal() const RTC_RUN_ON(signaling_thread()); std::vector< rtc::scoped_refptr>> @@ -475,10 +476,10 @@ class TgPeerConnection : public PeerConnectionInternal, // onto the |operations_chain_| when the public CreateOffer(), CreateAnswer(), // SetLocalDescription() and SetRemoteDescription() methods are invoked. void DoCreateOffer( - const RTCOfferAnswerOptions& options, + const PeerConnectionInterface::RTCOfferAnswerOptions& options, rtc::scoped_refptr observer); void DoCreateAnswer( - const RTCOfferAnswerOptions& options, + const PeerConnectionInterface::RTCOfferAnswerOptions& options, rtc::scoped_refptr observer); void DoSetLocalDescription( std::unique_ptr desc, @@ -540,7 +541,7 @@ class TgPeerConnection : public PeerConnectionInternal, const RtpTransceiverInit& init, bool fire_callback = true) RTC_RUN_ON(signaling_thread()); - rtc::scoped_refptr> + rtc::scoped_refptr> CreateSender(cricket::MediaType media_type, const std::string& id, rtc::scoped_refptr track, @@ -554,11 +555,11 @@ class TgPeerConnection : public PeerConnectionInternal, // transceivers. rtc::scoped_refptr> CreateAndAddTransceiver( - rtc::scoped_refptr> sender, + rtc::scoped_refptr> sender, rtc::scoped_refptr> receiver) RTC_RUN_ON(signaling_thread()); - void SetIceConnectionState(IceConnectionState new_state) + void SetIceConnectionState(PeerConnectionInterface::IceConnectionState new_state) RTC_RUN_ON(signaling_thread()); void SetStandardizedIceConnectionState( PeerConnectionInterface::IceConnectionState new_state) @@ -568,7 +569,7 @@ class TgPeerConnection : public PeerConnectionInternal, RTC_RUN_ON(signaling_thread()); // Called any time the IceGatheringState changes. - void OnIceGatheringChange(IceGatheringState new_state) + void OnIceGatheringChange(PeerConnectionInterface::IceGatheringState new_state) RTC_RUN_ON(signaling_thread()); // New ICE candidate has been gathered. void OnIceCandidate(std::unique_ptr candidate) @@ -589,7 +590,7 @@ class TgPeerConnection : public PeerConnectionInternal, RTC_RUN_ON(signaling_thread()); // Update the state, signaling if necessary. - void ChangeSignalingState(SignalingState signaling_state) + void ChangeSignalingState(PeerConnectionInterface::SignalingState signaling_state) RTC_RUN_ON(signaling_thread()); // Signals from MediaStreamObserver. @@ -732,7 +733,7 @@ class TgPeerConnection : public PeerConnectionInternal, cricket::MediaSessionOptions* session_options) RTC_RUN_ON(signaling_thread()); - RTCError HandleLegacyOfferOptions(const RTCOfferAnswerOptions& options) + RTCError HandleLegacyOfferOptions(const PeerConnectionInterface::RTCOfferAnswerOptions& options) RTC_RUN_ON(signaling_thread()); void RemoveRecvDirectionFromReceivingTransceiversOfType( cricket::MediaType media_type) RTC_RUN_ON(signaling_thread()); @@ -744,7 +745,7 @@ class TgPeerConnection : public PeerConnectionInternal, // Returns a MediaSessionOptions struct with options decided by // |constraints|, the local MediaStreams and DataChannels. - void GetOptionsForAnswer(const RTCOfferAnswerOptions& offer_answer_options, + void GetOptionsForAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, cricket::MediaSessionOptions* session_options) RTC_RUN_ON(signaling_thread()); void GetOptionsForPlanBAnswer( @@ -854,12 +855,12 @@ class TgPeerConnection : public PeerConnectionInternal, RTC_RUN_ON(signaling_thread()); // Return the RtpSender with the given track attached. - rtc::scoped_refptr> + rtc::scoped_refptr> FindSenderForTrack(MediaStreamTrackInterface* track) const RTC_RUN_ON(signaling_thread()); // Return the RtpSender with the given id, or null if none exists. - rtc::scoped_refptr> + rtc::scoped_refptr> FindSenderById(const std::string& sender_id) const RTC_RUN_ON(signaling_thread()); @@ -888,13 +889,13 @@ class TgPeerConnection : public PeerConnectionInternal, InitializePortAllocatorResult InitializePortAllocator_n( const cricket::ServerAddresses& stun_servers, const std::vector& turn_servers, - const RTCConfiguration& configuration); + const PeerConnectionInterface::RTCConfiguration& configuration); // Called when SetConfiguration is called to apply the supported subset // of the configuration on the network thread. bool ReconfigurePortAllocator_n( const cricket::ServerAddresses& stun_servers, const std::vector& turn_servers, - IceTransportsType type, + PeerConnectionInterface::IceTransportsType type, int candidate_pool_size, PortPrunePolicy turn_port_prune_policy, webrtc::TurnCustomizer* turn_customizer, @@ -914,7 +915,7 @@ class TgPeerConnection : public PeerConnectionInternal, // or values that conflict with other parameters. // // Returns RTCError::OK() if there are no issues. - RTCError ValidateConfiguration(const RTCConfiguration& config) const; + RTCError ValidateConfiguration(const PeerConnectionInterface::RTCConfiguration& config) const; cricket::ChannelManager* channel_manager() const; @@ -1138,7 +1139,7 @@ class TgPeerConnection : public PeerConnectionInternal, rtc::scoped_refptr dtls_transport, DataChannelTransportInterface* data_channel_transport) override; - // RtpSenderBase::SetStreamsObserver override. + // TgRtpSenderBase::SetStreamsObserver override. void OnSetStreams() override; // Returns the CryptoOptions for this TgPeerConnection. This will always @@ -1185,16 +1186,16 @@ class TgPeerConnection : public PeerConnectionInternal, rtc::scoped_refptr operations_chain_ RTC_GUARDED_BY(signaling_thread()); - SignalingState signaling_state_ RTC_GUARDED_BY(signaling_thread()) = kStable; - IceConnectionState ice_connection_state_ RTC_GUARDED_BY(signaling_thread()) = - kIceConnectionNew; + PeerConnectionInterface::SignalingState signaling_state_ RTC_GUARDED_BY(signaling_thread()) = PeerConnectionInterface::kStable; + PeerConnectionInterface::IceConnectionState ice_connection_state_ RTC_GUARDED_BY(signaling_thread()) = + PeerConnectionInterface::kIceConnectionNew; PeerConnectionInterface::IceConnectionState standardized_ice_connection_state_ - RTC_GUARDED_BY(signaling_thread()) = kIceConnectionNew; + RTC_GUARDED_BY(signaling_thread()) = PeerConnectionInterface::kIceConnectionNew; PeerConnectionInterface::PeerConnectionState connection_state_ - RTC_GUARDED_BY(signaling_thread()) = PeerConnectionState::kNew; + RTC_GUARDED_BY(signaling_thread()) = PeerConnectionInterface::PeerConnectionState::kNew; - IceGatheringState ice_gathering_state_ RTC_GUARDED_BY(signaling_thread()) = - kIceGatheringNew; + PeerConnectionInterface::IceGatheringState ice_gathering_state_ RTC_GUARDED_BY(signaling_thread()) = + PeerConnectionInterface::kIceGatheringNew; PeerConnectionInterface::RTCConfiguration configuration_ RTC_GUARDED_BY(signaling_thread()); @@ -1270,10 +1271,6 @@ class TgPeerConnection : public PeerConnectionInternal, // pointer from any thread. Call* const call_ptr_; - std::unique_ptr stats_ - RTC_GUARDED_BY(signaling_thread()); // A pointer is passed to senders_ - rtc::scoped_refptr stats_collector_ - RTC_GUARDED_BY(signaling_thread()); // Holds changes made to transceivers during applying descriptors for // potential rollback. Gets cleared once signaling state goes to stable. std::map>, @@ -1338,7 +1335,7 @@ class TgPeerConnection : public PeerConnectionInternal, std::set pending_ice_restarts_ RTC_GUARDED_BY(signaling_thread()); - std::unique_ptr webrtc_session_desc_factory_ + std::unique_ptr webrtc_session_desc_factory_ RTC_GUARDED_BY(signaling_thread()); // Member variables for caching global options. diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp index 7cf9478b66..610e66f522 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp @@ -48,25 +48,6 @@ namespace webrtc { -rtc::scoped_refptr -CreateModularPeerConnectionFactory( - PeerConnectionFactoryDependencies dependencies) { - rtc::scoped_refptr pc_factory( - new rtc::RefCountedObject( - std::move(dependencies))); - // Call Initialize synchronously but make sure it is executed on - // |signaling_thread|. - MethodCall call( - pc_factory.get(), &TgPeerConnectionFactory::Initialize); - bool result = call.Marshal(RTC_FROM_HERE, pc_factory->signaling_thread()); - - if (!result) { - return nullptr; - } - return PeerConnectionFactoryProxy::Create(pc_factory->signaling_thread(), - pc_factory); -} - TgPeerConnectionFactory::TgPeerConnectionFactory( PeerConnectionFactoryDependencies dependencies) : wraps_current_thread_(false), @@ -153,7 +134,7 @@ bool TgPeerConnectionFactory::Initialize() { return true; } -void TgPeerConnectionFactory::SetOptions(const Options& options) { +void TgPeerConnectionFactory::SetOptions(const PeerConnectionFactory::Options& options) { options_ = options; } @@ -229,7 +210,7 @@ void TgPeerConnectionFactory::StopAecDump() { channel_manager_->StopAecDump(); } -rtc::scoped_refptr +rtc::scoped_refptr TgPeerConnectionFactory::CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, std::unique_ptr allocator, @@ -243,7 +224,7 @@ TgPeerConnectionFactory::CreatePeerConnection( return CreatePeerConnection(configuration, std::move(dependencies)); } -rtc::scoped_refptr +rtc::scoped_refptr TgPeerConnectionFactory::CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, PeerConnectionDependencies dependencies) { @@ -300,11 +281,10 @@ TgPeerConnectionFactory::CreatePeerConnection( rtc::scoped_refptr pc( new rtc::RefCountedObject(this, std::move(event_log), std::move(call))); - ActionsBeforeInitializeForTesting(pc); if (!pc->Initialize(configuration, std::move(dependencies))) { return nullptr; } - return PeerConnectionProxy::Create(signaling_thread(), pc); + return pc; } rtc::scoped_refptr diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h index a9765e1002..ac6129a861 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h @@ -32,45 +32,46 @@ class BasicPacketSocketFactory; namespace webrtc { class RtcEventLog; +class TgPeerConnection; -class TgPeerConnectionFactory : public PeerConnectionFactoryInterface { +class TgPeerConnectionFactory: public rtc::RefCountInterface { public: - void SetOptions(const Options& options) override; + void SetOptions(const PeerConnectionFactoryInterface::Options& options); - rtc::scoped_refptr CreatePeerConnection( + rtc::scoped_refptr CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, std::unique_ptr allocator, std::unique_ptr cert_generator, - PeerConnectionObserver* observer) override; + PeerConnectionObserver* observer); - rtc::scoped_refptr CreatePeerConnection( + rtc::scoped_refptr CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies) override; + PeerConnectionDependencies dependencies); bool Initialize(); RtpCapabilities GetRtpSenderCapabilities( - cricket::MediaType kind) const override; + cricket::MediaType kind) const; RtpCapabilities GetRtpReceiverCapabilities( - cricket::MediaType kind) const override; + cricket::MediaType kind) const; rtc::scoped_refptr CreateLocalMediaStream( - const std::string& stream_id) override; + const std::string& stream_id); rtc::scoped_refptr CreateAudioSource( - const cricket::AudioOptions& options) override; + const cricket::AudioOptions& options); rtc::scoped_refptr CreateVideoTrack( const std::string& id, - VideoTrackSourceInterface* video_source) override; + VideoTrackSourceInterface* video_source); rtc::scoped_refptr CreateAudioTrack( const std::string& id, - AudioSourceInterface* audio_source) override; + AudioSourceInterface* audio_source); - bool StartAecDump(FILE* file, int64_t max_size_bytes) override; - void StopAecDump() override; + bool StartAecDump(FILE* file, int64_t max_size_bytes); + void StopAecDump(); virtual std::unique_ptr CreateSctpTransportInternalFactory(); @@ -85,7 +86,7 @@ class TgPeerConnectionFactory : public PeerConnectionFactoryInterface { rtc::Thread* worker_thread() { return worker_thread_; } rtc::Thread* network_thread() { return network_thread_; } - const Options& options() const { return options_; } + const PeerConnectionFactoryInterface::Options& options() const { return options_; } MediaTransportFactory* media_transport_factory() { return media_transport_factory_.get(); @@ -116,7 +117,7 @@ class TgPeerConnectionFactory : public PeerConnectionFactoryInterface { std::unique_ptr owned_network_thread_; std::unique_ptr owned_worker_thread_; const std::unique_ptr task_queue_factory_; - Options options_; + PeerConnectionFactoryInterface::Options options_; std::unique_ptr channel_manager_; std::unique_ptr default_network_manager_; std::unique_ptr default_socket_factory_; diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp new file mode 100644 index 0000000000..0d57d3d3eb --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp @@ -0,0 +1,625 @@ +/* + * Copyright 2015 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 "tg_rtp_sender.h" + +#include +#include +#include + +#include "api/audio_options.h" +#include "api/media_stream_interface.h" +#include "media/base/media_engine.h" +#include "pc/peer_connection.h" +#include "pc/stats_collector.h" +#include "rtc_base/checks.h" +#include "rtc_base/helpers.h" +#include "rtc_base/location.h" +#include "rtc_base/logging.h" +#include "rtc_base/trace_event.h" + +namespace webrtc { + +namespace { + +// This function is only expected to be called on the signaling thread. +// On the other hand, some test or even production setups may use +// several signaling threads. +int GenerateUniqueId() { + static std::atomic g_unique_id{0}; + + return ++g_unique_id; +} + +// Returns true if a "per-sender" encoding parameter contains a value that isn't +// its default. Currently max_bitrate_bps and bitrate_priority both are +// implemented "per-sender," meaning that these encoding parameters +// are used for the RtpSender as a whole, not for a specific encoding layer. +// This is done by setting these encoding parameters at index 0 of +// RtpParameters.encodings. This function can be used to check if these +// parameters are set at any index other than 0 of RtpParameters.encodings, +// because they are currently unimplemented to be used for a specific encoding +// layer. +bool PerSenderRtpEncodingParameterHasValue( + const RtpEncodingParameters& encoding_params) { + if (encoding_params.bitrate_priority != kDefaultBitratePriority || + encoding_params.network_priority != kDefaultBitratePriority) { + return true; + } + return false; +} + +void RemoveEncodingLayers(const std::vector& rids, + std::vector* encodings) { + RTC_DCHECK(encodings); + encodings->erase( + std::remove_if(encodings->begin(), encodings->end(), + [&rids](const RtpEncodingParameters& encoding) { + return absl::c_linear_search(rids, encoding.rid); + }), + encodings->end()); +} + +RtpParameters RestoreEncodingLayers( + const RtpParameters& parameters, + const std::vector& removed_rids, + const std::vector& all_layers) { + RTC_DCHECK_EQ(parameters.encodings.size() + removed_rids.size(), + all_layers.size()); + RtpParameters result(parameters); + result.encodings.clear(); + size_t index = 0; + for (const RtpEncodingParameters& encoding : all_layers) { + if (absl::c_linear_search(removed_rids, encoding.rid)) { + result.encodings.push_back(encoding); + continue; + } + result.encodings.push_back(parameters.encodings[index++]); + } + return result; +} + +} // namespace + +// Returns true if any RtpParameters member that isn't implemented contains a +// value. +bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) { + if (!parameters.mid.empty()) { + return true; + } + for (size_t i = 0; i < parameters.encodings.size(); ++i) { + // Encoding parameters that are per-sender should only contain value at + // index 0. + if (i != 0 && + PerSenderRtpEncodingParameterHasValue(parameters.encodings[i])) { + return true; + } + } + return false; +} + +TgRtpSenderBase::TgRtpSenderBase(rtc::Thread* worker_thread, + const std::string& id, + SetStreamsObserver* set_streams_observer) + : worker_thread_(worker_thread), + id_(id), + set_streams_observer_(set_streams_observer) { + RTC_DCHECK(worker_thread); + init_parameters_.encodings.emplace_back(); +} + +void TgRtpSenderBase::SetFrameEncryptor( + rtc::scoped_refptr frame_encryptor) { + frame_encryptor_ = std::move(frame_encryptor); + // Special Case: Set the frame encryptor to any value on any existing channel. + if (media_channel_ && ssrc_ && !stopped_) { + worker_thread_->Invoke(RTC_FROM_HERE, [&] { + media_channel_->SetFrameEncryptor(ssrc_, frame_encryptor_); + }); + } +} + +void TgRtpSenderBase::SetMediaChannel(cricket::MediaChannel* media_channel) { + RTC_DCHECK(media_channel == nullptr || + media_channel->media_type() == media_type()); + media_channel_ = media_channel; +} + +RtpParameters TgRtpSenderBase::GetParametersInternal() const { + if (stopped_) { + return RtpParameters(); + } + if (!media_channel_ || !ssrc_) { + return init_parameters_; + } + return worker_thread_->Invoke(RTC_FROM_HERE, [&] { + RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_); + RemoveEncodingLayers(disabled_rids_, &result.encodings); + return result; + }); +} + +RtpParameters TgRtpSenderBase::GetParameters() const { + RtpParameters result = GetParametersInternal(); + last_transaction_id_ = rtc::CreateRandomUuid(); + result.transaction_id = last_transaction_id_.value(); + return result; +} + +RTCError TgRtpSenderBase::SetParametersInternal(const RtpParameters& parameters) { + RTC_DCHECK(!stopped_); + + if (UnimplementedRtpParameterHasValue(parameters)) { + LOG_AND_RETURN_ERROR( + RTCErrorType::UNSUPPORTED_PARAMETER, + "Attempted to set an unimplemented parameter of RtpParameters."); + } + if (!media_channel_ || !ssrc_) { + auto result = cricket::CheckRtpParametersInvalidModificationAndValues( + init_parameters_, parameters); + if (result.ok()) { + init_parameters_ = parameters; + } + return result; + } + return worker_thread_->Invoke(RTC_FROM_HERE, [&] { + RtpParameters rtp_parameters = parameters; + if (!disabled_rids_.empty()) { + // Need to add the inactive layers. + RtpParameters old_parameters = + media_channel_->GetRtpSendParameters(ssrc_); + rtp_parameters = RestoreEncodingLayers(parameters, disabled_rids_, + old_parameters.encodings); + } + return media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters); + }); +} + +RTCError TgRtpSenderBase::SetParameters(const RtpParameters& parameters) { + TRACE_EVENT0("webrtc", "TgRtpSenderBase::SetParameters"); + if (stopped_) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "Cannot set parameters on a stopped sender."); + } + if (!last_transaction_id_) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_STATE, + "Failed to set parameters since getParameters() has never been called" + " on this sender"); + } + if (last_transaction_id_ != parameters.transaction_id) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Failed to set parameters since the transaction_id doesn't match" + " the last value returned from getParameters()"); + } + + RTCError result = SetParametersInternal(parameters); + last_transaction_id_.reset(); + return result; +} + +void TgRtpSenderBase::SetStreams(const std::vector& stream_ids) { + set_stream_ids(stream_ids); + if (set_streams_observer_) + set_streams_observer_->OnSetStreams(); +} + +bool TgRtpSenderBase::SetTrack(MediaStreamTrackInterface* track) { + TRACE_EVENT0("webrtc", "TgRtpSenderBase::SetTrack"); + if (stopped_) { + RTC_LOG(LS_ERROR) << "SetTrack can't be called on a stopped RtpSender."; + return false; + } + if (track && track->kind() != track_kind()) { + RTC_LOG(LS_ERROR) << "SetTrack with " << track->kind() + << " called on RtpSender with " << track_kind() + << " track."; + return false; + } + + // Detach from old track. + if (track_) { + DetachTrack(); + track_->UnregisterObserver(this); + RemoveTrackFromStats(); + } + + // Attach to new track. + bool prev_can_send_track = can_send_track(); + // Keep a reference to the old track to keep it alive until we call SetSend. + rtc::scoped_refptr old_track = track_; + track_ = track; + if (track_) { + track_->RegisterObserver(this); + AttachTrack(); + } + + // Update channel. + if (can_send_track()) { + SetSend(); + AddTrackToStats(); + } else if (prev_can_send_track) { + ClearSend(); + } + attachment_id_ = (track_ ? GenerateUniqueId() : 0); + return true; +} + +void TgRtpSenderBase::SetSsrc(uint32_t ssrc) { + TRACE_EVENT0("webrtc", "TgRtpSenderBase::SetSsrc"); + if (stopped_ || ssrc == ssrc_) { + return; + } + // If we are already sending with a particular SSRC, stop sending. + if (can_send_track()) { + ClearSend(); + RemoveTrackFromStats(); + } + ssrc_ = ssrc; + if (can_send_track()) { + SetSend(); + AddTrackToStats(); + } + if (!init_parameters_.encodings.empty()) { + worker_thread_->Invoke(RTC_FROM_HERE, [&] { + RTC_DCHECK(media_channel_); + // Get the current parameters, which are constructed from the SDP. + // The number of layers in the SDP is currently authoritative to support + // SDP munging for Plan-B simulcast with "a=ssrc-group:SIM ..." + // lines as described in RFC 5576. + // All fields should be default constructed and the SSRC field set, which + // we need to copy. + RtpParameters current_parameters = + media_channel_->GetRtpSendParameters(ssrc_); + RTC_DCHECK_GE(current_parameters.encodings.size(), + init_parameters_.encodings.size()); + for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) { + init_parameters_.encodings[i].ssrc = + current_parameters.encodings[i].ssrc; + init_parameters_.encodings[i].rid = current_parameters.encodings[i].rid; + current_parameters.encodings[i] = init_parameters_.encodings[i]; + } + current_parameters.degradation_preference = + init_parameters_.degradation_preference; + media_channel_->SetRtpSendParameters(ssrc_, current_parameters); + init_parameters_.encodings.clear(); + }); + } + // Attempt to attach the frame decryptor to the current media channel. + if (frame_encryptor_) { + SetFrameEncryptor(frame_encryptor_); + } +} + +void TgRtpSenderBase::Stop() { + TRACE_EVENT0("webrtc", "TgRtpSenderBase::Stop"); + // TODO(deadbeef): Need to do more here to fully stop sending packets. + if (stopped_) { + return; + } + if (track_) { + DetachTrack(); + track_->UnregisterObserver(this); + } + if (can_send_track()) { + ClearSend(); + RemoveTrackFromStats(); + } + media_channel_ = nullptr; + set_streams_observer_ = nullptr; + stopped_ = true; +} + +RTCError TgRtpSenderBase::DisableEncodingLayers( + const std::vector& rids) { + if (stopped_) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "Cannot disable encodings on a stopped sender."); + } + + if (rids.empty()) { + return RTCError::OK(); + } + + // Check that all the specified layers exist and disable them in the channel. + RtpParameters parameters = GetParametersInternal(); + for (const std::string& rid : rids) { + if (absl::c_none_of(parameters.encodings, + [&rid](const RtpEncodingParameters& encoding) { + return encoding.rid == rid; + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "RID: " + rid + " does not refer to a valid layer."); + } + } + + if (!media_channel_ || !ssrc_) { + RemoveEncodingLayers(rids, &init_parameters_.encodings); + // Invalidate any transaction upon success. + last_transaction_id_.reset(); + return RTCError::OK(); + } + + for (RtpEncodingParameters& encoding : parameters.encodings) { + // Remain active if not in the disable list. + encoding.active &= absl::c_none_of( + rids, + [&encoding](const std::string& rid) { return encoding.rid == rid; }); + } + + RTCError result = SetParametersInternal(parameters); + if (result.ok()) { + disabled_rids_.insert(disabled_rids_.end(), rids.begin(), rids.end()); + // Invalidate any transaction upon success. + last_transaction_id_.reset(); + } + return result; +} + +TgLocalAudioSinkAdapter::TgLocalAudioSinkAdapter() : sink_(nullptr) {} + +TgLocalAudioSinkAdapter::~TgLocalAudioSinkAdapter() { + rtc::CritScope lock(&lock_); + if (sink_) + sink_->OnClose(); +} + +void TgLocalAudioSinkAdapter::OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames) { + rtc::CritScope lock(&lock_); + if (sink_) { + sink_->OnData(audio_data, bits_per_sample, sample_rate, number_of_channels, + number_of_frames); + } +} + +void TgLocalAudioSinkAdapter::SetSink(cricket::AudioSource::Sink* sink) { + rtc::CritScope lock(&lock_); + RTC_DCHECK(!sink || !sink_); + sink_ = sink; +} + +rtc::scoped_refptr TgAudioRtpSender::Create( + rtc::Thread* worker_thread, + const std::string& id, + StatsCollector* stats, + SetStreamsObserver* set_streams_observer) { + return rtc::scoped_refptr( + new rtc::RefCountedObject(worker_thread, id, stats, + set_streams_observer)); +} + +TgAudioRtpSender::TgAudioRtpSender(rtc::Thread* worker_thread, + const std::string& id, + StatsCollector* stats, + SetStreamsObserver* set_streams_observer) + : TgRtpSenderBase(worker_thread, id, set_streams_observer), + stats_(stats), + dtmf_sender_proxy_(DtmfSenderProxy::Create( + rtc::Thread::Current(), + DtmfSender::Create(rtc::Thread::Current(), this))), + sink_adapter_(new TgLocalAudioSinkAdapter()) {} + +TgAudioRtpSender::~TgAudioRtpSender() { + // For DtmfSender. + SignalDestroyed(); + Stop(); +} + +bool TgAudioRtpSender::CanInsertDtmf() { + if (!media_channel_) { + RTC_LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists."; + return false; + } + // Check that this RTP sender is active (description has been applied that + // matches an SSRC to its ID). + if (!ssrc_) { + RTC_LOG(LS_ERROR) << "CanInsertDtmf: Sender does not have SSRC."; + return false; + } + return worker_thread_->Invoke( + RTC_FROM_HERE, [&] { return voice_media_channel()->CanInsertDtmf(); }); +} + +bool TgAudioRtpSender::InsertDtmf(int code, int duration) { + if (!media_channel_) { + RTC_LOG(LS_ERROR) << "InsertDtmf: No audio channel exists."; + return false; + } + if (!ssrc_) { + RTC_LOG(LS_ERROR) << "InsertDtmf: Sender does not have SSRC."; + return false; + } + bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { + return voice_media_channel()->InsertDtmf(ssrc_, code, duration); + }); + if (!success) { + RTC_LOG(LS_ERROR) << "Failed to insert DTMF to channel."; + } + return success; +} + +sigslot::signal0<>* TgAudioRtpSender::GetOnDestroyedSignal() { + return &SignalDestroyed; +} + +void TgAudioRtpSender::OnChanged() { + TRACE_EVENT0("webrtc", "TgAudioRtpSender::OnChanged"); + RTC_DCHECK(!stopped_); + if (cached_track_enabled_ != track_->enabled()) { + cached_track_enabled_ = track_->enabled(); + if (can_send_track()) { + SetSend(); + } + } +} + +void TgAudioRtpSender::DetachTrack() { + RTC_DCHECK(track_); + audio_track()->RemoveSink(sink_adapter_.get()); +} + +void TgAudioRtpSender::AttachTrack() { + RTC_DCHECK(track_); + cached_track_enabled_ = track_->enabled(); + audio_track()->AddSink(sink_adapter_.get()); +} + +void TgAudioRtpSender::AddTrackToStats() { + if (can_send_track() && stats_) { + stats_->AddLocalAudioTrack(audio_track().get(), ssrc_); + } +} + +void TgAudioRtpSender::RemoveTrackFromStats() { + if (can_send_track() && stats_) { + stats_->RemoveLocalAudioTrack(audio_track().get(), ssrc_); + } +} + +rtc::scoped_refptr TgAudioRtpSender::GetDtmfSender() const { + return dtmf_sender_proxy_; +} + +void TgAudioRtpSender::SetSend() { + RTC_DCHECK(!stopped_); + RTC_DCHECK(can_send_track()); + if (!media_channel_) { + RTC_LOG(LS_ERROR) << "SetAudioSend: No audio channel exists."; + return; + } + cricket::AudioOptions options; +#if !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_WEBKIT_BUILD) + // TODO(tommi): Remove this hack when we move CreateAudioSource out of + // PeerConnection. This is a bit of a strange way to apply local audio + // options since it is also applied to all streams/channels, local or remote. + if (track_->enabled() && audio_track()->GetSource() && + !audio_track()->GetSource()->remote()) { + options = audio_track()->GetSource()->options(); + } +#endif + + // |track_->enabled()| hops to the signaling thread, so call it before we hop + // to the worker thread or else it will deadlock. + bool track_enabled = track_->enabled(); + bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { + return voice_media_channel()->SetAudioSend(ssrc_, track_enabled, &options, + sink_adapter_.get()); + }); + if (!success) { + RTC_LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc_; + } +} + +void TgAudioRtpSender::ClearSend() { + RTC_DCHECK(ssrc_ != 0); + RTC_DCHECK(!stopped_); + if (!media_channel_) { + RTC_LOG(LS_WARNING) << "ClearAudioSend: No audio channel exists."; + return; + } + cricket::AudioOptions options; + bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { + return voice_media_channel()->SetAudioSend(ssrc_, false, &options, nullptr); + }); + if (!success) { + RTC_LOG(LS_WARNING) << "ClearAudioSend: ssrc is incorrect: " << ssrc_; + } +} + +rtc::scoped_refptr TgVideoRtpSender::Create( + rtc::Thread* worker_thread, + const std::string& id, + SetStreamsObserver* set_streams_observer) { + return rtc::scoped_refptr( + new rtc::RefCountedObject(worker_thread, id, + set_streams_observer)); +} + +TgVideoRtpSender::TgVideoRtpSender(rtc::Thread* worker_thread, + const std::string& id, + SetStreamsObserver* set_streams_observer) + : TgRtpSenderBase(worker_thread, id, set_streams_observer) {} + +TgVideoRtpSender::~TgVideoRtpSender() { + Stop(); +} + +void TgVideoRtpSender::OnChanged() { + TRACE_EVENT0("webrtc", "TgVideoRtpSender::OnChanged"); + RTC_DCHECK(!stopped_); + if (cached_track_content_hint_ != video_track()->content_hint()) { + cached_track_content_hint_ = video_track()->content_hint(); + if (can_send_track()) { + SetSend(); + } + } +} + +void TgVideoRtpSender::AttachTrack() { + RTC_DCHECK(track_); + cached_track_content_hint_ = video_track()->content_hint(); +} + +rtc::scoped_refptr TgVideoRtpSender::GetDtmfSender() const { + RTC_LOG(LS_ERROR) << "Tried to get DTMF sender from video sender."; + return nullptr; +} + +void TgVideoRtpSender::SetSend() { + RTC_DCHECK(!stopped_); + RTC_DCHECK(can_send_track()); + if (!media_channel_) { + RTC_LOG(LS_ERROR) << "SetVideoSend: No video channel exists."; + return; + } + cricket::VideoOptions options; + VideoTrackSourceInterface* source = video_track()->GetSource(); + if (source) { + options.is_screencast = source->is_screencast(); + options.video_noise_reduction = source->needs_denoising(); + } + switch (cached_track_content_hint_) { + case VideoTrackInterface::ContentHint::kNone: + break; + case VideoTrackInterface::ContentHint::kFluid: + options.is_screencast = false; + break; + case VideoTrackInterface::ContentHint::kDetailed: + case VideoTrackInterface::ContentHint::kText: + options.is_screencast = true; + break; + } + bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { + return video_media_channel()->SetVideoSend(ssrc_, &options, video_track()); + }); + RTC_DCHECK(success); +} + +void TgVideoRtpSender::ClearSend() { + RTC_DCHECK(ssrc_ != 0); + RTC_DCHECK(!stopped_); + if (!media_channel_) { + RTC_LOG(LS_WARNING) << "SetVideoSend: No video channel exists."; + return; + } + // Allow SetVideoSend to fail since |enable| is false and |source| is null. + // This the normal case when the underlying media channel has already been + // deleted. + worker_thread_->Invoke(RTC_FROM_HERE, [&] { + return video_media_channel()->SetVideoSend(ssrc_, nullptr, nullptr); + }); +} + +} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h new file mode 100644 index 0000000000..3ff5ed65d9 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h @@ -0,0 +1,344 @@ +/* + * Copyright 2015 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. + */ + +// This file contains classes that implement RtpSenderInterface. +// An RtpSender associates a MediaStreamTrackInterface with an underlying +// transport (provided by AudioProviderInterface/VideoProviderInterface) + +#ifndef TG_PC_RTP_SENDER_H_ +#define TG_PC_RTP_SENDER_H_ + +#include +#include +#include + +#include "api/media_stream_interface.h" +#include "api/rtp_sender_interface.h" +#include "media/base/audio_source.h" +#include "media/base/media_channel.h" +#include "pc/dtmf_sender.h" +#include "rtc_base/critical_section.h" + +namespace webrtc { + +class StatsCollector; + +bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters); + +// Internal interface used by PeerConnection. +class TgRtpSenderInternal : public RtpSenderInterface { + public: + // Sets the underlying MediaEngine channel associated with this RtpSender. + // A VoiceMediaChannel should be used for audio RtpSenders and + // a VideoMediaChannel should be used for video RtpSenders. + // Must call SetMediaChannel(nullptr) before the media channel is destroyed. + virtual void SetMediaChannel(cricket::MediaChannel* media_channel) = 0; + + // Used to set the SSRC of the sender, once a local description has been set. + // If |ssrc| is 0, this indiates that the sender should disconnect from the + // underlying transport (this occurs if the sender isn't seen in a local + // description). + virtual void SetSsrc(uint32_t ssrc) = 0; + + virtual void set_stream_ids(const std::vector& stream_ids) = 0; + virtual void set_init_send_encodings( + const std::vector& init_send_encodings) = 0; + virtual void set_transport( + rtc::scoped_refptr dtls_transport) = 0; + + virtual void Stop() = 0; + + // |GetParameters| and |SetParameters| operate with a transactional model. + // Allow access to get/set parameters without invalidating transaction id. + virtual RtpParameters GetParametersInternal() const = 0; + virtual RTCError SetParametersInternal(const RtpParameters& parameters) = 0; + + // Returns an ID that changes every time SetTrack() is called, but + // otherwise remains constant. Used to generate IDs for stats. + // The special value zero means that no track is attached. + virtual int AttachmentId() const = 0; + + // Disables the layers identified by the specified RIDs. + // If the specified list is empty, this is a no-op. + virtual RTCError DisableEncodingLayers( + const std::vector& rid) = 0; +}; + +// Shared implementation for RtpSenderInternal interface. +class TgRtpSenderBase : public TgRtpSenderInternal, public ObserverInterface { + public: + class SetStreamsObserver { + public: + virtual ~SetStreamsObserver() = default; + virtual void OnSetStreams() = 0; + }; + + // Sets the underlying MediaEngine channel associated with this RtpSender. + // A VoiceMediaChannel should be used for audio RtpSenders and + // a VideoMediaChannel should be used for video RtpSenders. + // Must call SetMediaChannel(nullptr) before the media channel is destroyed. + void SetMediaChannel(cricket::MediaChannel* media_channel) override; + + bool SetTrack(MediaStreamTrackInterface* track) override; + rtc::scoped_refptr track() const override { + return track_; + } + + RtpParameters GetParameters() const override; + RTCError SetParameters(const RtpParameters& parameters) override; + + // |GetParameters| and |SetParameters| operate with a transactional model. + // Allow access to get/set parameters without invalidating transaction id. + RtpParameters GetParametersInternal() const override; + RTCError SetParametersInternal(const RtpParameters& parameters) override; + + // Used to set the SSRC of the sender, once a local description has been set. + // If |ssrc| is 0, this indiates that the sender should disconnect from the + // underlying transport (this occurs if the sender isn't seen in a local + // description). + void SetSsrc(uint32_t ssrc) override; + uint32_t ssrc() const override { return ssrc_; } + + std::vector stream_ids() const override { return stream_ids_; } + void set_stream_ids(const std::vector& stream_ids) override { + stream_ids_ = stream_ids; + } + void SetStreams(const std::vector& stream_ids) override; + + std::string id() const override { return id_; } + + void set_init_send_encodings( + const std::vector& init_send_encodings) override { + init_parameters_.encodings = init_send_encodings; + } + std::vector init_send_encodings() const override { + return init_parameters_.encodings; + } + + void set_transport( + rtc::scoped_refptr dtls_transport) override { + dtls_transport_ = dtls_transport; + } + rtc::scoped_refptr dtls_transport() const override { + return dtls_transport_; + } + + void SetFrameEncryptor( + rtc::scoped_refptr frame_encryptor) override; + + rtc::scoped_refptr GetFrameEncryptor() + const override { + return frame_encryptor_; + } + + void Stop() override; + + // Returns an ID that changes every time SetTrack() is called, but + // otherwise remains constant. Used to generate IDs for stats. + // The special value zero means that no track is attached. + int AttachmentId() const override { return attachment_id_; } + + // Disables the layers identified by the specified RIDs. + // If the specified list is empty, this is a no-op. + RTCError DisableEncodingLayers(const std::vector& rid) override; + + protected: + // If |set_streams_observer| is not null, it is invoked when SetStreams() + // is called. |set_streams_observer| is not owned by this object. If not + // null, it must be valid at least until this sender becomes stopped. + TgRtpSenderBase(rtc::Thread* worker_thread, + const std::string& id, + SetStreamsObserver* set_streams_observer); + // TODO(nisse): Since SSRC == 0 is technically valid, figure out + // some other way to test if we have a valid SSRC. + bool can_send_track() const { return track_ && ssrc_; } + + virtual std::string track_kind() const = 0; + + // Enable sending on the media channel. + virtual void SetSend() = 0; + // Disable sending on the media channel. + virtual void ClearSend() = 0; + + // Template method pattern to allow subclasses to add custom behavior for + // when tracks are attached, detached, and for adding tracks to statistics. + virtual void AttachTrack() {} + virtual void DetachTrack() {} + virtual void AddTrackToStats() {} + virtual void RemoveTrackFromStats() {} + + rtc::Thread* worker_thread_; + uint32_t ssrc_ = 0; + bool stopped_ = false; + int attachment_id_ = 0; + const std::string id_; + + std::vector stream_ids_; + RtpParameters init_parameters_; + + cricket::MediaChannel* media_channel_ = nullptr; + rtc::scoped_refptr track_; + + rtc::scoped_refptr dtls_transport_; + rtc::scoped_refptr frame_encryptor_; + // |last_transaction_id_| is used to verify that |SetParameters| is receiving + // the parameters object that was last returned from |GetParameters|. + // As such, it is used for internal verification and is not observable by the + // the client. It is marked as mutable to enable |GetParameters| to be a + // const method. + mutable absl::optional last_transaction_id_; + std::vector disabled_rids_; + + SetStreamsObserver* set_streams_observer_ = nullptr; +}; + +// TgLocalAudioSinkAdapter receives data callback as a sink to the local +// AudioTrack, and passes the data to the sink of AudioSource. +class TgLocalAudioSinkAdapter : public AudioTrackSinkInterface, + public cricket::AudioSource { + public: + TgLocalAudioSinkAdapter(); + virtual ~TgLocalAudioSinkAdapter(); + + private: + // AudioSinkInterface implementation. + void OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames) override; + + // cricket::AudioSource implementation. + void SetSink(cricket::AudioSource::Sink* sink) override; + + cricket::AudioSource::Sink* sink_; + // Critical section protecting |sink_|. + rtc::CriticalSection lock_; +}; + +class TgAudioRtpSender : public DtmfProviderInterface, public TgRtpSenderBase { + public: + // Construct an RtpSender for audio with the given sender ID. + // The sender is initialized with no track to send and no associated streams. + // StatsCollector provided so that Add/RemoveLocalAudioTrack can be called + // at the appropriate times. + // If |set_streams_observer| is not null, it is invoked when SetStreams() + // is called. |set_streams_observer| is not owned by this object. If not + // null, it must be valid at least until this sender becomes stopped. + static rtc::scoped_refptr Create( + rtc::Thread* worker_thread, + const std::string& id, + StatsCollector* stats, + SetStreamsObserver* set_streams_observer); + virtual ~TgAudioRtpSender(); + + // DtmfSenderProvider implementation. + bool CanInsertDtmf() override; + bool InsertDtmf(int code, int duration) override; + sigslot::signal0<>* GetOnDestroyedSignal() override; + + // ObserverInterface implementation. + void OnChanged() override; + + cricket::MediaType media_type() const override { + return cricket::MEDIA_TYPE_AUDIO; + } + std::string track_kind() const override { + return MediaStreamTrackInterface::kAudioKind; + } + + rtc::scoped_refptr GetDtmfSender() const override; + + protected: + TgAudioRtpSender(rtc::Thread* worker_thread, + const std::string& id, + StatsCollector* stats, + SetStreamsObserver* set_streams_observer); + + void SetSend() override; + void ClearSend() override; + + // Hooks to allow custom logic when tracks are attached and detached. + void AttachTrack() override; + void DetachTrack() override; + void AddTrackToStats() override; + void RemoveTrackFromStats() override; + + private: + cricket::VoiceMediaChannel* voice_media_channel() { + return static_cast(media_channel_); + } + rtc::scoped_refptr audio_track() const { + return rtc::scoped_refptr( + static_cast(track_.get())); + } + sigslot::signal0<> SignalDestroyed; + + StatsCollector* stats_ = nullptr; + rtc::scoped_refptr dtmf_sender_proxy_; + bool cached_track_enabled_ = false; + + // Used to pass the data callback from the |track_| to the other end of + // cricket::AudioSource. + std::unique_ptr sink_adapter_; +}; + +class TgVideoRtpSender : public TgRtpSenderBase { + public: + // Construct an RtpSender for video with the given sender ID. + // The sender is initialized with no track to send and no associated streams. + // If |set_streams_observer| is not null, it is invoked when SetStreams() + // is called. |set_streams_observer| is not owned by this object. If not + // null, it must be valid at least until this sender becomes stopped. + static rtc::scoped_refptr Create( + rtc::Thread* worker_thread, + const std::string& id, + SetStreamsObserver* set_streams_observer); + virtual ~TgVideoRtpSender(); + + // ObserverInterface implementation + void OnChanged() override; + + cricket::MediaType media_type() const override { + return cricket::MEDIA_TYPE_VIDEO; + } + std::string track_kind() const override { + return MediaStreamTrackInterface::kVideoKind; + } + + rtc::scoped_refptr GetDtmfSender() const override; + + protected: + TgVideoRtpSender(rtc::Thread* worker_thread, + const std::string& id, + SetStreamsObserver* set_streams_observer); + + void SetSend() override; + void ClearSend() override; + + // Hook to allow custom logic when tracks are attached. + void AttachTrack() override; + + private: + cricket::VideoMediaChannel* video_media_channel() { + return static_cast(media_channel_); + } + rtc::scoped_refptr video_track() const { + return rtc::scoped_refptr( + static_cast(track_.get())); + } + + VideoTrackInterface::ContentHint cached_track_content_hint_ = + VideoTrackInterface::ContentHint::kNone; +}; + +} // namespace webrtc + +#endif // PC_RTP_SENDER_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp new file mode 100644 index 0000000000..94e64e2e3f --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp @@ -0,0 +1,501 @@ +/* + * Copyright 2013 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 "tg_webrtc_session_description_factory.h" + +#include + +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/types/optional.h" +#include "api/jsep.h" +#include "api/jsep_session_description.h" +#include "api/rtc_error.h" +#include "pc/session_description.h" +#include "rtc_base/checks.h" +#include "rtc_base/location.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/string_encode.h" + +#include "tg_peer_connection.h" + +using cricket::MediaSessionOptions; +using rtc::UniqueRandomIdGenerator; + +namespace webrtc { +namespace { +static const char kFailedDueToIdentityFailed[] = + " failed because DTLS identity request failed"; +static const char kFailedDueToSessionShutdown[] = + " failed because the session was shut down"; + +static const uint64_t kInitSessionVersion = 2; + +// Check that each sender has a unique ID. +static bool ValidMediaSessionOptions( + const cricket::MediaSessionOptions& session_options) { + std::vector sorted_senders; + for (const cricket::MediaDescriptionOptions& media_description_options : + session_options.media_description_options) { + sorted_senders.insert(sorted_senders.end(), + media_description_options.sender_options.begin(), + media_description_options.sender_options.end()); + } + absl::c_sort(sorted_senders, [](const cricket::SenderOptions& sender1, + const cricket::SenderOptions& sender2) { + return sender1.track_id < sender2.track_id; + }); + return absl::c_adjacent_find(sorted_senders, + [](const cricket::SenderOptions& sender1, + const cricket::SenderOptions& sender2) { + return sender1.track_id == sender2.track_id; + }) == sorted_senders.end(); +} + +enum { + MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, + MSG_CREATE_SESSIONDESCRIPTION_FAILED, + MSG_USE_CONSTRUCTOR_CERTIFICATE +}; + +struct CreateSessionDescriptionMsg : public rtc::MessageData { + explicit CreateSessionDescriptionMsg( + webrtc::CreateSessionDescriptionObserver* observer, + RTCError error_in) + : observer(observer), error(std::move(error_in)) {} + + rtc::scoped_refptr observer; + RTCError error; + std::unique_ptr description; +}; +} // namespace + +void TgWebRtcCertificateGeneratorCallback::OnFailure() { + SignalRequestFailed(); +} + +void TgWebRtcCertificateGeneratorCallback::OnSuccess( + const rtc::scoped_refptr& certificate) { + SignalCertificateReady(certificate); +} + +// static +void TgWebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( + const SessionDescriptionInterface* source_desc, + const std::string& content_name, + SessionDescriptionInterface* dest_desc) { + if (!source_desc) { + return; + } + const cricket::ContentInfos& contents = + source_desc->description()->contents(); + const cricket::ContentInfo* cinfo = + source_desc->description()->GetContentByName(content_name); + if (!cinfo) { + return; + } + size_t mediasection_index = static_cast(cinfo - &contents[0]); + const IceCandidateCollection* source_candidates = + source_desc->candidates(mediasection_index); + const IceCandidateCollection* dest_candidates = + dest_desc->candidates(mediasection_index); + if (!source_candidates || !dest_candidates) { + return; + } + for (size_t n = 0; n < source_candidates->count(); ++n) { + const IceCandidateInterface* new_candidate = source_candidates->at(n); + if (!dest_candidates->HasCandidate(new_candidate)) { + dest_desc->AddCandidate(source_candidates->at(n)); + } + } +} + +TgWebRtcSessionDescriptionFactory::TgWebRtcSessionDescriptionFactory( + rtc::Thread* signaling_thread, + cricket::ChannelManager* channel_manager, + TgPeerConnection* pc, + const std::string& session_id, + std::unique_ptr cert_generator, + const rtc::scoped_refptr& certificate, + UniqueRandomIdGenerator* ssrc_generator) + : signaling_thread_(signaling_thread), + session_desc_factory_(channel_manager, + &transport_desc_factory_, + ssrc_generator), + // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp + // as the session id and session version. To simplify, it should be fine + // to just use a random number as session id and start version from + // |kInitSessionVersion|. + session_version_(kInitSessionVersion), + cert_generator_(std::move(cert_generator)), + pc_(pc), + session_id_(session_id), + certificate_request_state_(CERTIFICATE_NOT_NEEDED) { + RTC_DCHECK(signaling_thread_); + RTC_DCHECK(!(cert_generator_ && certificate)); + bool dtls_enabled = cert_generator_ || certificate; + // SRTP-SDES is disabled if DTLS is on. + SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED); + if (!dtls_enabled) { + RTC_LOG(LS_VERBOSE) << "DTLS-SRTP disabled."; + return; + } + + if (certificate) { + // Use |certificate|. + certificate_request_state_ = CERTIFICATE_WAITING; + + RTC_LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter."; + // We already have a certificate but we wait to do |SetIdentity|; if we do + // it in the constructor then the caller has not had a chance to connect to + // |SignalCertificateReady|. + signaling_thread_->Post( + RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE, + new rtc::ScopedRefMessageData(certificate)); + } else { + // Generate certificate. + certificate_request_state_ = CERTIFICATE_WAITING; + + rtc::scoped_refptr callback( + new rtc::RefCountedObject()); + callback->SignalRequestFailed.connect( + this, &TgWebRtcSessionDescriptionFactory::OnCertificateRequestFailed); + callback->SignalCertificateReady.connect( + this, &TgWebRtcSessionDescriptionFactory::SetCertificate); + + rtc::KeyParams key_params = rtc::KeyParams(); + RTC_LOG(LS_VERBOSE) + << "DTLS-SRTP enabled; sending DTLS identity request (key type: " + << key_params.type() << ")."; + + // Request certificate. This happens asynchronously, so that the caller gets + // a chance to connect to |SignalCertificateReady|. + cert_generator_->GenerateCertificateAsync(key_params, absl::nullopt, + callback); + } +} + +TgWebRtcSessionDescriptionFactory::~TgWebRtcSessionDescriptionFactory() { + RTC_DCHECK(signaling_thread_->IsCurrent()); + + // Fail any requests that were asked for before identity generation completed. + FailPendingRequests(kFailedDueToSessionShutdown); + + // Process all pending notifications in the message queue. If we don't do + // this, requests will linger and not know they succeeded or failed. + rtc::MessageList list; + signaling_thread_->Clear(this, rtc::MQID_ANY, &list); + for (auto& msg : list) { + if (msg.message_id != MSG_USE_CONSTRUCTOR_CERTIFICATE) { + OnMessage(&msg); + } else { + // Skip MSG_USE_CONSTRUCTOR_CERTIFICATE because we don't want to trigger + // SetIdentity-related callbacks in the destructor. This can be a problem + // when WebRtcSession listens to the callback but it was the WebRtcSession + // destructor that caused TgWebRtcSessionDescriptionFactory's destruction. + // The callback is then ignored, leaking memory allocated by OnMessage for + // MSG_USE_CONSTRUCTOR_CERTIFICATE. + delete msg.pdata; + } + } +} + +void TgWebRtcSessionDescriptionFactory::CreateOffer( + CreateSessionDescriptionObserver* observer, + const PeerConnectionInterface::RTCOfferAnswerOptions& options, + const cricket::MediaSessionOptions& session_options) { + std::string error = "CreateOffer"; + if (certificate_request_state_ == CERTIFICATE_FAILED) { + error += kFailedDueToIdentityFailed; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailed(observer, error); + return; + } + + if (!ValidMediaSessionOptions(session_options)) { + error += " called with invalid session options"; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailed(observer, error); + return; + } + + TgCreateSessionDescriptionRequest request( + TgCreateSessionDescriptionRequest::kOffer, observer, session_options); + if (certificate_request_state_ == CERTIFICATE_WAITING) { + create_session_description_requests_.push(request); + } else { + RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED || + certificate_request_state_ == CERTIFICATE_NOT_NEEDED); + InternalCreateOffer(request); + } +} + +void TgWebRtcSessionDescriptionFactory::CreateAnswer( + CreateSessionDescriptionObserver* observer, + const cricket::MediaSessionOptions& session_options) { + std::string error = "CreateAnswer"; + if (certificate_request_state_ == CERTIFICATE_FAILED) { + error += kFailedDueToIdentityFailed; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailed(observer, error); + return; + } + if (!pc_->remote_description()) { + error += " can't be called before SetRemoteDescription."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailed(observer, error); + return; + } + if (pc_->remote_description()->GetType() != SdpType::kOffer) { + error += " failed because remote_description is not an offer."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailed(observer, error); + return; + } + + if (!ValidMediaSessionOptions(session_options)) { + error += " called with invalid session options."; + RTC_LOG(LS_ERROR) << error; + PostCreateSessionDescriptionFailed(observer, error); + return; + } + + TgCreateSessionDescriptionRequest request( + TgCreateSessionDescriptionRequest::kAnswer, observer, session_options); + if (certificate_request_state_ == CERTIFICATE_WAITING) { + create_session_description_requests_.push(request); + } else { + RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED || + certificate_request_state_ == CERTIFICATE_NOT_NEEDED); + InternalCreateAnswer(request); + } +} + +void TgWebRtcSessionDescriptionFactory::SetSdesPolicy( + cricket::SecurePolicy secure_policy) { + session_desc_factory_.set_secure(secure_policy); +} + +cricket::SecurePolicy TgWebRtcSessionDescriptionFactory::SdesPolicy() const { + return session_desc_factory_.secure(); +} + +void TgWebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) { + switch (msg->message_id) { + case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: { + CreateSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnSuccess(param->description.release()); + delete param; + break; + } + case MSG_CREATE_SESSIONDESCRIPTION_FAILED: { + CreateSessionDescriptionMsg* param = + static_cast(msg->pdata); + param->observer->OnFailure(std::move(param->error)); + delete param; + break; + } + case MSG_USE_CONSTRUCTOR_CERTIFICATE: { + rtc::ScopedRefMessageData* param = + static_cast*>( + msg->pdata); + RTC_LOG(LS_INFO) << "Using certificate supplied to the constructor."; + SetCertificate(param->data()); + delete param; + break; + } + default: + RTC_NOTREACHED(); + break; + } +} + +void TgWebRtcSessionDescriptionFactory::InternalCreateOffer( + TgCreateSessionDescriptionRequest request) { + if (pc_->local_description()) { + // If the needs-ice-restart flag is set as described by JSEP, we should + // generate an offer with a new ufrag/password to trigger an ICE restart. + for (cricket::MediaDescriptionOptions& options : + request.options.media_description_options) { + if (pc_->NeedsIceRestart(options.mid)) { + options.transport_options.ice_restart = true; + } + } + } + + std::unique_ptr desc = + session_desc_factory_.CreateOffer( + request.options, pc_->local_description() + ? pc_->local_description()->description() + : nullptr); + if (!desc) { + PostCreateSessionDescriptionFailed(request.observer, + "Failed to initialize the offer."); + return; + } + + // RFC 3264 + // When issuing an offer that modifies the session, + // the "o=" line of the new SDP MUST be identical to that in the + // previous SDP, except that the version in the origin field MUST + // increment by one from the previous SDP. + + // Just increase the version number by one each time when a new offer + // is created regardless if it's identical to the previous one or not. + // The |session_version_| is a uint64_t, the wrap around should not happen. + RTC_DCHECK(session_version_ + 1 > session_version_); + auto offer = std::make_unique( + SdpType::kOffer, std::move(desc), session_id_, + rtc::ToString(session_version_++)); + if (pc_->local_description()) { + for (const cricket::MediaDescriptionOptions& options : + request.options.media_description_options) { + if (!options.transport_options.ice_restart) { + CopyCandidatesFromSessionDescription(pc_->local_description(), + options.mid, offer.get()); + } + } + } + PostCreateSessionDescriptionSucceeded(request.observer, std::move(offer)); +} + +void TgWebRtcSessionDescriptionFactory::InternalCreateAnswer( + TgCreateSessionDescriptionRequest request) { + if (pc_->remote_description()) { + for (cricket::MediaDescriptionOptions& options : + request.options.media_description_options) { + // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1 + // an answer should also contain new ICE ufrag and password if an offer + // has been received with new ufrag and password. + options.transport_options.ice_restart = + pc_->IceRestartPending(options.mid); + // We should pass the current SSL role to the transport description + // factory, if there is already an existing ongoing session. + rtc::SSLRole ssl_role; + if (pc_->GetSslRole(options.mid, &ssl_role)) { + options.transport_options.prefer_passive_role = + (rtc::SSL_SERVER == ssl_role); + } + } + } + + std::unique_ptr desc = + session_desc_factory_.CreateAnswer( + pc_->remote_description() ? pc_->remote_description()->description() + : nullptr, + request.options, + pc_->local_description() ? pc_->local_description()->description() + : nullptr); + if (!desc) { + PostCreateSessionDescriptionFailed(request.observer, + "Failed to initialize the answer."); + return; + } + + // RFC 3264 + // If the answer is different from the offer in any way (different IP + // addresses, ports, etc.), the origin line MUST be different in the answer. + // In that case, the version number in the "o=" line of the answer is + // unrelated to the version number in the o line of the offer. + // Get a new version number by increasing the |session_version_answer_|. + // The |session_version_| is a uint64_t, the wrap around should not happen. + RTC_DCHECK(session_version_ + 1 > session_version_); + auto answer = std::make_unique( + SdpType::kAnswer, std::move(desc), session_id_, + rtc::ToString(session_version_++)); + if (pc_->local_description()) { + // Include all local ICE candidates in the SessionDescription unless + // the remote peer has requested an ICE restart. + for (const cricket::MediaDescriptionOptions& options : + request.options.media_description_options) { + if (!options.transport_options.ice_restart) { + CopyCandidatesFromSessionDescription(pc_->local_description(), + options.mid, answer.get()); + } + } + } + PostCreateSessionDescriptionSucceeded(request.observer, std::move(answer)); +} + +void TgWebRtcSessionDescriptionFactory::FailPendingRequests( + const std::string& reason) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + while (!create_session_description_requests_.empty()) { + const TgCreateSessionDescriptionRequest& request = + create_session_description_requests_.front(); + PostCreateSessionDescriptionFailed( + request.observer, + ((request.type == TgCreateSessionDescriptionRequest::kOffer) + ? "CreateOffer" + : "CreateAnswer") + + reason); + create_session_description_requests_.pop(); + } +} + +void TgWebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed( + CreateSessionDescriptionObserver* observer, + const std::string& error) { + CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg( + observer, RTCError(RTCErrorType::INTERNAL_ERROR, std::string(error))); + signaling_thread_->Post(RTC_FROM_HERE, this, + MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg); + RTC_LOG(LS_ERROR) << "Create SDP failed: " << error; +} + +void TgWebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded( + CreateSessionDescriptionObserver* observer, + std::unique_ptr description) { + CreateSessionDescriptionMsg* msg = + new CreateSessionDescriptionMsg(observer, RTCError::OK()); + msg->description = std::move(description); + signaling_thread_->Post(RTC_FROM_HERE, this, + MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg); +} + +void TgWebRtcSessionDescriptionFactory::OnCertificateRequestFailed() { + RTC_DCHECK(signaling_thread_->IsCurrent()); + + RTC_LOG(LS_ERROR) << "Asynchronous certificate generation request failed."; + certificate_request_state_ = CERTIFICATE_FAILED; + + FailPendingRequests(kFailedDueToIdentityFailed); +} + +void TgWebRtcSessionDescriptionFactory::SetCertificate( + const rtc::scoped_refptr& certificate) { + RTC_DCHECK(certificate); + RTC_LOG(LS_VERBOSE) << "Setting new certificate."; + + certificate_request_state_ = CERTIFICATE_SUCCEEDED; + SignalCertificateReady(certificate); + + transport_desc_factory_.set_certificate(certificate); + transport_desc_factory_.set_secure(cricket::SEC_ENABLED); + + while (!create_session_description_requests_.empty()) { + if (create_session_description_requests_.front().type == + TgCreateSessionDescriptionRequest::kOffer) { + InternalCreateOffer(create_session_description_requests_.front()); + } else { + InternalCreateAnswer(create_session_description_requests_.front()); + } + create_session_description_requests_.pop(); + } +} +} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h b/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h new file mode 100644 index 0000000000..655253b4d5 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h @@ -0,0 +1,167 @@ +/* + * Copyright 2013 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 TG_PC_WEBRTC_SESSION_DESCRIPTION_FACTORY_H_ +#define TG_PC_WEBRTC_SESSION_DESCRIPTION_FACTORY_H_ + +#include + +#include +#include +#include + +#include "api/jsep.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_description_factory.h" +#include "pc/media_session.h" +#include "pc/peer_connection_internal.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/message_handler.h" +#include "rtc_base/message_queue.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" +#include "rtc_base/unique_id_generator.h" + +namespace webrtc { + +class TgPeerConnection; + +// DTLS certificate request callback class. +class TgWebRtcCertificateGeneratorCallback + : public rtc::RTCCertificateGeneratorCallback, + public sigslot::has_slots<> { + public: + // |rtc::RTCCertificateGeneratorCallback| overrides. + void OnSuccess( + const rtc::scoped_refptr& certificate) override; + void OnFailure() override; + + sigslot::signal0<> SignalRequestFailed; + sigslot::signal1&> + SignalCertificateReady; +}; + +struct TgCreateSessionDescriptionRequest { + enum Type { + kOffer, + kAnswer, + }; + + TgCreateSessionDescriptionRequest(Type type, + CreateSessionDescriptionObserver* observer, + const cricket::MediaSessionOptions& options) + : type(type), observer(observer), options(options) {} + + Type type; + rtc::scoped_refptr observer; + cricket::MediaSessionOptions options; +}; + +// This class is used to create offer/answer session description. Certificates +// for WebRtcSession/DTLS are either supplied at construction or generated +// asynchronously. It queues the create offer/answer request until the +// certificate generation has completed, i.e. when OnCertificateRequestFailed or +// OnCertificateReady is called. +class TgWebRtcSessionDescriptionFactory : public rtc::MessageHandler, + public sigslot::has_slots<> { + public: + // Can specify either a |cert_generator| or |certificate| to enable DTLS. If + // a certificate generator is given, starts generating the certificate + // asynchronously. If a certificate is given, will use that for identifying + // over DTLS. If neither is specified, DTLS is disabled. + TgWebRtcSessionDescriptionFactory( + rtc::Thread* signaling_thread, + cricket::ChannelManager* channel_manager, + TgPeerConnection* pc, + const std::string& session_id, + std::unique_ptr cert_generator, + const rtc::scoped_refptr& certificate, + rtc::UniqueRandomIdGenerator* ssrc_generator); + virtual ~TgWebRtcSessionDescriptionFactory(); + + static void CopyCandidatesFromSessionDescription( + const SessionDescriptionInterface* source_desc, + const std::string& content_name, + SessionDescriptionInterface* dest_desc); + + void CreateOffer( + CreateSessionDescriptionObserver* observer, + const PeerConnectionInterface::RTCOfferAnswerOptions& options, + const cricket::MediaSessionOptions& session_options); + void CreateAnswer(CreateSessionDescriptionObserver* observer, + const cricket::MediaSessionOptions& session_options); + + void SetSdesPolicy(cricket::SecurePolicy secure_policy); + cricket::SecurePolicy SdesPolicy() const; + + void set_enable_encrypted_rtp_header_extensions(bool enable) { + session_desc_factory_.set_enable_encrypted_rtp_header_extensions(enable); + } + + void set_is_unified_plan(bool is_unified_plan) { + session_desc_factory_.set_is_unified_plan(is_unified_plan); + } + + sigslot::signal1&> + SignalCertificateReady; + + // For testing. + bool waiting_for_certificate_for_testing() const { + return certificate_request_state_ == CERTIFICATE_WAITING; + } + + private: + enum CertificateRequestState { + CERTIFICATE_NOT_NEEDED, + CERTIFICATE_WAITING, + CERTIFICATE_SUCCEEDED, + CERTIFICATE_FAILED, + }; + + // MessageHandler implementation. + virtual void OnMessage(rtc::Message* msg); + + void InternalCreateOffer(TgCreateSessionDescriptionRequest request); + void InternalCreateAnswer(TgCreateSessionDescriptionRequest request); + // Posts failure notifications for all pending session description requests. + void FailPendingRequests(const std::string& reason); + void PostCreateSessionDescriptionFailed( + CreateSessionDescriptionObserver* observer, + const std::string& error); + void PostCreateSessionDescriptionSucceeded( + CreateSessionDescriptionObserver* observer, + std::unique_ptr description); + + void OnCertificateRequestFailed(); + void SetCertificate( + const rtc::scoped_refptr& certificate); + + std::queue + create_session_description_requests_; + rtc::Thread* const signaling_thread_; + cricket::TransportDescriptionFactory transport_desc_factory_; + cricket::MediaSessionDescriptionFactory session_desc_factory_; + uint64_t session_version_; + const std::unique_ptr cert_generator_; + // TODO(jiayl): remove the dependency on peer connection once bug 2264 is + // fixed. + TgPeerConnection* const pc_; + const std::string session_id_; + CertificateRequestState certificate_request_state_; + + RTC_DISALLOW_COPY_AND_ASSIGN(TgWebRtcSessionDescriptionFactory); +}; +} // namespace webrtc + +#endif // PC_WEBRTC_SESSION_DESCRIPTION_FACTORY_H_ From a422fac01fc8da8e3d5ea5ec7f580be51056cba7 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 2 Jun 2020 19:49:54 +0400 Subject: [PATCH 4/8] Temp --- .../Sources/RtcConnection.mm | 6 +- .../Sources/tg_dtls_transport.cpp | 811 ++++++++++++++++++ .../Sources/tg_dtls_transport.h | 229 +++++ .../Sources/tg_jsep_transport_controller.cpp | 24 +- .../Sources/tg_peer_connection.cpp | 319 ++----- .../Sources/tg_peer_connection.h | 377 +++++++- .../Sources/tg_peer_connection_factory.cpp | 136 ++- .../Sources/tg_peer_connection_factory.h | 130 ++- .../Sources/tg_rtp_data_engine.cpp | 16 +- .../Sources/tg_rtp_sender.cpp | 276 +----- .../Sources/tg_rtp_sender.h | 178 +--- 11 files changed, 1749 insertions(+), 753 deletions(-) create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp create mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h diff --git a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm index 0532482146..4e72b848d9 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm +++ b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm @@ -183,10 +183,10 @@ public: std::unique_ptr _networkThread; std::unique_ptr _workerThread; std::unique_ptr _signalingThread; - rtc::scoped_refptr _nativeFactory; + rtc::scoped_refptr _nativeFactory; std::unique_ptr _observer; - rtc::scoped_refptr _peerConnection; + rtc::scoped_refptr _peerConnection; std::unique_ptr _nativeConstraints; bool _hasStartedRtcEventLog; @@ -255,7 +255,7 @@ public: if (!result) { return nil; } - _nativeFactory = pc_factory; + _nativeFactory = webrtc::TgPeerConnectionFactoryProxy::Create(pc_factory->signaling_thread(), pc_factory); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp new file mode 100644 index 0000000000..d5105f055f --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp @@ -0,0 +1,811 @@ +/* + * Copyright 2011 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 "tg_dtls_transport.h" + +#include +#include +#include + +#include "api/rtc_event_log/rtc_event_log.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "p2p/base/packet_transport_internal.h" +#include "rtc_base/buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/dscp.h" +#include "rtc_base/logging.h" +#include "rtc_base/message_queue.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/stream.h" +#include "rtc_base/thread.h" + +namespace cricket { + +// We don't pull the RTP constants from rtputils.h, to avoid a layer violation. +static const size_t kDtlsRecordHeaderLen = 13; +static const size_t kMaxDtlsPacketLen = 2048; +static const size_t kMinRtpPacketLen = 12; + +// Maximum number of pending packets in the queue. Packets are read immediately +// after they have been written, so a capacity of "1" is sufficient. +static const size_t kMaxPendingPackets = 1; + +// Minimum and maximum values for the initial DTLS handshake timeout. We'll pick +// an initial timeout based on ICE RTT estimates, but clamp it to this range. +static const int kMinHandshakeTimeout = 50; +static const int kMaxHandshakeTimeout = 3000; + +static bool IsDtlsPacket(const char* data, size_t len) { + const uint8_t* u = reinterpret_cast(data); + return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64)); +} +static bool IsDtlsClientHelloPacket(const char* data, size_t len) { + if (!IsDtlsPacket(data, len)) { + return false; + } + const uint8_t* u = reinterpret_cast(data); + return len > 17 && u[0] == 22 && u[13] == 1; +} +static bool IsRtpPacket(const char* data, size_t len) { + const uint8_t* u = reinterpret_cast(data); + return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80); +} + +/*StreamInterfaceChannel::StreamInterfaceChannel( + IceTransportInternal* ice_transport) + : ice_transport_(ice_transport), + state_(rtc::SS_OPEN), + packets_(kMaxPendingPackets, kMaxDtlsPacketLen) {} + +rtc::StreamResult StreamInterfaceChannel::Read(void* buffer, + size_t buffer_len, + size_t* read, + int* error) { + if (state_ == rtc::SS_CLOSED) + return rtc::SR_EOS; + if (state_ == rtc::SS_OPENING) + return rtc::SR_BLOCK; + + if (!packets_.ReadFront(buffer, buffer_len, read)) { + return rtc::SR_BLOCK; + } + + return rtc::SR_SUCCESS; +} + +rtc::StreamResult StreamInterfaceChannel::Write(const void* data, + size_t data_len, + size_t* written, + int* error) { + // Always succeeds, since this is an unreliable transport anyway. + // TODO(zhihuang): Should this block if ice_transport_'s temporarily + // unwritable? + rtc::PacketOptions packet_options; + ice_transport_->SendPacket(static_cast(data), data_len, + packet_options); + if (written) { + *written = data_len; + } + return rtc::SR_SUCCESS; +} + +bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) { + // We force a read event here to ensure that we don't overflow our queue. + bool ret = packets_.WriteBack(data, size, NULL); + RTC_CHECK(ret) << "Failed to write packet to queue."; + if (ret) { + SignalEvent(this, rtc::SE_READ, 0); + } + return ret; +} + +rtc::StreamState StreamInterfaceChannel::GetState() const { + return state_; +} + +void StreamInterfaceChannel::Close() { + packets_.Clear(); + state_ = rtc::SS_CLOSED; +}*/ + +TgDtlsTransport::TgDtlsTransport(IceTransportInternal* ice_transport, + const webrtc::CryptoOptions& crypto_options, + webrtc::RtcEventLog* event_log) + : transport_name_(ice_transport->transport_name()), + component_(ice_transport->component()), + ice_transport_(ice_transport), + downward_(NULL), + srtp_ciphers_(crypto_options.GetSupportedDtlsSrtpCryptoSuites()), + ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12), + crypto_options_(crypto_options), + event_log_(event_log) { + RTC_DCHECK(ice_transport_); + ConnectToIceTransport(); +} + +TgDtlsTransport::~TgDtlsTransport() = default; + +const webrtc::CryptoOptions& TgDtlsTransport::crypto_options() const { + return crypto_options_; +} + +DtlsTransportState TgDtlsTransport::dtls_state() const { + return dtls_state_; +} + +const std::string& TgDtlsTransport::transport_name() const { + return transport_name_; +} + +int TgDtlsTransport::component() const { + return component_; +} + +bool TgDtlsTransport::IsDtlsActive() const { + return dtls_active_; +} + +bool TgDtlsTransport::SetLocalCertificate( + const rtc::scoped_refptr& certificate) { + if (dtls_active_) { + if (certificate == local_certificate_) { + // This may happen during renegotiation. + RTC_LOG(LS_INFO) << ToString() << ": Ignoring identical DTLS identity"; + return true; + } else { + RTC_LOG(LS_ERROR) << ToString() + << ": Can't change DTLS local identity in this state"; + return false; + } + } + + if (certificate) { + local_certificate_ = certificate; + dtls_active_ = true; + } else { + RTC_LOG(LS_INFO) << ToString() + << ": NULL DTLS identity supplied. Not doing DTLS"; + } + + return true; +} + +rtc::scoped_refptr TgDtlsTransport::GetLocalCertificate() + const { + return local_certificate_; +} + +bool TgDtlsTransport::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) { + if (dtls_active_) { + RTC_LOG(LS_ERROR) << "Not changing max. protocol version " + "while DTLS is negotiating"; + return false; + } + + ssl_max_version_ = version; + return true; +} + +bool TgDtlsTransport::SetDtlsRole(rtc::SSLRole role) { + if (dtls_) { + RTC_DCHECK(dtls_role_); + if (*dtls_role_ != role) { + RTC_LOG(LS_ERROR) + << "SSL Role can't be reversed after the session is setup."; + return false; + } + return true; + } + + dtls_role_ = role; + return true; +} + +bool TgDtlsTransport::GetDtlsRole(rtc::SSLRole* role) const { + if (!dtls_role_) { + return false; + } + *role = *dtls_role_; + return true; +} + +bool TgDtlsTransport::GetSslCipherSuite(int* cipher) { + if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { + return false; + } + + return dtls_->GetSslCipherSuite(cipher); +} + +bool TgDtlsTransport::SetRemoteFingerprint(const std::string& digest_alg, + const uint8_t* digest, + size_t digest_len) { + rtc::Buffer remote_fingerprint_value(digest, digest_len); + + // Once we have the local certificate, the same remote fingerprint can be set + // multiple times. + if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value && + !digest_alg.empty()) { + // This may happen during renegotiation. + RTC_LOG(LS_INFO) << ToString() + << ": Ignoring identical remote DTLS fingerprint"; + return true; + } + + // If the other side doesn't support DTLS, turn off |dtls_active_|. + // TODO(deadbeef): Remove this. It's dangerous, because it relies on higher + // level code to ensure DTLS is actually used, but there are tests that + // depend on it, for the case where an m= section is rejected. In that case + // SetRemoteFingerprint shouldn't even be called though. + if (digest_alg.empty()) { + RTC_DCHECK(!digest_len); + RTC_LOG(LS_INFO) << ToString() << ": Other side didn't support DTLS."; + dtls_active_ = false; + return true; + } + + // Otherwise, we must have a local certificate before setting remote + // fingerprint. + if (!dtls_active_) { + RTC_LOG(LS_ERROR) << ToString() + << ": Can't set DTLS remote settings in this state."; + return false; + } + + // At this point we know we are doing DTLS + bool fingerprint_changing = remote_fingerprint_value_.size() > 0u; + remote_fingerprint_value_ = std::move(remote_fingerprint_value); + remote_fingerprint_algorithm_ = digest_alg; + + if (dtls_ && !fingerprint_changing) { + // This can occur if DTLS is set up before a remote fingerprint is + // received. For instance, if we set up DTLS due to receiving an early + // ClientHello. + rtc::SSLPeerCertificateDigestError err; + if (!dtls_->SetPeerCertificateDigest( + remote_fingerprint_algorithm_, + reinterpret_cast(remote_fingerprint_value_.data()), + remote_fingerprint_value_.size(), &err)) { + RTC_LOG(LS_ERROR) << ToString() + << ": Couldn't set DTLS certificate digest."; + set_dtls_state(DTLS_TRANSPORT_FAILED); + // If the error is "verification failed", don't return false, because + // this means the fingerprint was formatted correctly but didn't match + // the certificate from the DTLS handshake. Thus the DTLS state should go + // to "failed", but SetRemoteDescription shouldn't fail. + return err == rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED; + } + return true; + } + + // If the fingerprint is changing, we'll tear down the DTLS association and + // create a new one, resetting our state. + if (dtls_ && fingerprint_changing) { + dtls_.reset(nullptr); + set_dtls_state(DTLS_TRANSPORT_NEW); + set_writable(false); + } + + if (!SetupDtls()) { + set_dtls_state(DTLS_TRANSPORT_FAILED); + return false; + } + + return true; +} + +std::unique_ptr TgDtlsTransport::GetRemoteSSLCertChain() + const { + if (!dtls_) { + return nullptr; + } + + return dtls_->GetPeerSSLCertChain(); +} + +bool TgDtlsTransport::ExportKeyingMaterial(const std::string& label, + const uint8_t* context, + size_t context_len, + bool use_context, + uint8_t* result, + size_t result_len) { + return (dtls_.get()) + ? dtls_->ExportKeyingMaterial(label, context, context_len, + use_context, result, result_len) + : false; +} + +bool TgDtlsTransport::SetupDtls() { + RTC_DCHECK(dtls_role_); + StreamInterfaceChannel* downward = new StreamInterfaceChannel(ice_transport_); + + dtls_.reset(rtc::SSLStreamAdapter::Create(downward)); + if (!dtls_) { + RTC_LOG(LS_ERROR) << ToString() << ": Failed to create DTLS adapter."; + delete downward; + return false; + } + + downward_ = downward; + + dtls_->SetIdentity(local_certificate_->identity()->GetReference()); + dtls_->SetMode(rtc::SSL_MODE_DTLS); + dtls_->SetMaxProtocolVersion(ssl_max_version_); + dtls_->SetServerRole(*dtls_role_); + dtls_->SignalEvent.connect(this, &TgDtlsTransport::OnDtlsEvent); + dtls_->SignalSSLHandshakeError.connect(this, + &TgDtlsTransport::OnDtlsHandshakeError); + if (remote_fingerprint_value_.size() && + !dtls_->SetPeerCertificateDigest( + remote_fingerprint_algorithm_, + reinterpret_cast(remote_fingerprint_value_.data()), + remote_fingerprint_value_.size())) { + RTC_LOG(LS_ERROR) << ToString() + << ": Couldn't set DTLS certificate digest."; + return false; + } + + // Set up DTLS-SRTP, if it's been enabled. + if (!srtp_ciphers_.empty()) { + if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_)) { + RTC_LOG(LS_ERROR) << ToString() << ": Couldn't set DTLS-SRTP ciphers."; + return false; + } + } else { + RTC_LOG(LS_INFO) << ToString() << ": Not using DTLS-SRTP."; + } + + RTC_LOG(LS_INFO) << ToString() << ": DTLS setup complete."; + + // If the underlying ice_transport is already writable at this point, we may + // be able to start DTLS right away. + MaybeStartDtls(); + return true; +} + +bool TgDtlsTransport::GetSrtpCryptoSuite(int* cipher) { + if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { + return false; + } + + return dtls_->GetDtlsSrtpCryptoSuite(cipher); +} + +bool TgDtlsTransport::GetSslVersionBytes(int* version) const { + if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { + return false; + } + + return dtls_->GetSslVersionBytes(version); +} + +// Called from upper layers to send a media packet. +int TgDtlsTransport::SendPacket(const char* data, + size_t size, + const rtc::PacketOptions& options, + int flags) { + if (!dtls_active_) { + // Not doing DTLS. + return ice_transport_->SendPacket(data, size, options); + } + + switch (dtls_state()) { + case DTLS_TRANSPORT_NEW: + // Can't send data until the connection is active. + // TODO(ekr@rtfm.com): assert here if dtls_ is NULL? + return -1; + case DTLS_TRANSPORT_CONNECTING: + // Can't send data until the connection is active. + return -1; + case DTLS_TRANSPORT_CONNECTED: + if (flags & PF_SRTP_BYPASS) { + RTC_DCHECK(!srtp_ciphers_.empty()); + if (!IsRtpPacket(data, size)) { + return -1; + } + + return ice_transport_->SendPacket(data, size, options); + } else { + return (dtls_->WriteAll(data, size, NULL, NULL) == rtc::SR_SUCCESS) + ? static_cast(size) + : -1; + } + case DTLS_TRANSPORT_FAILED: + case DTLS_TRANSPORT_CLOSED: + // Can't send anything when we're closed. + return -1; + default: + RTC_NOTREACHED(); + return -1; + } +} + +IceTransportInternal* TgDtlsTransport::ice_transport() { + return ice_transport_; +} + +bool TgDtlsTransport::IsDtlsConnected() { + return dtls_ && dtls_->IsTlsConnected(); +} + +bool TgDtlsTransport::receiving() const { + return receiving_; +} + +bool TgDtlsTransport::writable() const { + return writable_; +} + +int TgDtlsTransport::GetError() { + return ice_transport_->GetError(); +} + +absl::optional TgDtlsTransport::network_route() const { + return ice_transport_->network_route(); +} + +bool TgDtlsTransport::GetOption(rtc::Socket::Option opt, int* value) { + return ice_transport_->GetOption(opt, value); +} + +int TgDtlsTransport::SetOption(rtc::Socket::Option opt, int value) { + return ice_transport_->SetOption(opt, value); +} + +void TgDtlsTransport::ConnectToIceTransport() { + RTC_DCHECK(ice_transport_); + ice_transport_->SignalWritableState.connect(this, + &TgDtlsTransport::OnWritableState); + ice_transport_->SignalReadPacket.connect(this, &TgDtlsTransport::OnReadPacket); + ice_transport_->SignalSentPacket.connect(this, &TgDtlsTransport::OnSentPacket); + ice_transport_->SignalReadyToSend.connect(this, + &TgDtlsTransport::OnReadyToSend); + ice_transport_->SignalReceivingState.connect( + this, &TgDtlsTransport::OnReceivingState); + ice_transport_->SignalNetworkRouteChanged.connect( + this, &TgDtlsTransport::OnNetworkRouteChanged); +} + +// The state transition logic here is as follows: +// (1) If we're not doing DTLS-SRTP, then the state is just the +// state of the underlying impl() +// (2) If we're doing DTLS-SRTP: +// - Prior to the DTLS handshake, the state is neither receiving nor +// writable +// - When the impl goes writable for the first time we +// start the DTLS handshake +// - Once the DTLS handshake completes, the state is that of the +// impl again +void TgDtlsTransport::OnWritableState(rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_); + RTC_LOG(LS_VERBOSE) << ToString() + << ": ice_transport writable state changed to " + << ice_transport_->writable(); + + if (!dtls_active_) { + // Not doing DTLS. + // Note: SignalWritableState fired by set_writable. + set_writable(ice_transport_->writable()); + return; + } + + switch (dtls_state()) { + case DTLS_TRANSPORT_NEW: + MaybeStartDtls(); + break; + case DTLS_TRANSPORT_CONNECTED: + // Note: SignalWritableState fired by set_writable. + set_writable(ice_transport_->writable()); + break; + case DTLS_TRANSPORT_CONNECTING: + // Do nothing. + break; + case DTLS_TRANSPORT_FAILED: + case DTLS_TRANSPORT_CLOSED: + // Should not happen. Do nothing. + break; + } +} + +void TgDtlsTransport::OnReceivingState(rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_); + RTC_LOG(LS_VERBOSE) << ToString() + << ": ice_transport " + "receiving state changed to " + << ice_transport_->receiving(); + if (!dtls_active_ || dtls_state() == DTLS_TRANSPORT_CONNECTED) { + // Note: SignalReceivingState fired by set_receiving. + set_receiving(ice_transport_->receiving()); + } +} + +void TgDtlsTransport::OnReadPacket(rtc::PacketTransportInternal* transport, + const char* data, + size_t size, + const int64_t& packet_time_us, + int flags) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(transport == ice_transport_); + RTC_DCHECK(flags == 0); + + if (!dtls_active_) { + // Not doing DTLS. + SignalReadPacket(this, data, size, packet_time_us, 0); + return; + } + + switch (dtls_state()) { + case DTLS_TRANSPORT_NEW: + if (dtls_) { + RTC_LOG(LS_INFO) << ToString() + << ": Packet received before DTLS started."; + } else { + RTC_LOG(LS_WARNING) << ToString() + << ": Packet received before we know if we are " + "doing DTLS or not."; + } + // Cache a client hello packet received before DTLS has actually started. + if (IsDtlsClientHelloPacket(data, size)) { + RTC_LOG(LS_INFO) << ToString() + << ": Caching DTLS ClientHello packet until DTLS is " + "started."; + cached_client_hello_.SetData(data, size); + // If we haven't started setting up DTLS yet (because we don't have a + // remote fingerprint/role), we can use the client hello as a clue that + // the peer has chosen the client role, and proceed with the handshake. + // The fingerprint will be verified when it's set. + if (!dtls_ && local_certificate_) { + SetDtlsRole(rtc::SSL_SERVER); + SetupDtls(); + } + } else { + RTC_LOG(LS_INFO) << ToString() + << ": Not a DTLS ClientHello packet; dropping."; + } + break; + + case DTLS_TRANSPORT_CONNECTING: + case DTLS_TRANSPORT_CONNECTED: + // We should only get DTLS or SRTP packets; STUN's already been demuxed. + // Is this potentially a DTLS packet? + if (IsDtlsPacket(data, size)) { + if (!HandleDtlsPacket(data, size)) { + RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet."; + return; + } + } else { + // Not a DTLS packet; our handshake should be complete by now. + if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { + RTC_LOG(LS_ERROR) << ToString() + << ": Received non-DTLS packet before DTLS " + "complete."; + return; + } + + // And it had better be a SRTP packet. + if (!IsRtpPacket(data, size)) { + RTC_LOG(LS_ERROR) + << ToString() << ": Received unexpected non-DTLS packet."; + return; + } + + // Sanity check. + RTC_DCHECK(!srtp_ciphers_.empty()); + + // Signal this upwards as a bypass packet. + SignalReadPacket(this, data, size, packet_time_us, PF_SRTP_BYPASS); + } + break; + case DTLS_TRANSPORT_FAILED: + case DTLS_TRANSPORT_CLOSED: + // This shouldn't be happening. Drop the packet. + break; + } +} + +void TgDtlsTransport::OnSentPacket(rtc::PacketTransportInternal* transport, + const rtc::SentPacket& sent_packet) { + RTC_DCHECK_RUN_ON(&thread_checker_); + SignalSentPacket(this, sent_packet); +} + +void TgDtlsTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) { + RTC_DCHECK_RUN_ON(&thread_checker_); + if (writable()) { + SignalReadyToSend(this); + } +} + +void TgDtlsTransport::OnDtlsEvent(rtc::StreamInterface* dtls, int sig, int err) { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(dtls == dtls_.get()); + if (sig & rtc::SE_OPEN) { + // This is the first time. + RTC_LOG(LS_INFO) << ToString() << ": DTLS handshake complete."; + if (dtls_->GetState() == rtc::SS_OPEN) { + // The check for OPEN shouldn't be necessary but let's make + // sure we don't accidentally frob the state if it's closed. + set_dtls_state(DTLS_TRANSPORT_CONNECTED); + set_writable(true); + } + } + if (sig & rtc::SE_READ) { + char buf[kMaxDtlsPacketLen]; + size_t read; + int read_error; + rtc::StreamResult ret; + // The underlying DTLS stream may have received multiple DTLS records in + // one packet, so read all of them. + do { + ret = dtls_->Read(buf, sizeof(buf), &read, &read_error); + if (ret == rtc::SR_SUCCESS) { + SignalReadPacket(this, buf, read, rtc::TimeMicros(), 0); + } else if (ret == rtc::SR_EOS) { + // Remote peer shut down the association with no error. + RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote"; + set_writable(false); + set_dtls_state(DTLS_TRANSPORT_CLOSED); + } else if (ret == rtc::SR_ERROR) { + // Remote peer shut down the association with an error. + RTC_LOG(LS_INFO) + << ToString() + << ": Closed by remote with DTLS transport error, code=" + << read_error; + set_writable(false); + set_dtls_state(DTLS_TRANSPORT_FAILED); + } + } while (ret == rtc::SR_SUCCESS); + } + if (sig & rtc::SE_CLOSE) { + RTC_DCHECK(sig == rtc::SE_CLOSE); // SE_CLOSE should be by itself. + set_writable(false); + if (!err) { + RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed"; + set_dtls_state(DTLS_TRANSPORT_CLOSED); + } else { + RTC_LOG(LS_INFO) << ToString() << ": DTLS transport error, code=" << err; + set_dtls_state(DTLS_TRANSPORT_FAILED); + } + } +} + +void TgDtlsTransport::OnNetworkRouteChanged( + absl::optional network_route) { + RTC_DCHECK_RUN_ON(&thread_checker_); + SignalNetworkRouteChanged(network_route); +} + +void TgDtlsTransport::MaybeStartDtls() { + if (dtls_ && ice_transport_->writable()) { + ConfigureHandshakeTimeout(); + + if (dtls_->StartSSL()) { + // This should never fail: + // Because we are operating in a nonblocking mode and all + // incoming packets come in via OnReadPacket(), which rejects + // packets in this state, the incoming queue must be empty. We + // ignore write errors, thus any errors must be because of + // configuration and therefore are our fault. + RTC_NOTREACHED() << "StartSSL failed."; + RTC_LOG(LS_ERROR) << ToString() << ": Couldn't start DTLS handshake"; + set_dtls_state(DTLS_TRANSPORT_FAILED); + return; + } + RTC_LOG(LS_INFO) << ToString() << ": DtlsTransport: Started DTLS handshake"; + set_dtls_state(DTLS_TRANSPORT_CONNECTING); + // Now that the handshake has started, we can process a cached ClientHello + // (if one exists). + if (cached_client_hello_.size()) { + if (*dtls_role_ == rtc::SSL_SERVER) { + RTC_LOG(LS_INFO) << ToString() + << ": Handling cached DTLS ClientHello packet."; + if (!HandleDtlsPacket(cached_client_hello_.data(), + cached_client_hello_.size())) { + RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet."; + } + } else { + RTC_LOG(LS_WARNING) << ToString() + << ": Discarding cached DTLS ClientHello packet " + "because we don't have the server role."; + } + cached_client_hello_.Clear(); + } + } +} + +// Called from OnReadPacket when a DTLS packet is received. +bool TgDtlsTransport::HandleDtlsPacket(const char* data, size_t size) { + // Sanity check we're not passing junk that + // just looks like DTLS. + const uint8_t* tmp_data = reinterpret_cast(data); + size_t tmp_size = size; + while (tmp_size > 0) { + if (tmp_size < kDtlsRecordHeaderLen) + return false; // Too short for the header + + size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]); + if ((record_len + kDtlsRecordHeaderLen) > tmp_size) + return false; // Body too short + + tmp_data += record_len + kDtlsRecordHeaderLen; + tmp_size -= record_len + kDtlsRecordHeaderLen; + } + + // Looks good. Pass to the SIC which ends up being passed to + // the DTLS stack. + return downward_->OnPacketReceived(data, size); +} + +void TgDtlsTransport::set_receiving(bool receiving) { + if (receiving_ == receiving) { + return; + } + receiving_ = receiving; + SignalReceivingState(this); +} + +void TgDtlsTransport::set_writable(bool writable) { + if (writable_ == writable) { + return; + } + if (event_log_) { + event_log_->Log( + std::make_unique(writable)); + } + RTC_LOG(LS_VERBOSE) << ToString() << ": set_writable to: " << writable; + writable_ = writable; + if (writable_) { + SignalReadyToSend(this); + } + SignalWritableState(this); +} + +void TgDtlsTransport::set_dtls_state(DtlsTransportState state) { + if (dtls_state_ == state) { + return; + } + if (event_log_) { + event_log_->Log(std::make_unique( + ConvertDtlsTransportState(state))); + } + RTC_LOG(LS_VERBOSE) << ToString() << ": set_dtls_state from:" << dtls_state_ + << " to " << state; + dtls_state_ = state; + SignalDtlsState(this, state); +} + +void TgDtlsTransport::OnDtlsHandshakeError(rtc::SSLHandshakeError error) { + SignalDtlsHandshakeError(error); +} + +void TgDtlsTransport::ConfigureHandshakeTimeout() { + RTC_DCHECK(dtls_); + absl::optional rtt = ice_transport_->GetRttEstimate(); + if (rtt) { + // Limit the timeout to a reasonable range in case the ICE RTT takes + // extreme values. + int initial_timeout = std::max(kMinHandshakeTimeout, + std::min(kMaxHandshakeTimeout, 2 * (*rtt))); + RTC_LOG(LS_INFO) << ToString() << ": configuring DTLS handshake timeout " + << initial_timeout << " based on ICE RTT " << *rtt; + + dtls_->SetInitialRetransmissionTimeout(initial_timeout); + } else { + RTC_LOG(LS_INFO) + << ToString() + << ": no RTT estimate - using default DTLS handshake timeout"; + } +} + +} // namespace cricket diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h b/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h new file mode 100644 index 0000000000..6753d68286 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h @@ -0,0 +1,229 @@ +/* + * Copyright 2011 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 TG_P2P_BASE_DTLS_TRANSPORT_H_ +#define TG_P2P_BASE_DTLS_TRANSPORT_H_ + +#include +#include +#include + +#include "p2p/base/dtls_transport.h" +#include "api/crypto/crypto_options.h" +#include "p2p/base/dtls_transport_internal.h" +#include "p2p/base/ice_transport_internal.h" +#include "rtc_base/buffer.h" +#include "rtc_base/buffer_queue.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/stream.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/thread_checker.h" + +namespace rtc { +class PacketTransportInternal; +} + +namespace cricket { + +// This class provides a DTLS SSLStreamAdapter inside a TransportChannel-style +// packet-based interface, wrapping an existing TransportChannel instance +// (e.g a P2PTransportChannel) +// Here's the way this works: +// +// DtlsTransport { +// SSLStreamAdapter* dtls_ { +// StreamInterfaceChannel downward_ { +// IceTransportInternal* ice_transport_; +// } +// } +// } +// +// - Data which comes into DtlsTransport from the underlying +// ice_transport_ via OnReadPacket() is checked for whether it is DTLS +// or not, and if it is, is passed to DtlsTransport::HandleDtlsPacket, +// which pushes it into to downward_. dtls_ is listening for events on +// downward_, so it immediately calls downward_->Read(). +// +// - Data written to DtlsTransport is passed either to downward_ or directly +// to ice_transport_, depending on whether DTLS is negotiated and whether +// the flags include PF_SRTP_BYPASS +// +// - The SSLStreamAdapter writes to downward_->Write() which translates it +// into packet writes on ice_transport_. +// +// This class is not thread safe; all methods must be called on the same thread +// as the constructor. +class TgDtlsTransport : public DtlsTransportInternal { + public: + // |ice_transport| is the ICE transport this DTLS transport is wrapping. It + // must outlive this DTLS transport. + // + // |crypto_options| are the options used for the DTLS handshake. This affects + // whether GCM crypto suites are negotiated. + // + // |event_log| is an optional RtcEventLog for logging state changes. It should + // outlive the DtlsTransport. + explicit TgDtlsTransport(IceTransportInternal* ice_transport, + const webrtc::CryptoOptions& crypto_options, + webrtc::RtcEventLog* event_log); + + ~TgDtlsTransport() override; + + const webrtc::CryptoOptions& crypto_options() const override; + DtlsTransportState dtls_state() const override; + const std::string& transport_name() const override; + int component() const override; + + // DTLS is active if a local certificate was set. Otherwise this acts in a + // "passthrough" mode, sending packets directly through the underlying ICE + // transport. + // TODO(deadbeef): Remove this weirdness, and handle it in the upper layers. + bool IsDtlsActive() const override; + + // SetLocalCertificate is what makes DTLS active. It must be called before + // SetRemoteFinterprint. + // TODO(deadbeef): Once DtlsTransport no longer has the concept of being + // "active" or not (acting as a passthrough if not active), just require this + // certificate on construction or "Start". + bool SetLocalCertificate( + const rtc::scoped_refptr& certificate) override; + rtc::scoped_refptr GetLocalCertificate() const override; + + // SetRemoteFingerprint must be called after SetLocalCertificate, and any + // other methods like SetDtlsRole. It's what triggers the actual DTLS setup. + // TODO(deadbeef): Rename to "Start" like in ORTC? + bool SetRemoteFingerprint(const std::string& digest_alg, + const uint8_t* digest, + size_t digest_len) override; + + // Called to send a packet (via DTLS, if turned on). + int SendPacket(const char* data, + size_t size, + const rtc::PacketOptions& options, + int flags) override; + + bool GetOption(rtc::Socket::Option opt, int* value) override; + + bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override; + + // Find out which TLS version was negotiated + bool GetSslVersionBytes(int* version) const override; + // Find out which DTLS-SRTP cipher was negotiated + bool GetSrtpCryptoSuite(int* cipher) override; + + bool GetDtlsRole(rtc::SSLRole* role) const override; + bool SetDtlsRole(rtc::SSLRole role) override; + + // Find out which DTLS cipher was negotiated + bool GetSslCipherSuite(int* cipher) override; + + // Once DTLS has been established, this method retrieves the certificate + // chain in use by the remote peer, for use in external identity + // verification. + std::unique_ptr GetRemoteSSLCertChain() const override; + + // Once DTLS has established (i.e., this ice_transport is writable), this + // method extracts the keys negotiated during the DTLS handshake, for use in + // external encryption. DTLS-SRTP uses this to extract the needed SRTP keys. + // See the SSLStreamAdapter documentation for info on the specific parameters. + bool ExportKeyingMaterial(const std::string& label, + const uint8_t* context, + size_t context_len, + bool use_context, + uint8_t* result, + size_t result_len) override; + + IceTransportInternal* ice_transport() override; + + // For informational purposes. Tells if the DTLS handshake has finished. + // This may be true even if writable() is false, if the remote fingerprint + // has not yet been verified. + bool IsDtlsConnected(); + + bool receiving() const override; + bool writable() const override; + + int GetError() override; + + absl::optional network_route() const override; + + int SetOption(rtc::Socket::Option opt, int value) override; + + std::string ToString() const { + const absl::string_view RECEIVING_ABBREV[2] = {"_", "R"}; + const absl::string_view WRITABLE_ABBREV[2] = {"_", "W"}; + rtc::StringBuilder sb; + sb << "DtlsTransport[" << transport_name_ << "|" << component_ << "|" + << RECEIVING_ABBREV[receiving()] << WRITABLE_ABBREV[writable()] << "]"; + return sb.Release(); + } + + private: + void ConnectToIceTransport(); + + void OnWritableState(rtc::PacketTransportInternal* transport); + void OnReadPacket(rtc::PacketTransportInternal* transport, + const char* data, + size_t size, + const int64_t& packet_time_us, + int flags); + void OnSentPacket(rtc::PacketTransportInternal* transport, + const rtc::SentPacket& sent_packet); + void OnReadyToSend(rtc::PacketTransportInternal* transport); + void OnReceivingState(rtc::PacketTransportInternal* transport); + void OnDtlsEvent(rtc::StreamInterface* stream_, int sig, int err); + void OnNetworkRouteChanged(absl::optional network_route); + bool SetupDtls(); + void MaybeStartDtls(); + bool HandleDtlsPacket(const char* data, size_t size); + void OnDtlsHandshakeError(rtc::SSLHandshakeError error); + void ConfigureHandshakeTimeout(); + + void set_receiving(bool receiving); + void set_writable(bool writable); + // Sets the DTLS state, signaling if necessary. + void set_dtls_state(DtlsTransportState state); + + rtc::ThreadChecker thread_checker_; + + std::string transport_name_; + int component_; + DtlsTransportState dtls_state_ = DTLS_TRANSPORT_NEW; + // Underlying ice_transport, not owned by this class. + IceTransportInternal* ice_transport_; + std::unique_ptr dtls_; // The DTLS stream + StreamInterfaceChannel* + downward_; // Wrapper for ice_transport_, owned by dtls_. + std::vector srtp_ciphers_; // SRTP ciphers to use with DTLS. + bool dtls_active_ = false; + rtc::scoped_refptr local_certificate_; + absl::optional dtls_role_; + rtc::SSLProtocolVersion ssl_max_version_; + webrtc::CryptoOptions crypto_options_; + rtc::Buffer remote_fingerprint_value_; + std::string remote_fingerprint_algorithm_; + + // Cached DTLS ClientHello packet that was received before we started the + // DTLS handshake. This could happen if the hello was received before the + // ice transport became writable, or before a remote fingerprint was received. + rtc::Buffer cached_client_hello_; + + bool receiving_ = false; + bool writable_ = false; + + webrtc::RtcEventLog* const event_log_; + + RTC_DISALLOW_COPY_AND_ASSIGN(TgDtlsTransport); +}; + +} // namespace cricket + +#endif // P2P_BASE_DTLS_TRANSPORT_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp index 62c5a2eaa5..3c5c11d1f6 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp @@ -25,6 +25,8 @@ #include "rtc_base/checks.h" #include "rtc_base/thread.h" +#include "tg_dtls_transport.h" + using webrtc::SdpType; namespace { @@ -481,7 +483,7 @@ TgJsepTransportController::CreateDtlsTransport( dtls = config_.dtls_transport_factory->CreateDtlsTransport( ice, config_.crypto_options); } else { - dtls = std::make_unique(ice, config_.crypto_options, + dtls = std::make_unique(ice, config_.crypto_options, config_.event_log); } @@ -1153,9 +1155,9 @@ RTCError TgJsepTransportController::MaybeCreateJsepTransport( CreateDtlsTransport(content_info, ice->internal(), nullptr); //std::unique_ptr rtcp_dtls_transport; - std::unique_ptr unencrypted_rtp_transport; + //std::unique_ptr unencrypted_rtp_transport; //std::unique_ptr sdes_transport; - //std::unique_ptr dtls_srtp_transport; + std::unique_ptr dtls_srtp_transport; //std::unique_ptr datagram_rtp_transport; //rtc::scoped_refptr rtcp_ice; @@ -1189,18 +1191,18 @@ RTCError TgJsepTransportController::MaybeCreateJsepTransport( /*if (config_.disable_encryption) { RTC_LOG(LS_INFO) - << "Creating UnencryptedRtpTransport, becayse encryption is disabled.";*/ + << "Creating UnencryptedRtpTransport, becayse encryption is disabled."; unencrypted_rtp_transport = CreateUnencryptedRtpTransport( - content_info.name, rtp_dtls_transport.get(), /*rtcp_dtls_transport.get()*/nullptr); - /*} else if (!content_desc->cryptos().empty()) { + content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); + } else if (!content_desc->cryptos().empty()) { sdes_transport = CreateSdesTransport( content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); RTC_LOG(LS_INFO) << "Creating SdesTransport."; - } else { + } else {*/ RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport."; dtls_srtp_transport = CreateDtlsSrtpTransport( - content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); - }*/ + content_info.name, rtp_dtls_transport.get(), nullptr); + //} /*std::unique_ptr sctp_transport; if (config_.sctp_factory) { @@ -1216,8 +1218,8 @@ RTCError TgJsepTransportController::MaybeCreateJsepTransport( std::unique_ptr jsep_transport = std::make_unique( content_info.name, certificate_, std::move(ice), /*std::move(rtcp_ice)*/nullptr, - std::move(unencrypted_rtp_transport), /*std::move(sdes_transport)*/nullptr, - /*std::move(dtls_srtp_transport)*/nullptr, /*std::move(datagram_rtp_transport)*/nullptr, + /*std::move(unencrypted_rtp_transport)*/nullptr, /*std::move(sdes_transport)*/nullptr, + std::move(dtls_srtp_transport), /*std::move(datagram_rtp_transport)*/nullptr, std::move(rtp_dtls_transport), /*std::move(rtcp_dtls_transport)*/nullptr, /*std::move(sctp_transport)*/nullptr, /*std::move(datagram_transport)*/nullptr, /*data_channel_transport*/nullptr); diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp index c10344fe7a..7e21008d0d 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp @@ -87,6 +87,34 @@ using cricket::STUN_PORT_TYPE; namespace webrtc { +RTCError TgPeerConnectionInterface::RemoveTrackNew( + rtc::scoped_refptr sender) { + return RTCError(RemoveTrack(sender) ? RTCErrorType::NONE + : RTCErrorType::INTERNAL_ERROR); +} + +RTCError TgPeerConnectionInterface::SetConfiguration( + const PeerConnectionInterface::RTCConfiguration& config) { + return RTCError(); +} + +RTCError TgPeerConnectionInterface::SetBitrate(const BitrateSettings& bitrate) { + PeerConnectionInterface::BitrateParameters bitrate_parameters; + bitrate_parameters.min_bitrate_bps = bitrate.min_bitrate_bps; + bitrate_parameters.current_bitrate_bps = bitrate.start_bitrate_bps; + bitrate_parameters.max_bitrate_bps = bitrate.max_bitrate_bps; + return SetBitrate(bitrate_parameters); +} + +RTCError TgPeerConnectionInterface::SetBitrate( + const PeerConnectionInterface::BitrateParameters& bitrate_parameters) { + BitrateSettings bitrate; + bitrate.min_bitrate_bps = bitrate_parameters.min_bitrate_bps; + bitrate.start_bitrate_bps = bitrate_parameters.current_bitrate_bps; + bitrate.max_bitrate_bps = bitrate_parameters.max_bitrate_bps; + return SetBitrate(bitrate); +} + // Error messages const char kBundleWithoutRtcpMux[] = "rtcp-mux must be enabled when BUNDLE " @@ -375,100 +403,6 @@ bool MediaSectionsHaveSameCount(const SessionDescription& desc1, return desc1.contents().size() == desc2.contents().size(); } -void NoteKeyProtocolAndMedia(KeyExchangeProtocolType protocol_type, - cricket::MediaType media_type) { - // Array of structs needed to map {KeyExchangeProtocolType, - // cricket::MediaType} to KeyExchangeProtocolMedia without using std::map in - // order to avoid -Wglobal-constructors and -Wexit-time-destructors. - static constexpr struct { - KeyExchangeProtocolType protocol_type; - cricket::MediaType media_type; - KeyExchangeProtocolMedia protocol_media; - } kEnumCounterKeyProtocolMediaMap[] = { - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_AUDIO, - kEnumCounterKeyProtocolMediaTypeDtlsAudio}, - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_VIDEO, - kEnumCounterKeyProtocolMediaTypeDtlsVideo}, - {kEnumCounterKeyProtocolDtls, cricket::MEDIA_TYPE_DATA, - kEnumCounterKeyProtocolMediaTypeDtlsData}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_AUDIO, - kEnumCounterKeyProtocolMediaTypeSdesAudio}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_VIDEO, - kEnumCounterKeyProtocolMediaTypeSdesVideo}, - {kEnumCounterKeyProtocolSdes, cricket::MEDIA_TYPE_DATA, - kEnumCounterKeyProtocolMediaTypeSdesData}, - }; - - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocol", protocol_type, - kEnumCounterKeyProtocolMax); - - for (const auto& i : kEnumCounterKeyProtocolMediaMap) { - if (i.protocol_type == protocol_type && i.media_type == media_type) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.KeyProtocolByMedia", - i.protocol_media, - kEnumCounterKeyProtocolMediaTypeMax); - } - } -} - -void NoteAddIceCandidateResult(int result) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.AddIceCandidate", result, - kAddIceCandidateMax); -} - -// 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 TgJsepTransport negotiation, and backstopped -// by Channel's |srtp_required| check. -RTCError VerifyCrypto(const SessionDescription* desc, bool dtls_enabled) { - const cricket::ContentGroup* bundle = - desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - for (const cricket::ContentInfo& content_info : desc->contents()) { - if (content_info.rejected) { - continue; - } - // Note what media is used with each crypto protocol, for all sections. - NoteKeyProtocolAndMedia(dtls_enabled ? webrtc::kEnumCounterKeyProtocolDtls - : webrtc::kEnumCounterKeyProtocolSdes, - content_info.media_description()->type()); - const std::string& mid = content_info.name; - if (bundle && bundle->HasContentName(mid) && - mid != *(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 = content_info.media_description(); - const TransportInfo* tinfo = desc->GetTransportInfoByName(mid); - if (!media || !tinfo) { - // Something is not right. - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); - } - if (dtls_enabled) { - if (!tinfo->description.identity_fingerprint) { - RTC_LOG(LS_WARNING) - << "Session description must have DTLS fingerprint if " - "DTLS enabled."; - return RTCError(RTCErrorType::INVALID_PARAMETER, - kSdpWithoutDtlsFingerprint); - } - } else { - if (media->cryptos().empty()) { - RTC_LOG(LS_WARNING) - << "Session description must have SDES when DTLS disabled."; - return RTCError(RTCErrorType::INVALID_PARAMETER, kSdpWithoutSdesCrypto); - } - } - } - return RTCError::OK(); -} - // 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. @@ -809,140 +743,6 @@ private: rtc::scoped_refptr wrapper_; }; -bool PeerConnectionInterface::RTCConfiguration::operator==( - const PeerConnectionInterface::RTCConfiguration& o) const { - // This static_assert prevents us from accidentally breaking operator==. - // Note: Order matters! Fields must be ordered the same as RTCConfiguration. - struct stuff_being_tested_for_equality { - IceServers servers; - IceTransportsType type; - BundlePolicy bundle_policy; - RtcpMuxPolicy rtcp_mux_policy; - std::vector> certificates; - int ice_candidate_pool_size; - bool disable_ipv6; - bool disable_ipv6_on_wifi; - int max_ipv6_networks; - bool disable_link_local_networks; - bool enable_rtp_data_channel; - absl::optional screencast_min_bitrate; - absl::optional combined_audio_video_bwe; - absl::optional enable_dtls_srtp; - TcpCandidatePolicy tcp_candidate_policy; - CandidateNetworkPolicy candidate_network_policy; - int audio_jitter_buffer_max_packets; - bool audio_jitter_buffer_fast_accelerate; - int audio_jitter_buffer_min_delay_ms; - bool audio_jitter_buffer_enable_rtx_handling; - int ice_connection_receiving_timeout; - int ice_backup_candidate_pair_ping_interval; - ContinualGatheringPolicy continual_gathering_policy; - bool prioritize_most_likely_ice_candidate_pairs; - struct cricket::MediaConfig media_config; - bool prune_turn_ports; - PortPrunePolicy turn_port_prune_policy; - bool presume_writable_when_fully_relayed; - bool enable_ice_renomination; - bool redetermine_role_on_ice_restart; - bool surface_ice_candidates_on_ice_transport_type_changed; - absl::optional ice_check_interval_strong_connectivity; - absl::optional ice_check_interval_weak_connectivity; - absl::optional ice_check_min_interval; - absl::optional ice_unwritable_timeout; - absl::optional ice_unwritable_min_checks; - absl::optional ice_inactive_timeout; - absl::optional stun_candidate_keepalive_interval; - absl::optional ice_regather_interval_range; - webrtc::TurnCustomizer* turn_customizer; - SdpSemantics sdp_semantics; - absl::optional network_preference; - bool active_reset_srtp_params; - bool use_media_transport; - bool use_media_transport_for_data_channels; - absl::optional use_datagram_transport; - absl::optional use_datagram_transport_for_data_channels; - absl::optional use_datagram_transport_for_data_channels_receive_only; - absl::optional crypto_options; - bool offer_extmap_allow_mixed; - std::string turn_logging_id; - bool enable_implicit_rollback; - absl::optional allow_codec_switching; - }; - static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this), - "Did you add something to RTCConfiguration and forget to " - "update operator==?"); - return type == o.type && servers == o.servers && - bundle_policy == o.bundle_policy && - rtcp_mux_policy == o.rtcp_mux_policy && - tcp_candidate_policy == o.tcp_candidate_policy && - candidate_network_policy == o.candidate_network_policy && - audio_jitter_buffer_max_packets == o.audio_jitter_buffer_max_packets && - audio_jitter_buffer_fast_accelerate == - o.audio_jitter_buffer_fast_accelerate && - audio_jitter_buffer_min_delay_ms == - o.audio_jitter_buffer_min_delay_ms && - audio_jitter_buffer_enable_rtx_handling == - o.audio_jitter_buffer_enable_rtx_handling && - ice_connection_receiving_timeout == - o.ice_connection_receiving_timeout && - ice_backup_candidate_pair_ping_interval == - o.ice_backup_candidate_pair_ping_interval && - continual_gathering_policy == o.continual_gathering_policy && - certificates == o.certificates && - prioritize_most_likely_ice_candidate_pairs == - o.prioritize_most_likely_ice_candidate_pairs && - media_config == o.media_config && disable_ipv6 == o.disable_ipv6 && - disable_ipv6_on_wifi == o.disable_ipv6_on_wifi && - max_ipv6_networks == o.max_ipv6_networks && - disable_link_local_networks == o.disable_link_local_networks && - enable_rtp_data_channel == o.enable_rtp_data_channel && - screencast_min_bitrate == o.screencast_min_bitrate && - combined_audio_video_bwe == o.combined_audio_video_bwe && - enable_dtls_srtp == o.enable_dtls_srtp && - ice_candidate_pool_size == o.ice_candidate_pool_size && - prune_turn_ports == o.prune_turn_ports && - turn_port_prune_policy == o.turn_port_prune_policy && - presume_writable_when_fully_relayed == - o.presume_writable_when_fully_relayed && - enable_ice_renomination == o.enable_ice_renomination && - redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart && - surface_ice_candidates_on_ice_transport_type_changed == - o.surface_ice_candidates_on_ice_transport_type_changed && - ice_check_interval_strong_connectivity == - o.ice_check_interval_strong_connectivity && - ice_check_interval_weak_connectivity == - o.ice_check_interval_weak_connectivity && - ice_check_min_interval == o.ice_check_min_interval && - ice_unwritable_timeout == o.ice_unwritable_timeout && - ice_unwritable_min_checks == o.ice_unwritable_min_checks && - ice_inactive_timeout == o.ice_inactive_timeout && - stun_candidate_keepalive_interval == - o.stun_candidate_keepalive_interval && - ice_regather_interval_range == o.ice_regather_interval_range && - turn_customizer == o.turn_customizer && - sdp_semantics == o.sdp_semantics && - network_preference == o.network_preference && - active_reset_srtp_params == o.active_reset_srtp_params && - use_media_transport == o.use_media_transport && - use_media_transport_for_data_channels == - o.use_media_transport_for_data_channels && - use_datagram_transport == o.use_datagram_transport && - use_datagram_transport_for_data_channels == - o.use_datagram_transport_for_data_channels && - use_datagram_transport_for_data_channels_receive_only == - o.use_datagram_transport_for_data_channels_receive_only && - crypto_options == o.crypto_options && - offer_extmap_allow_mixed == o.offer_extmap_allow_mixed && - turn_logging_id == o.turn_logging_id && - enable_implicit_rollback == o.enable_implicit_rollback && - allow_codec_switching == o.allow_codec_switching; -} - -bool PeerConnectionInterface::RTCConfiguration::operator!=( - const PeerConnectionInterface::RTCConfiguration& o) const { - return !(*this == o); -} - void TgPeerConnection::TransceiverStableState::set_newly_created() { RTC_DCHECK(!has_m_section_); newly_created_ = true; @@ -966,7 +766,7 @@ void TgPeerConnection::TransceiverStableState::SetRemoteStreamIdsIfUnset( } // Generate a RTCP CNAME when a TgPeerConnection is created. -std::string GenerateRtcpCname() { +std::string TgGenerateRtcpCname() { std::string cname; if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) { RTC_LOG(LS_ERROR) << "Failed to generate CNAME."; @@ -975,7 +775,7 @@ std::string GenerateRtcpCname() { return cname; } -bool ValidateOfferAnswerOptions( +bool TgValidateOfferAnswerOptions( const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) { return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) && IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video); @@ -983,7 +783,7 @@ bool ValidateOfferAnswerOptions( // From |rtc_options|, fill parts of |session_options| shared by all generated // m= sections (in other words, nothing that involves a map/array). -void ExtractSharedMediaSessionOptions( +void TgExtractSharedMediaSessionOptions( const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options, cricket::MediaSessionOptions* session_options) { session_options->vad_enabled = rtc_options.voice_activity_detection; @@ -1003,7 +803,7 @@ datagram_transport_config_( field_trial::FindFullName(kDatagramTransportFieldTrial)), datagram_transport_data_channel_config_( field_trial::FindFullName(kDatagramTransportDataChannelFieldTrial)), -rtcp_cname_(GenerateRtcpCname()), +rtcp_cname_(TgGenerateRtcpCname()), local_streams_(StreamCollection::Create()), remote_streams_(StreamCollection::Create()), call_(std::move(call)), @@ -1717,7 +1517,7 @@ TgPeerConnection::AddTransceiver( } } - if (UnimplementedRtpParameterHasValue(parameters)) { + if (TgUnimplementedRtpParameterHasValue(parameters)) { LOG_AND_RETURN_ERROR( RTCErrorType::UNSUPPORTED_PARAMETER, "Attempted to set an unimplemented parameter of RtpParameters."); @@ -1748,7 +1548,7 @@ TgPeerConnection::AddTransceiver( return rtc::scoped_refptr(transceiver); } -rtc::scoped_refptr> +rtc::scoped_refptr> TgPeerConnection::CreateSender( cricket::MediaType media_type, const std::string& id, @@ -1756,19 +1556,19 @@ TgPeerConnection::CreateSender( const std::vector& stream_ids, const std::vector& send_encodings) { RTC_DCHECK_RUN_ON(signaling_thread()); - rtc::scoped_refptr> sender; + rtc::scoped_refptr> sender; if (media_type == cricket::MEDIA_TYPE_AUDIO) { RTC_DCHECK(!track || (track->kind() == MediaStreamTrackInterface::kAudioKind)); - sender = RtpSenderProxyWithInternal::Create( + sender = RtpSenderProxyWithInternal::Create( signaling_thread(), - TgAudioRtpSender::Create(worker_thread(), id, stats_.get(), this)); + TgAudioRtpSender::Create(worker_thread(), id, this)); NoteUsageEvent(UsageEvent::AUDIO_ADDED); } else { RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); RTC_DCHECK(!track || (track->kind() == MediaStreamTrackInterface::kVideoKind)); - sender = RtpSenderProxyWithInternal::Create( + sender = RtpSenderProxyWithInternal::Create( signaling_thread(), TgVideoRtpSender::Create(worker_thread(), id, this)); NoteUsageEvent(UsageEvent::VIDEO_ADDED); } @@ -1801,7 +1601,7 @@ TgPeerConnection::CreateReceiver(cricket::MediaType media_type, rtc::scoped_refptr> TgPeerConnection::CreateAndAddTransceiver( - rtc::scoped_refptr> sender, + rtc::scoped_refptr> sender, rtc::scoped_refptr> receiver) { // Ensure that the new sender does not have an ID that is already in use by @@ -1849,19 +1649,19 @@ rtc::scoped_refptr TgPeerConnection::CreateSender( } // TODO(steveanton): Move construction of the RtpSenders to RtpTransceiver. - rtc::scoped_refptr> new_sender; + rtc::scoped_refptr> new_sender; if (kind == MediaStreamTrackInterface::kAudioKind) { auto audio_sender = TgAudioRtpSender::Create( - worker_thread(), rtc::CreateRandomUuid(), stats_.get(), this); + worker_thread(), rtc::CreateRandomUuid(), this); audio_sender->SetMediaChannel(voice_media_channel()); - new_sender = RtpSenderProxyWithInternal::Create( + new_sender = RtpSenderProxyWithInternal::Create( signaling_thread(), audio_sender); GetAudioTransceiver()->internal()->AddSender(new_sender); } else if (kind == MediaStreamTrackInterface::kVideoKind) { auto video_sender = TgVideoRtpSender::Create(worker_thread(), rtc::CreateRandomUuid(), this); video_sender->SetMediaChannel(video_media_channel()); - new_sender = RtpSenderProxyWithInternal::Create( + new_sender = RtpSenderProxyWithInternal::Create( signaling_thread(), video_sender); GetVideoTransceiver()->internal()->AddSender(new_sender); } else { @@ -1883,9 +1683,9 @@ const { return ret; } -std::vector>> +std::vector>> TgPeerConnection::GetSendersInternal() const { - std::vector>> + std::vector>> all_senders; for (const auto& transceiver : transceivers_) { auto senders = transceiver->internal()->senders(); @@ -2031,7 +1831,7 @@ void TgPeerConnection::DoCreateOffer( return; } - if (!ValidateOfferAnswerOptions(options)) { + if (!TgValidateOfferAnswerOptions(options)) { std::string error = "CreateOffer called with invalid options."; RTC_LOG(LS_ERROR) << error; PostCreateSessionDescriptionFailure( @@ -3305,7 +3105,7 @@ static std::vector GetSendEncodingsFromRemoteDescription( static RTCError UpdateSimulcastLayerStatusInSender( const std::vector& layers, - rtc::scoped_refptr sender) { + rtc::scoped_refptr sender) { RTC_DCHECK(sender); RtpParameters parameters = sender->GetParametersInternal(); std::vector disabled_layers; @@ -3347,7 +3147,7 @@ static bool SimulcastIsRejected( } static RTCError DisableSimulcastInSender( - rtc::scoped_refptr sender) { + rtc::scoped_refptr sender) { RTC_DCHECK(sender); RtpParameters parameters = sender->GetParametersInternal(); if (parameters.encodings.size() <= 1) { @@ -3783,34 +3583,29 @@ bool TgPeerConnection::AddIceCandidate( TRACE_EVENT0("webrtc", "TgPeerConnection::AddIceCandidate"); if (IsClosed()) { RTC_LOG(LS_ERROR) << "AddIceCandidate: TgPeerConnection is closed."; - NoteAddIceCandidateResult(kAddIceCandidateFailClosed); return false; } if (!remote_description()) { RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added " "without any remote session description."; - NoteAddIceCandidateResult(kAddIceCandidateFailNoRemoteDescription); return false; } if (!ice_candidate) { RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null."; - NoteAddIceCandidateResult(kAddIceCandidateFailNullCandidate); return false; } bool valid = false; bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid); if (!valid) { - NoteAddIceCandidateResult(kAddIceCandidateFailNotValid); return false; } // Add this candidate to the remote session description. if (!mutable_remote_description()->AddCandidate(ice_candidate)) { RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used."; - NoteAddIceCandidateResult(kAddIceCandidateFailInAddition); return false; } @@ -3818,14 +3613,11 @@ bool TgPeerConnection::AddIceCandidate( bool result = UseCandidate(ice_candidate); if (result) { NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED); - NoteAddIceCandidateResult(kAddIceCandidateSuccess); } else { - NoteAddIceCandidateResult(kAddIceCandidateFailNotUsable); } return result; } else { RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate."; - NoteAddIceCandidateResult(kAddIceCandidateFailNotReady); return true; } } @@ -4508,7 +4300,7 @@ void TgPeerConnection::PostCreateSessionDescriptionFailure( void TgPeerConnection::GetOptionsForOffer( const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, cricket::MediaSessionOptions* session_options) { - ExtractSharedMediaSessionOptions(offer_answer_options, session_options); + TgExtractSharedMediaSessionOptions(offer_answer_options, session_options); GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options); @@ -4720,7 +4512,7 @@ void TgPeerConnection::GetOptionsForUnifiedPlanOffer( void TgPeerConnection::GetOptionsForAnswer( const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, cricket::MediaSessionOptions* session_options) { - ExtractSharedMediaSessionOptions(offer_answer_options, session_options); + TgExtractSharedMediaSessionOptions(offer_answer_options, session_options); GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options); @@ -5118,7 +4910,7 @@ bool TgPeerConnection::HasRtpSender(cricket::MediaType type) const { return false; } -rtc::scoped_refptr> +rtc::scoped_refptr> TgPeerConnection::FindSenderForTrack(MediaStreamTrackInterface* track) const { for (const auto& transceiver : transceivers_) { for (auto sender : transceiver->internal()->senders()) { @@ -5130,7 +4922,7 @@ TgPeerConnection::FindSenderForTrack(MediaStreamTrackInterface* track) const { return nullptr; } -rtc::scoped_refptr> +rtc::scoped_refptr> TgPeerConnection::FindSenderById(const std::string& sender_id) const { for (const auto& transceiver : transceivers_) { for (auto sender : transceiver->internal()->senders()) { @@ -6058,13 +5850,6 @@ RTCError TgPeerConnection::ValidateSessionDescription( // Verify crypto settings. std::string crypto_error; - if (webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || - dtls_enabled_) { - RTCError crypto_error = VerifyCrypto(sdesc->description(), dtls_enabled_); - if (!crypto_error.ok()) { - return crypto_error; - } - } // Verify ice-ufrag and ice-pwd. if (!VerifyIceUfragPwdPresent(sdesc->description())) { diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h index ff658e84c1..e362101034 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h @@ -63,9 +63,370 @@ class RtcEventLog; // - Generating offers and answers based on the current state. // - The ICE state machine. // - Generating stats. -class TgPeerConnection : public rtc::RefCountInterface, + +class RTC_EXPORT TgPeerConnectionInterface : public rtc::RefCountInterface { + public: + // Accessor methods to active local streams. + // This method is not supported with kUnifiedPlan semantics. Please use + // GetSenders() instead. + virtual rtc::scoped_refptr local_streams() = 0; + + // Accessor methods to remote streams. + // This method is not supported with kUnifiedPlan semantics. Please use + // GetReceivers() instead. + virtual rtc::scoped_refptr remote_streams() = 0; + + // Add a new MediaStream to be sent on this PeerConnection. + // Note that a SessionDescription negotiation is needed before the + // remote peer can receive the stream. + // + // This has been removed from the standard in favor of a track-based API. So, + // this is equivalent to simply calling AddTrack for each track within the + // stream, with the one difference that if "stream->AddTrack(...)" is called + // later, the PeerConnection will automatically pick up the new track. Though + // this functionality will be deprecated in the future. + // + // This method is not supported with kUnifiedPlan semantics. Please use + // AddTrack instead. + virtual bool AddStream(MediaStreamInterface* stream) = 0; + + // Remove a MediaStream from this PeerConnection. + // Note that a SessionDescription negotiation is needed before the + // remote peer is notified. + // + // This method is not supported with kUnifiedPlan semantics. Please use + // RemoveTrack instead. + virtual void RemoveStream(MediaStreamInterface* stream) = 0; + + // Add a new MediaStreamTrack to be sent on this PeerConnection, and return + // the newly created RtpSender. The RtpSender will be associated with the + // streams specified in the |stream_ids| list. + // + // Errors: + // - INVALID_PARAMETER: |track| is null, has a kind other than audio or video, + // or a sender already exists for the track. + // - INVALID_STATE: The PeerConnection is closed. + virtual RTCErrorOr> AddTrack( + rtc::scoped_refptr track, + const std::vector& stream_ids) = 0; + + // Remove an RtpSender from this PeerConnection. + // Returns true on success. + // TODO(steveanton): Replace with signature that returns RTCError. + virtual bool RemoveTrack(RtpSenderInterface* sender) = 0; + + // Plan B semantics: Removes the RtpSender from this PeerConnection. + // Unified Plan semantics: Stop sending on the RtpSender and mark the + // corresponding RtpTransceiver direction as no longer sending. + // + // Errors: + // - INVALID_PARAMETER: |sender| is null or (Plan B only) the sender is not + // associated with this PeerConnection. + // - INVALID_STATE: PeerConnection is closed. + // TODO(bugs.webrtc.org/9534): Rename to RemoveTrack once the other signature + // is removed. + virtual RTCError RemoveTrackNew( + rtc::scoped_refptr sender); + + // AddTransceiver creates a new RtpTransceiver and adds it to the set of + // transceivers. Adding a transceiver will cause future calls to CreateOffer + // to add a media description for the corresponding transceiver. + // + // The initial value of |mid| in the returned transceiver is null. Setting a + // new session description may change it to a non-null value. + // + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver + // + // Optionally, an RtpTransceiverInit structure can be specified to configure + // the transceiver from construction. If not specified, the transceiver will + // default to having a direction of kSendRecv and not be part of any streams. + // + // These methods are only available when Unified Plan is enabled (see + // RTCConfiguration). + // + // Common errors: + // - INTERNAL_ERROR: The configuration does not have Unified Plan enabled. + + // Adds a transceiver with a sender set to transmit the given track. The kind + // of the transceiver (and sender/receiver) will be derived from the kind of + // the track. + // Errors: + // - INVALID_PARAMETER: |track| is null. + virtual RTCErrorOr> + AddTransceiver(rtc::scoped_refptr track) = 0; + virtual RTCErrorOr> + AddTransceiver(rtc::scoped_refptr track, + const RtpTransceiverInit& init) = 0; + + // Adds a transceiver with the given kind. Can either be MEDIA_TYPE_AUDIO or + // MEDIA_TYPE_VIDEO. + // Errors: + // - INVALID_PARAMETER: |media_type| is not MEDIA_TYPE_AUDIO or + // MEDIA_TYPE_VIDEO. + virtual RTCErrorOr> + AddTransceiver(cricket::MediaType media_type) = 0; + virtual RTCErrorOr> + AddTransceiver(cricket::MediaType media_type, + const RtpTransceiverInit& init) = 0; + + // Creates a sender without a track. Can be used for "early media"/"warmup" + // use cases, where the application may want to negotiate video attributes + // before a track is available to send. + // + // The standard way to do this would be through "addTransceiver", but we + // don't support that API yet. + // + // |kind| must be "audio" or "video". + // + // |stream_id| is used to populate the msid attribute; if empty, one will + // be generated automatically. + // + // This method is not supported with kUnifiedPlan semantics. Please use + // AddTransceiver instead. + virtual rtc::scoped_refptr CreateSender( + const std::string& kind, + const std::string& stream_id) = 0; + + // If Plan B semantics are specified, gets all RtpSenders, created either + // through AddStream, AddTrack, or CreateSender. All senders of a specific + // media type share the same media description. + // + // If Unified Plan semantics are specified, gets the RtpSender for each + // RtpTransceiver. + virtual std::vector> GetSenders() + const = 0; + + // If Plan B semantics are specified, gets all RtpReceivers created when a + // remote description is applied. All receivers of a specific media type share + // the same media description. It is also possible to have a media description + // with no associated RtpReceivers, if the directional attribute does not + // indicate that the remote peer is sending any media. + // + // If Unified Plan semantics are specified, gets the RtpReceiver for each + // RtpTransceiver. + virtual std::vector> GetReceivers() + const = 0; + + // Get all RtpTransceivers, created either through AddTransceiver, AddTrack or + // by a remote description applied with SetRemoteDescription. + // + // Note: This method is only available when Unified Plan is enabled (see + // RTCConfiguration). + virtual std::vector> + GetTransceivers() const = 0; + + + // Clear cached stats in the RTCStatsCollector. + // Exposed for testing while waiting for automatic cache clear to work. + // https://bugs.webrtc.org/8693 + virtual void ClearStatsCache() {} + + // Create a data channel with the provided config, or default config if none + // is provided. Note that an offer/answer negotiation is still necessary + // before the data channel can be used. + // + // Also, calling CreateDataChannel is the only way to get a data "m=" section + // in SDP, so it should be done before CreateOffer is called, if the + // application plans to use data channels. + virtual rtc::scoped_refptr CreateDataChannel( + const std::string& label, + const DataChannelInit* config) = 0; + + // Returns the more recently applied description; "pending" if it exists, and + // otherwise "current". See below. + virtual const SessionDescriptionInterface* local_description() const = 0; + virtual const SessionDescriptionInterface* remote_description() const = 0; + + // A "current" description the one currently negotiated from a complete + // offer/answer exchange. + virtual const SessionDescriptionInterface* current_local_description() + const = 0; + virtual const SessionDescriptionInterface* current_remote_description() + const = 0; + + // A "pending" description is one that's part of an incomplete offer/answer + // exchange (thus, either an offer or a pranswer). Once the offer/answer + // exchange is finished, the "pending" description will become "current". + virtual const SessionDescriptionInterface* pending_local_description() + const = 0; + virtual const SessionDescriptionInterface* pending_remote_description() + const = 0; + + // Tells the PeerConnection that ICE should be restarted. This triggers a need + // for negotiation and subsequent CreateOffer() calls will act as if + // RTCOfferAnswerOptions::ice_restart is true. + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-restartice + // TODO(hbos): Remove default implementation when downstream projects + // implement this. + virtual void RestartIce() = 0; + + // Create a new offer. + // The CreateSessionDescriptionObserver callback will be called when done. + virtual void CreateOffer(CreateSessionDescriptionObserver* observer, + const PeerConnectionInterface::RTCOfferAnswerOptions& options) = 0; + + // Create an answer to an offer. + // The CreateSessionDescriptionObserver callback will be called when done. + virtual void CreateAnswer(CreateSessionDescriptionObserver* observer, + const PeerConnectionInterface::RTCOfferAnswerOptions& options) = 0; + + // Sets the local session description. + // The PeerConnection takes the ownership of |desc| even if it fails. + // The |observer| callback will be called when done. + // TODO(deadbeef): Change |desc| to be a unique_ptr, to make it clear + // that this method always takes ownership of it. + virtual void SetLocalDescription(SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc) = 0; + // Implicitly creates an offer or answer (depending on the current signaling + // state) and performs SetLocalDescription() with the newly generated session + // description. + // TODO(hbos): Make pure virtual when implemented by downstream projects. + virtual void SetLocalDescription(SetSessionDescriptionObserver* observer) {} + // Sets the remote session description. + // The PeerConnection takes the ownership of |desc| even if it fails. + // The |observer| callback will be called when done. + // TODO(hbos): Remove when Chrome implements the new signature. + virtual void SetRemoteDescription(SetSessionDescriptionObserver* observer, + SessionDescriptionInterface* desc) {} + virtual void SetRemoteDescription( + std::unique_ptr desc, + rtc::scoped_refptr observer) = 0; + + virtual PeerConnectionInterface::RTCConfiguration GetConfiguration() = 0; + + // Sets the PeerConnection's global configuration to |config|. + // + // The members of |config| that may be changed are |type|, |servers|, + // |ice_candidate_pool_size| and |prune_turn_ports| (though the candidate + // pool size can't be changed after the first call to SetLocalDescription). + // Note that this means the BUNDLE and RTCP-multiplexing policies cannot be + // changed with this method. + // + // Any changes to STUN/TURN servers or ICE candidate policy will affect the + // next gathering phase, and cause the next call to createOffer to generate + // new ICE credentials, as described in JSEP. This also occurs when + // |prune_turn_ports| changes, for the same reasoning. + // + // If an error occurs, returns false and populates |error| if non-null: + // - INVALID_MODIFICATION if |config| contains a modified parameter other + // than one of the parameters listed above. + // - INVALID_RANGE if |ice_candidate_pool_size| is out of range. + // - SYNTAX_ERROR if parsing an ICE server URL failed. + // - INVALID_PARAMETER if a TURN server is missing |username| or |password|. + // - INTERNAL_ERROR if an unexpected error occurred. + // + // TODO(nisse): Make this pure virtual once all Chrome subclasses of + // PeerConnectionInterface implement it. + virtual RTCError SetConfiguration( + const PeerConnectionInterface::RTCConfiguration& config); + + // Provides a remote candidate to the ICE Agent. + // A copy of the |candidate| will be created and added to the remote + // description. So the caller of this method still has the ownership of the + // |candidate|. + // TODO(hbos): The spec mandates chaining this operation onto the operations + // chain; deprecate and remove this version in favor of the callback-based + // signature. + virtual bool AddIceCandidate(const IceCandidateInterface* candidate) = 0; + // TODO(hbos): Remove default implementation once implemented by downstream + // projects. + virtual void AddIceCandidate(std::unique_ptr candidate, + std::function callback) {} + + // Removes a group of remote candidates from the ICE agent. Needed mainly for + // continual gathering, to avoid an ever-growing list of candidates as + // networks come and go. + virtual bool RemoveIceCandidates( + const std::vector& candidates) = 0; + + // SetBitrate limits the bandwidth allocated for all RTP streams sent by + // this PeerConnection. Other limitations might affect these limits and + // are respected (for example "b=AS" in SDP). + // + // Setting |current_bitrate_bps| will reset the current bitrate estimate + // to the provided value. + virtual RTCError SetBitrate(const BitrateSettings& bitrate); + + // TODO(nisse): Deprecated - use version above. These two default + // implementations require subclasses to implement one or the other + // of the methods. + virtual RTCError SetBitrate(const PeerConnectionInterface::BitrateParameters& bitrate_parameters); + + // Enable/disable playout of received audio streams. Enabled by default. Note + // that even if playout is enabled, streams will only be played out if the + // appropriate SDP is also applied. Setting |playout| to false will stop + // playout of the underlying audio device but starts a task which will poll + // for audio data every 10ms to ensure that audio processing happens and the + // audio statistics are updated. + // TODO(henrika): deprecate and remove this. + virtual void SetAudioPlayout(bool playout) {} + + // Enable/disable recording of transmitted audio streams. Enabled by default. + // Note that even if recording is enabled, streams will only be recorded if + // the appropriate SDP is also applied. + // TODO(henrika): deprecate and remove this. + virtual void SetAudioRecording(bool recording) {} + + // Looks up the DtlsTransport associated with a MID value. + // In the Javascript API, DtlsTransport is a property of a sender, but + // because the PeerConnection owns the DtlsTransport in this implementation, + // it is better to look them up on the PeerConnection. + virtual rtc::scoped_refptr LookupDtlsTransportByMid( + const std::string& mid) = 0; + + // Returns the SCTP transport, if any. + virtual rtc::scoped_refptr GetSctpTransport() + const = 0; + + // Returns the current SignalingState. + virtual PeerConnectionInterface::SignalingState signaling_state() = 0; + + // Returns an aggregate state of all ICE *and* DTLS transports. + // This is left in place to avoid breaking native clients who expect our old, + // nonstandard behavior. + // TODO(jonasolsson): deprecate and remove this. + virtual PeerConnectionInterface::IceConnectionState ice_connection_state() = 0; + + // Returns an aggregated state of all ICE transports. + virtual PeerConnectionInterface::IceConnectionState standardized_ice_connection_state() = 0; + + // Returns an aggregated state of all ICE and DTLS transports. + virtual PeerConnectionInterface::PeerConnectionState peer_connection_state() = 0; + + virtual PeerConnectionInterface::IceGatheringState ice_gathering_state() = 0; + + // Start RtcEventLog using an existing output-sink. Takes ownership of + // |output| and passes it on to Call, which will take the ownership. If the + // operation fails the output will be closed and deallocated. The event log + // will send serialized events to the output object every |output_period_ms|. + // Applications using the event log should generally make their own trade-off + // regarding the output period. A long period is generally more efficient, + // with potential drawbacks being more bursty thread usage, and more events + // lost in case the application crashes. If the |output_period_ms| argument is + // omitted, webrtc selects a default deemed to be workable in most cases. + virtual bool StartRtcEventLog(std::unique_ptr output, + int64_t output_period_ms) = 0; + virtual bool StartRtcEventLog(std::unique_ptr output) = 0; + + // Stops logging the RtcEventLog. + virtual void StopRtcEventLog() = 0; + + // Terminates all media, closes the transports, and in general releases any + // resources used by the PeerConnection. This is an irreversible operation. + // + // Note that after this method completes, the PeerConnection will no longer + // use the PeerConnectionObserver interface passed in on construction, and + // thus the observer object can be safely destroyed. + virtual void Close() = 0; + + protected: + // Dtor protected as objects shouldn't be deleted via this interface. + ~TgPeerConnectionInterface() override = default; +}; + +class TgPeerConnection : public TgPeerConnectionInterface, public TgJsepTransportController::Observer, -public TgRtpSenderBase::SetStreamsObserver, +public RtpSenderBase::SetStreamsObserver, public rtc::MessageHandler, public sigslot::has_slots<> { public: @@ -458,7 +819,7 @@ public sigslot::has_slots<> { cricket::VideoMediaChannel* video_media_channel() const RTC_RUN_ON(signaling_thread()); - std::vector>> + std::vector>> GetSendersInternal() const RTC_RUN_ON(signaling_thread()); std::vector< rtc::scoped_refptr>> @@ -541,7 +902,7 @@ public sigslot::has_slots<> { const RtpTransceiverInit& init, bool fire_callback = true) RTC_RUN_ON(signaling_thread()); - rtc::scoped_refptr> + rtc::scoped_refptr> CreateSender(cricket::MediaType media_type, const std::string& id, rtc::scoped_refptr track, @@ -555,7 +916,7 @@ public sigslot::has_slots<> { // transceivers. rtc::scoped_refptr> CreateAndAddTransceiver( - rtc::scoped_refptr> sender, + rtc::scoped_refptr> sender, rtc::scoped_refptr> receiver) RTC_RUN_ON(signaling_thread()); @@ -855,12 +1216,12 @@ public sigslot::has_slots<> { RTC_RUN_ON(signaling_thread()); // Return the RtpSender with the given track attached. - rtc::scoped_refptr> + rtc::scoped_refptr> FindSenderForTrack(MediaStreamTrackInterface* track) const RTC_RUN_ON(signaling_thread()); // Return the RtpSender with the given id, or null if none exists. - rtc::scoped_refptr> + rtc::scoped_refptr> FindSenderById(const std::string& sender_id) const RTC_RUN_ON(signaling_thread()); @@ -1139,7 +1500,7 @@ public sigslot::has_slots<> { rtc::scoped_refptr dtls_transport, DataChannelTransportInterface* data_channel_transport) override; - // TgRtpSenderBase::SetStreamsObserver override. + // RtpSenderBase::SetStreamsObserver override. void OnSetStreams() override; // Returns the CryptoOptions for this TgPeerConnection. This will always diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp index 610e66f522..0cfc58193f 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp @@ -48,6 +48,136 @@ namespace webrtc { +rtc::scoped_refptr +TgPeerConnectionFactoryInterface::CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + std::unique_ptr allocator, + std::unique_ptr cert_generator, + PeerConnectionObserver* observer) { + return nullptr; +} + +rtc::scoped_refptr +TgPeerConnectionFactoryInterface::CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies) { + return nullptr; +} + +RtpCapabilities TgPeerConnectionFactoryInterface::GetRtpSenderCapabilities( + cricket::MediaType kind) const { + return {}; +} + +RtpCapabilities TgPeerConnectionFactoryInterface::GetRtpReceiverCapabilities( + cricket::MediaType kind) const { + return {}; +} + +BEGIN_SIGNALING_PROXY_MAP(TgPeerConnection) +PROXY_SIGNALING_THREAD_DESTRUCTOR() +PROXY_METHOD0(rtc::scoped_refptr, local_streams) +PROXY_METHOD0(rtc::scoped_refptr, remote_streams) +PROXY_METHOD1(bool, AddStream, MediaStreamInterface*) +PROXY_METHOD1(void, RemoveStream, MediaStreamInterface*) +PROXY_METHOD2(RTCErrorOr>, + AddTrack, + rtc::scoped_refptr, + const std::vector&) +PROXY_METHOD1(bool, RemoveTrack, RtpSenderInterface*) +PROXY_METHOD1(RTCError, RemoveTrackNew, rtc::scoped_refptr) +PROXY_METHOD1(RTCErrorOr>, + AddTransceiver, + rtc::scoped_refptr) +PROXY_METHOD2(RTCErrorOr>, + AddTransceiver, + rtc::scoped_refptr, + const RtpTransceiverInit&) +PROXY_METHOD1(RTCErrorOr>, + AddTransceiver, + cricket::MediaType) +PROXY_METHOD2(RTCErrorOr>, + AddTransceiver, + cricket::MediaType, + const RtpTransceiverInit&) +PROXY_METHOD2(rtc::scoped_refptr, + CreateSender, + const std::string&, + const std::string&) +PROXY_CONSTMETHOD0(std::vector>, + GetSenders) +PROXY_CONSTMETHOD0(std::vector>, + GetReceivers) +PROXY_CONSTMETHOD0(std::vector>, + GetTransceivers) +PROXY_METHOD0(void, ClearStatsCache) +PROXY_METHOD2(rtc::scoped_refptr, + CreateDataChannel, + const std::string&, + const DataChannelInit*) +PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, local_description) +PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, remote_description) +PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, + current_local_description) +PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, + current_remote_description) +PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, + pending_local_description) +PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, + pending_remote_description) +PROXY_METHOD0(void, RestartIce) +PROXY_METHOD2(void, + CreateOffer, + CreateSessionDescriptionObserver*, + const PeerConnectionInterface::RTCOfferAnswerOptions&) +PROXY_METHOD2(void, + CreateAnswer, + CreateSessionDescriptionObserver*, + const PeerConnectionInterface::RTCOfferAnswerOptions&) +PROXY_METHOD2(void, + SetLocalDescription, + SetSessionDescriptionObserver*, + SessionDescriptionInterface*) +PROXY_METHOD1(void, SetLocalDescription, SetSessionDescriptionObserver*) +PROXY_METHOD2(void, + SetRemoteDescription, + SetSessionDescriptionObserver*, + SessionDescriptionInterface*) +PROXY_METHOD2(void, + SetRemoteDescription, + std::unique_ptr, + rtc::scoped_refptr) +PROXY_METHOD0(PeerConnectionInterface::RTCConfiguration, GetConfiguration) +PROXY_METHOD1(RTCError, + SetConfiguration, + const PeerConnectionInterface::RTCConfiguration&) +PROXY_METHOD1(bool, AddIceCandidate, const IceCandidateInterface*) +PROXY_METHOD2(void, + AddIceCandidate, + std::unique_ptr, + std::function) +PROXY_METHOD1(bool, RemoveIceCandidates, const std::vector&) +PROXY_METHOD1(RTCError, SetBitrate, const BitrateSettings&) +PROXY_METHOD1(void, SetAudioPlayout, bool) +PROXY_METHOD1(void, SetAudioRecording, bool) +PROXY_METHOD1(rtc::scoped_refptr, + LookupDtlsTransportByMid, + const std::string&) +PROXY_CONSTMETHOD0(rtc::scoped_refptr, GetSctpTransport) +PROXY_METHOD0(PeerConnectionInterface::SignalingState, signaling_state) +PROXY_METHOD0(PeerConnectionInterface::IceConnectionState, ice_connection_state) +PROXY_METHOD0(PeerConnectionInterface::IceConnectionState, standardized_ice_connection_state) +PROXY_METHOD0(PeerConnectionInterface::PeerConnectionState, peer_connection_state) +PROXY_METHOD0(PeerConnectionInterface::IceGatheringState, ice_gathering_state) +PROXY_METHOD2(bool, + StartRtcEventLog, + std::unique_ptr, + int64_t) +PROXY_METHOD1(bool, StartRtcEventLog, std::unique_ptr) +PROXY_METHOD0(void, StopRtcEventLog) +PROXY_METHOD0(void, Close) +END_PROXY_MAP() + TgPeerConnectionFactory::TgPeerConnectionFactory( PeerConnectionFactoryDependencies dependencies) : wraps_current_thread_(false), @@ -210,7 +340,7 @@ void TgPeerConnectionFactory::StopAecDump() { channel_manager_->StopAecDump(); } -rtc::scoped_refptr +rtc::scoped_refptr TgPeerConnectionFactory::CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, std::unique_ptr allocator, @@ -224,7 +354,7 @@ TgPeerConnectionFactory::CreatePeerConnection( return CreatePeerConnection(configuration, std::move(dependencies)); } -rtc::scoped_refptr +rtc::scoped_refptr TgPeerConnectionFactory::CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, PeerConnectionDependencies dependencies) { @@ -284,7 +414,7 @@ TgPeerConnectionFactory::CreatePeerConnection( if (!pc->Initialize(configuration, std::move(dependencies))) { return nullptr; } - return pc; + return TgPeerConnectionProxy::Create(signaling_thread(), pc); } rtc::scoped_refptr diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h index ac6129a861..e97b02a2a4 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h @@ -23,6 +23,7 @@ #include "pc/channel_manager.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/thread.h" +#include "pc/peer_connection_factory.h" namespace rtc { class BasicNetworkManager; @@ -33,18 +34,102 @@ namespace webrtc { class RtcEventLog; class TgPeerConnection; +class TgPeerConnectionInterface; -class TgPeerConnectionFactory: public rtc::RefCountInterface { +class RTC_EXPORT TgPeerConnectionFactoryInterface + : public rtc::RefCountInterface { public: - void SetOptions(const PeerConnectionFactoryInterface::Options& options); + // Set the options to be used for subsequently created PeerConnections. + virtual void SetOptions(const PeerConnectionFactoryInterface::Options& options) = 0; - rtc::scoped_refptr CreatePeerConnection( + // The preferred way to create a new peer connection. Simply provide the + // configuration and a PeerConnectionDependencies structure. + // TODO(benwright): Make pure virtual once downstream mock PC factory classes + // are updated. + virtual rtc::scoped_refptr CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + PeerConnectionDependencies dependencies); + + // Deprecated; |allocator| and |cert_generator| may be null, in which case + // default implementations will be used. + // + // |observer| must not be null. + // + // Note that this method does not take ownership of |observer|; it's the + // responsibility of the caller to delete it. It can be safely deleted after + // Close has been called on the returned PeerConnection, which ensures no + // more observer callbacks will be invoked. + virtual rtc::scoped_refptr CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, std::unique_ptr allocator, std::unique_ptr cert_generator, PeerConnectionObserver* observer); - rtc::scoped_refptr CreatePeerConnection( + // Returns the capabilities of an RTP sender of type |kind|. + // If for some reason you pass in MEDIA_TYPE_DATA, returns an empty structure. + // TODO(orphis): Make pure virtual when all subclasses implement it. + virtual RtpCapabilities GetRtpSenderCapabilities( + cricket::MediaType kind) const; + + // Returns the capabilities of an RTP receiver of type |kind|. + // If for some reason you pass in MEDIA_TYPE_DATA, returns an empty structure. + // TODO(orphis): Make pure virtual when all subclasses implement it. + virtual RtpCapabilities GetRtpReceiverCapabilities( + cricket::MediaType kind) const; + + virtual rtc::scoped_refptr CreateLocalMediaStream( + const std::string& stream_id) = 0; + + // Creates an AudioSourceInterface. + // |options| decides audio processing settings. + virtual rtc::scoped_refptr CreateAudioSource( + const cricket::AudioOptions& options) = 0; + + // Creates a new local VideoTrack. The same |source| can be used in several + // tracks. + virtual rtc::scoped_refptr CreateVideoTrack( + const std::string& label, + VideoTrackSourceInterface* source) = 0; + + // Creates an new AudioTrack. At the moment |source| can be null. + virtual rtc::scoped_refptr CreateAudioTrack( + const std::string& label, + AudioSourceInterface* source) = 0; + + // Starts AEC dump using existing file. Takes ownership of |file| and passes + // it on to VoiceEngine (via other objects) immediately, which will take + // the ownerhip. If the operation fails, the file will be closed. + // A maximum file size in bytes can be specified. When the file size limit is + // reached, logging is stopped automatically. If max_size_bytes is set to a + // value <= 0, no limit will be used, and logging will continue until the + // StopAecDump function is called. + // TODO(webrtc:6463): Delete default implementation when downstream mocks + // classes are updated. + virtual bool StartAecDump(FILE* file, int64_t max_size_bytes) { + return false; + } + + // Stops logging the AEC dump. + virtual void StopAecDump() = 0; + + protected: + // Dtor and ctor protected as objects shouldn't be created or deleted via + // this interface. + TgPeerConnectionFactoryInterface() {} + ~TgPeerConnectionFactoryInterface() override = default; +}; + +class TgPeerConnectionFactory: public TgPeerConnectionFactoryInterface { + public: + void SetOptions(const PeerConnectionFactoryInterface::Options& options); + + rtc::scoped_refptr CreatePeerConnection( + const PeerConnectionInterface::RTCConfiguration& configuration, + std::unique_ptr allocator, + std::unique_ptr cert_generator, + PeerConnectionObserver* observer); + + rtc::scoped_refptr CreatePeerConnection( const PeerConnectionInterface::RTCConfiguration& configuration, PeerConnectionDependencies dependencies); @@ -134,6 +219,43 @@ class TgPeerConnectionFactory: public rtc::RefCountInterface { const std::unique_ptr trials_; }; +BEGIN_SIGNALING_PROXY_MAP(TgPeerConnectionFactory) +PROXY_SIGNALING_THREAD_DESTRUCTOR() +PROXY_METHOD1(void, SetOptions, const PeerConnectionFactory::Options&) +PROXY_METHOD4(rtc::scoped_refptr, + CreatePeerConnection, + const PeerConnectionInterface::RTCConfiguration&, + std::unique_ptr, + std::unique_ptr, + PeerConnectionObserver*) +PROXY_METHOD2(rtc::scoped_refptr, + CreatePeerConnection, + const PeerConnectionInterface::RTCConfiguration&, + PeerConnectionDependencies) +PROXY_CONSTMETHOD1(webrtc::RtpCapabilities, + GetRtpSenderCapabilities, + cricket::MediaType) +PROXY_CONSTMETHOD1(webrtc::RtpCapabilities, + GetRtpReceiverCapabilities, + cricket::MediaType) +PROXY_METHOD1(rtc::scoped_refptr, + CreateLocalMediaStream, + const std::string&) +PROXY_METHOD1(rtc::scoped_refptr, + CreateAudioSource, + const cricket::AudioOptions&) +PROXY_METHOD2(rtc::scoped_refptr, + CreateVideoTrack, + const std::string&, + VideoTrackSourceInterface*) +PROXY_METHOD2(rtc::scoped_refptr, + CreateAudioTrack, + const std::string&, + AudioSourceInterface*) +PROXY_METHOD2(bool, StartAecDump, FILE*, int64_t) +PROXY_METHOD0(void, StopAecDump) +END_PROXY_MAP() + } // namespace webrtc #endif // PC_PEER_CONNECTION_FACTORY_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp index 9c95b1913f..a784e3814c 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp @@ -73,15 +73,7 @@ TgRtpDataMediaChannel::~TgRtpDataMediaChannel() { } } -void RTC_NO_SANITIZE("float-cast-overflow") // bugs.webrtc.org/8204 - RtpClock::Tick(double now, int* seq_num, uint32_t* timestamp) { - *seq_num = ++last_seq_num_; - *timestamp = timestamp_offset_ + static_cast(now * clockrate_); - // UBSan: 5.92374e+10 is outside the range of representable values of type - // 'unsigned int' -} - -const DataCodec* FindUnknownCodec(const std::vector& codecs) { +const DataCodec* TgFindUnknownCodec(const std::vector& codecs) { DataCodec data_codec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName); std::vector::const_iterator iter; for (iter = codecs.begin(); iter != codecs.end(); ++iter) { @@ -92,7 +84,7 @@ const DataCodec* FindUnknownCodec(const std::vector& codecs) { return NULL; } -const DataCodec* FindKnownCodec(const std::vector& codecs) { +const DataCodec* TgFindKnownCodec(const std::vector& codecs) { DataCodec data_codec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName); std::vector::const_iterator iter; for (iter = codecs.begin(); iter != codecs.end(); ++iter) { @@ -104,7 +96,7 @@ const DataCodec* FindKnownCodec(const std::vector& codecs) { } bool TgRtpDataMediaChannel::SetRecvCodecs(const std::vector& codecs) { - const DataCodec* unknown_codec = FindUnknownCodec(codecs); + const DataCodec* unknown_codec = TgFindUnknownCodec(codecs); if (unknown_codec) { RTC_LOG(LS_WARNING) << "Failed to SetRecvCodecs because of unknown codec: " << unknown_codec->ToString(); @@ -116,7 +108,7 @@ bool TgRtpDataMediaChannel::SetRecvCodecs(const std::vector& codecs) } bool TgRtpDataMediaChannel::SetSendCodecs(const std::vector& codecs) { - const DataCodec* known_codec = FindKnownCodec(codecs); + const DataCodec* known_codec = TgFindKnownCodec(codecs); if (!known_codec) { RTC_LOG(LS_WARNING) << "Failed to SetSendCodecs because there is no known codec."; diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp index 0d57d3d3eb..f612cf3eb4 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp @@ -90,7 +90,7 @@ RtpParameters RestoreEncodingLayers( // Returns true if any RtpParameters member that isn't implemented contains a // value. -bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) { +bool TgUnimplementedRtpParameterHasValue(const RtpParameters& parameters) { if (!parameters.mid.empty()) { return true; } @@ -105,265 +105,6 @@ bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) { return false; } -TgRtpSenderBase::TgRtpSenderBase(rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer) - : worker_thread_(worker_thread), - id_(id), - set_streams_observer_(set_streams_observer) { - RTC_DCHECK(worker_thread); - init_parameters_.encodings.emplace_back(); -} - -void TgRtpSenderBase::SetFrameEncryptor( - rtc::scoped_refptr frame_encryptor) { - frame_encryptor_ = std::move(frame_encryptor); - // Special Case: Set the frame encryptor to any value on any existing channel. - if (media_channel_ && ssrc_ && !stopped_) { - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - media_channel_->SetFrameEncryptor(ssrc_, frame_encryptor_); - }); - } -} - -void TgRtpSenderBase::SetMediaChannel(cricket::MediaChannel* media_channel) { - RTC_DCHECK(media_channel == nullptr || - media_channel->media_type() == media_type()); - media_channel_ = media_channel; -} - -RtpParameters TgRtpSenderBase::GetParametersInternal() const { - if (stopped_) { - return RtpParameters(); - } - if (!media_channel_ || !ssrc_) { - return init_parameters_; - } - return worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_); - RemoveEncodingLayers(disabled_rids_, &result.encodings); - return result; - }); -} - -RtpParameters TgRtpSenderBase::GetParameters() const { - RtpParameters result = GetParametersInternal(); - last_transaction_id_ = rtc::CreateRandomUuid(); - result.transaction_id = last_transaction_id_.value(); - return result; -} - -RTCError TgRtpSenderBase::SetParametersInternal(const RtpParameters& parameters) { - RTC_DCHECK(!stopped_); - - if (UnimplementedRtpParameterHasValue(parameters)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); - } - if (!media_channel_ || !ssrc_) { - auto result = cricket::CheckRtpParametersInvalidModificationAndValues( - init_parameters_, parameters); - if (result.ok()) { - init_parameters_ = parameters; - } - return result; - } - return worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RtpParameters rtp_parameters = parameters; - if (!disabled_rids_.empty()) { - // Need to add the inactive layers. - RtpParameters old_parameters = - media_channel_->GetRtpSendParameters(ssrc_); - rtp_parameters = RestoreEncodingLayers(parameters, disabled_rids_, - old_parameters.encodings); - } - return media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters); - }); -} - -RTCError TgRtpSenderBase::SetParameters(const RtpParameters& parameters) { - TRACE_EVENT0("webrtc", "TgRtpSenderBase::SetParameters"); - if (stopped_) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "Cannot set parameters on a stopped sender."); - } - if (!last_transaction_id_) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_STATE, - "Failed to set parameters since getParameters() has never been called" - " on this sender"); - } - if (last_transaction_id_ != parameters.transaction_id) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Failed to set parameters since the transaction_id doesn't match" - " the last value returned from getParameters()"); - } - - RTCError result = SetParametersInternal(parameters); - last_transaction_id_.reset(); - return result; -} - -void TgRtpSenderBase::SetStreams(const std::vector& stream_ids) { - set_stream_ids(stream_ids); - if (set_streams_observer_) - set_streams_observer_->OnSetStreams(); -} - -bool TgRtpSenderBase::SetTrack(MediaStreamTrackInterface* track) { - TRACE_EVENT0("webrtc", "TgRtpSenderBase::SetTrack"); - if (stopped_) { - RTC_LOG(LS_ERROR) << "SetTrack can't be called on a stopped RtpSender."; - return false; - } - if (track && track->kind() != track_kind()) { - RTC_LOG(LS_ERROR) << "SetTrack with " << track->kind() - << " called on RtpSender with " << track_kind() - << " track."; - return false; - } - - // Detach from old track. - if (track_) { - DetachTrack(); - track_->UnregisterObserver(this); - RemoveTrackFromStats(); - } - - // Attach to new track. - bool prev_can_send_track = can_send_track(); - // Keep a reference to the old track to keep it alive until we call SetSend. - rtc::scoped_refptr old_track = track_; - track_ = track; - if (track_) { - track_->RegisterObserver(this); - AttachTrack(); - } - - // Update channel. - if (can_send_track()) { - SetSend(); - AddTrackToStats(); - } else if (prev_can_send_track) { - ClearSend(); - } - attachment_id_ = (track_ ? GenerateUniqueId() : 0); - return true; -} - -void TgRtpSenderBase::SetSsrc(uint32_t ssrc) { - TRACE_EVENT0("webrtc", "TgRtpSenderBase::SetSsrc"); - if (stopped_ || ssrc == ssrc_) { - return; - } - // If we are already sending with a particular SSRC, stop sending. - if (can_send_track()) { - ClearSend(); - RemoveTrackFromStats(); - } - ssrc_ = ssrc; - if (can_send_track()) { - SetSend(); - AddTrackToStats(); - } - if (!init_parameters_.encodings.empty()) { - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK(media_channel_); - // Get the current parameters, which are constructed from the SDP. - // The number of layers in the SDP is currently authoritative to support - // SDP munging for Plan-B simulcast with "a=ssrc-group:SIM ..." - // lines as described in RFC 5576. - // All fields should be default constructed and the SSRC field set, which - // we need to copy. - RtpParameters current_parameters = - media_channel_->GetRtpSendParameters(ssrc_); - RTC_DCHECK_GE(current_parameters.encodings.size(), - init_parameters_.encodings.size()); - for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) { - init_parameters_.encodings[i].ssrc = - current_parameters.encodings[i].ssrc; - init_parameters_.encodings[i].rid = current_parameters.encodings[i].rid; - current_parameters.encodings[i] = init_parameters_.encodings[i]; - } - current_parameters.degradation_preference = - init_parameters_.degradation_preference; - media_channel_->SetRtpSendParameters(ssrc_, current_parameters); - init_parameters_.encodings.clear(); - }); - } - // Attempt to attach the frame decryptor to the current media channel. - if (frame_encryptor_) { - SetFrameEncryptor(frame_encryptor_); - } -} - -void TgRtpSenderBase::Stop() { - TRACE_EVENT0("webrtc", "TgRtpSenderBase::Stop"); - // TODO(deadbeef): Need to do more here to fully stop sending packets. - if (stopped_) { - return; - } - if (track_) { - DetachTrack(); - track_->UnregisterObserver(this); - } - if (can_send_track()) { - ClearSend(); - RemoveTrackFromStats(); - } - media_channel_ = nullptr; - set_streams_observer_ = nullptr; - stopped_ = true; -} - -RTCError TgRtpSenderBase::DisableEncodingLayers( - const std::vector& rids) { - if (stopped_) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "Cannot disable encodings on a stopped sender."); - } - - if (rids.empty()) { - return RTCError::OK(); - } - - // Check that all the specified layers exist and disable them in the channel. - RtpParameters parameters = GetParametersInternal(); - for (const std::string& rid : rids) { - if (absl::c_none_of(parameters.encodings, - [&rid](const RtpEncodingParameters& encoding) { - return encoding.rid == rid; - })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "RID: " + rid + " does not refer to a valid layer."); - } - } - - if (!media_channel_ || !ssrc_) { - RemoveEncodingLayers(rids, &init_parameters_.encodings); - // Invalidate any transaction upon success. - last_transaction_id_.reset(); - return RTCError::OK(); - } - - for (RtpEncodingParameters& encoding : parameters.encodings) { - // Remain active if not in the disable list. - encoding.active &= absl::c_none_of( - rids, - [&encoding](const std::string& rid) { return encoding.rid == rid; }); - } - - RTCError result = SetParametersInternal(parameters); - if (result.ok()) { - disabled_rids_.insert(disabled_rids_.end(), rids.begin(), rids.end()); - // Invalidate any transaction upon success. - last_transaction_id_.reset(); - } - return result; -} - TgLocalAudioSinkAdapter::TgLocalAudioSinkAdapter() : sink_(nullptr) {} TgLocalAudioSinkAdapter::~TgLocalAudioSinkAdapter() { @@ -393,19 +134,16 @@ void TgLocalAudioSinkAdapter::SetSink(cricket::AudioSource::Sink* sink) { rtc::scoped_refptr TgAudioRtpSender::Create( rtc::Thread* worker_thread, const std::string& id, - StatsCollector* stats, SetStreamsObserver* set_streams_observer) { return rtc::scoped_refptr( - new rtc::RefCountedObject(worker_thread, id, stats, + new rtc::RefCountedObject(worker_thread, id, set_streams_observer)); } TgAudioRtpSender::TgAudioRtpSender(rtc::Thread* worker_thread, const std::string& id, - StatsCollector* stats, SetStreamsObserver* set_streams_observer) - : TgRtpSenderBase(worker_thread, id, set_streams_observer), - stats_(stats), + : RtpSenderBase(worker_thread, id, set_streams_observer), dtmf_sender_proxy_(DtmfSenderProxy::Create( rtc::Thread::Current(), DtmfSender::Create(rtc::Thread::Current(), this))), @@ -477,15 +215,9 @@ void TgAudioRtpSender::AttachTrack() { } void TgAudioRtpSender::AddTrackToStats() { - if (can_send_track() && stats_) { - stats_->AddLocalAudioTrack(audio_track().get(), ssrc_); - } } void TgAudioRtpSender::RemoveTrackFromStats() { - if (can_send_track() && stats_) { - stats_->RemoveLocalAudioTrack(audio_track().get(), ssrc_); - } } rtc::scoped_refptr TgAudioRtpSender::GetDtmfSender() const { @@ -550,7 +282,7 @@ rtc::scoped_refptr TgVideoRtpSender::Create( TgVideoRtpSender::TgVideoRtpSender(rtc::Thread* worker_thread, const std::string& id, SetStreamsObserver* set_streams_observer) - : TgRtpSenderBase(worker_thread, id, set_streams_observer) {} + : RtpSenderBase(worker_thread, id, set_streams_observer) {} TgVideoRtpSender::~TgVideoRtpSender() { Stop(); diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h index 3ff5ed65d9..0822bd4ce4 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h +++ b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h @@ -26,178 +26,13 @@ #include "pc/dtmf_sender.h" #include "rtc_base/critical_section.h" +#include "pc/rtp_sender.h" + namespace webrtc { class StatsCollector; -bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters); - -// Internal interface used by PeerConnection. -class TgRtpSenderInternal : public RtpSenderInterface { - public: - // Sets the underlying MediaEngine channel associated with this RtpSender. - // A VoiceMediaChannel should be used for audio RtpSenders and - // a VideoMediaChannel should be used for video RtpSenders. - // Must call SetMediaChannel(nullptr) before the media channel is destroyed. - virtual void SetMediaChannel(cricket::MediaChannel* media_channel) = 0; - - // Used to set the SSRC of the sender, once a local description has been set. - // If |ssrc| is 0, this indiates that the sender should disconnect from the - // underlying transport (this occurs if the sender isn't seen in a local - // description). - virtual void SetSsrc(uint32_t ssrc) = 0; - - virtual void set_stream_ids(const std::vector& stream_ids) = 0; - virtual void set_init_send_encodings( - const std::vector& init_send_encodings) = 0; - virtual void set_transport( - rtc::scoped_refptr dtls_transport) = 0; - - virtual void Stop() = 0; - - // |GetParameters| and |SetParameters| operate with a transactional model. - // Allow access to get/set parameters without invalidating transaction id. - virtual RtpParameters GetParametersInternal() const = 0; - virtual RTCError SetParametersInternal(const RtpParameters& parameters) = 0; - - // Returns an ID that changes every time SetTrack() is called, but - // otherwise remains constant. Used to generate IDs for stats. - // The special value zero means that no track is attached. - virtual int AttachmentId() const = 0; - - // Disables the layers identified by the specified RIDs. - // If the specified list is empty, this is a no-op. - virtual RTCError DisableEncodingLayers( - const std::vector& rid) = 0; -}; - -// Shared implementation for RtpSenderInternal interface. -class TgRtpSenderBase : public TgRtpSenderInternal, public ObserverInterface { - public: - class SetStreamsObserver { - public: - virtual ~SetStreamsObserver() = default; - virtual void OnSetStreams() = 0; - }; - - // Sets the underlying MediaEngine channel associated with this RtpSender. - // A VoiceMediaChannel should be used for audio RtpSenders and - // a VideoMediaChannel should be used for video RtpSenders. - // Must call SetMediaChannel(nullptr) before the media channel is destroyed. - void SetMediaChannel(cricket::MediaChannel* media_channel) override; - - bool SetTrack(MediaStreamTrackInterface* track) override; - rtc::scoped_refptr track() const override { - return track_; - } - - RtpParameters GetParameters() const override; - RTCError SetParameters(const RtpParameters& parameters) override; - - // |GetParameters| and |SetParameters| operate with a transactional model. - // Allow access to get/set parameters without invalidating transaction id. - RtpParameters GetParametersInternal() const override; - RTCError SetParametersInternal(const RtpParameters& parameters) override; - - // Used to set the SSRC of the sender, once a local description has been set. - // If |ssrc| is 0, this indiates that the sender should disconnect from the - // underlying transport (this occurs if the sender isn't seen in a local - // description). - void SetSsrc(uint32_t ssrc) override; - uint32_t ssrc() const override { return ssrc_; } - - std::vector stream_ids() const override { return stream_ids_; } - void set_stream_ids(const std::vector& stream_ids) override { - stream_ids_ = stream_ids; - } - void SetStreams(const std::vector& stream_ids) override; - - std::string id() const override { return id_; } - - void set_init_send_encodings( - const std::vector& init_send_encodings) override { - init_parameters_.encodings = init_send_encodings; - } - std::vector init_send_encodings() const override { - return init_parameters_.encodings; - } - - void set_transport( - rtc::scoped_refptr dtls_transport) override { - dtls_transport_ = dtls_transport; - } - rtc::scoped_refptr dtls_transport() const override { - return dtls_transport_; - } - - void SetFrameEncryptor( - rtc::scoped_refptr frame_encryptor) override; - - rtc::scoped_refptr GetFrameEncryptor() - const override { - return frame_encryptor_; - } - - void Stop() override; - - // Returns an ID that changes every time SetTrack() is called, but - // otherwise remains constant. Used to generate IDs for stats. - // The special value zero means that no track is attached. - int AttachmentId() const override { return attachment_id_; } - - // Disables the layers identified by the specified RIDs. - // If the specified list is empty, this is a no-op. - RTCError DisableEncodingLayers(const std::vector& rid) override; - - protected: - // If |set_streams_observer| is not null, it is invoked when SetStreams() - // is called. |set_streams_observer| is not owned by this object. If not - // null, it must be valid at least until this sender becomes stopped. - TgRtpSenderBase(rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer); - // TODO(nisse): Since SSRC == 0 is technically valid, figure out - // some other way to test if we have a valid SSRC. - bool can_send_track() const { return track_ && ssrc_; } - - virtual std::string track_kind() const = 0; - - // Enable sending on the media channel. - virtual void SetSend() = 0; - // Disable sending on the media channel. - virtual void ClearSend() = 0; - - // Template method pattern to allow subclasses to add custom behavior for - // when tracks are attached, detached, and for adding tracks to statistics. - virtual void AttachTrack() {} - virtual void DetachTrack() {} - virtual void AddTrackToStats() {} - virtual void RemoveTrackFromStats() {} - - rtc::Thread* worker_thread_; - uint32_t ssrc_ = 0; - bool stopped_ = false; - int attachment_id_ = 0; - const std::string id_; - - std::vector stream_ids_; - RtpParameters init_parameters_; - - cricket::MediaChannel* media_channel_ = nullptr; - rtc::scoped_refptr track_; - - rtc::scoped_refptr dtls_transport_; - rtc::scoped_refptr frame_encryptor_; - // |last_transaction_id_| is used to verify that |SetParameters| is receiving - // the parameters object that was last returned from |GetParameters|. - // As such, it is used for internal verification and is not observable by the - // the client. It is marked as mutable to enable |GetParameters| to be a - // const method. - mutable absl::optional last_transaction_id_; - std::vector disabled_rids_; - - SetStreamsObserver* set_streams_observer_ = nullptr; -}; +bool TgUnimplementedRtpParameterHasValue(const RtpParameters& parameters); // TgLocalAudioSinkAdapter receives data callback as a sink to the local // AudioTrack, and passes the data to the sink of AudioSource. @@ -223,7 +58,7 @@ class TgLocalAudioSinkAdapter : public AudioTrackSinkInterface, rtc::CriticalSection lock_; }; -class TgAudioRtpSender : public DtmfProviderInterface, public TgRtpSenderBase { +class TgAudioRtpSender : public DtmfProviderInterface, public RtpSenderBase { public: // Construct an RtpSender for audio with the given sender ID. // The sender is initialized with no track to send and no associated streams. @@ -235,7 +70,6 @@ class TgAudioRtpSender : public DtmfProviderInterface, public TgRtpSenderBase { static rtc::scoped_refptr Create( rtc::Thread* worker_thread, const std::string& id, - StatsCollector* stats, SetStreamsObserver* set_streams_observer); virtual ~TgAudioRtpSender(); @@ -259,7 +93,6 @@ class TgAudioRtpSender : public DtmfProviderInterface, public TgRtpSenderBase { protected: TgAudioRtpSender(rtc::Thread* worker_thread, const std::string& id, - StatsCollector* stats, SetStreamsObserver* set_streams_observer); void SetSend() override; @@ -281,7 +114,6 @@ class TgAudioRtpSender : public DtmfProviderInterface, public TgRtpSenderBase { } sigslot::signal0<> SignalDestroyed; - StatsCollector* stats_ = nullptr; rtc::scoped_refptr dtmf_sender_proxy_; bool cached_track_enabled_ = false; @@ -290,7 +122,7 @@ class TgAudioRtpSender : public DtmfProviderInterface, public TgRtpSenderBase { std::unique_ptr sink_adapter_; }; -class TgVideoRtpSender : public TgRtpSenderBase { +class TgVideoRtpSender : public RtpSenderBase { public: // Construct an RtpSender for video with the given sender ID. // The sender is initialized with no track to send and no associated streams. From 20246f9b2fb522624fe33a4f56becf6dbb75573e Mon Sep 17 00:00:00 2001 From: Ali <> Date: Mon, 8 Jun 2020 22:13:41 +0400 Subject: [PATCH 5/8] Temp --- submodules/TelegramVoip/BUILD | 1 + .../Sources/OngoingCallContext.swift | 35 +++---- submodules/TgVoip/BUILD | 17 ++++ submodules/TgVoipWebrtc/BUILD | 1 + submodules/TgVoipWebrtc/Impl/Controller.cpp | 33 +------ submodules/TgVoipWebrtc/Impl/Controller.h | 11 --- .../TgVoipWebrtc/Impl/MediaEngineBase.h | 2 - .../TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp | 93 +++++-------------- .../TgVoipWebrtc/Impl/MediaEngineWebrtc.h | 7 +- 9 files changed, 66 insertions(+), 134 deletions(-) diff --git a/submodules/TelegramVoip/BUILD b/submodules/TelegramVoip/BUILD index f7a03155f3..d3c64da09d 100644 --- a/submodules/TelegramVoip/BUILD +++ b/submodules/TelegramVoip/BUILD @@ -14,6 +14,7 @@ swift_library( "//submodules/TelegramUIPreferences:TelegramUIPreferences", "//submodules/TgVoip:TgVoip", "//submodules/TgVoipWebrtcCustom:TgVoipWebrtcCustom", + "//submodules/TgVoipWebrtc:TgVoipWebrtc", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 1dbdf317c4..d6cd87c809 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -7,16 +7,16 @@ import Postbox import TelegramUIPreferences import TgVoip -//import TgVoipWebrtc +import TgVoipWebrtc import TgVoipWebrtcCustom private func callConnectionDescription(_ connection: CallSessionConnection) -> OngoingCallConnectionDescription { return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) } -/*private func callConnectionDescriptionWebrtc(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtc { +private func callConnectionDescriptionWebrtc(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtc { return OngoingCallConnectionDescriptionWebrtc(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) -}*/ +} private func callConnectionDescriptionWebrtcCustom(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtcCustom { return OngoingCallConnectionDescriptionWebrtcCustom(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) @@ -80,11 +80,11 @@ private let setupLogs: Bool = { Logger.shared.log("TGVOIP", value) } }) - /*OngoingCallThreadLocalContextWebrtc.setupLoggingFunction({ value in + OngoingCallThreadLocalContextWebrtc.setupLoggingFunction({ value in if let value = value { Logger.shared.log("TGVOIP", value) } - })*/ + }) OngoingCallThreadLocalContextWebrtcCustom.setupLoggingFunction({ value in if let value = value { Logger.shared.log("TGVOIP", value) @@ -100,7 +100,7 @@ public enum OngoingCallContextState { case failed } -private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue/*, OngoingCallThreadLocalContextQueueWebrtc*/ , OngoingCallThreadLocalContextQueueWebrtcCustom { +private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue, OngoingCallThreadLocalContextQueueWebrtc , OngoingCallThreadLocalContextQueueWebrtcCustom { private let queue: Queue init(queue: Queue) { @@ -144,7 +144,7 @@ private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetwor } } -/*private func ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtc { +private func ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtc { switch type { case .none: return .wifi @@ -162,7 +162,7 @@ private func ongoingNetworkTypeForType(_ type: NetworkType) -> OngoingCallNetwor return .cellularLte } } -}*/ +} private func ongoingNetworkTypeForTypeWebrtcCustom(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtcCustom { switch type { @@ -197,7 +197,7 @@ private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCal } } -/*private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtc { +private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtc { switch type { case .never: return .never @@ -208,7 +208,7 @@ private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCal default: return .never } -}*/ +} private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtcCustom { switch type { @@ -266,7 +266,7 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol { } } -/*extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProtocol { +extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProtocol { func nativeSetNetworkType(_ type: NetworkType) { self.setNetworkType(ongoingNetworkTypeForTypeWebrtc(type)) } @@ -290,7 +290,7 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol { func nativeGetDerivedState() -> Data { return self.getDerivedState() } -}*/ +} extension OngoingCallThreadLocalContextWebrtcCustom: OngoingCallThreadLocalContextProtocol { func nativeSetNetworkType(_ type: NetworkType) { @@ -335,7 +335,7 @@ private extension OngoingCallContextState { } } -/*private extension OngoingCallContextState { +private extension OngoingCallContextState { init(_ state: OngoingCallStateWebrtc) { switch state { case .initializing: @@ -350,7 +350,7 @@ private extension OngoingCallContextState { self = .failed } } -}*/ +} private extension OngoingCallContextState { init(_ state: OngoingCallStateWebrtcCustom) { @@ -401,7 +401,8 @@ public final class OngoingCallContext { public static func versions(includeExperimental: Bool) -> [String] { var result: [String] = [OngoingCallThreadLocalContext.version()] if includeExperimental { - result.append(OngoingCallThreadLocalContextWebrtcCustom.version()) + result.append(OngoingCallThreadLocalContextWebrtc.version()) + //result.append(OngoingCallThreadLocalContextWebrtcCustom.version()) } return result } @@ -453,7 +454,7 @@ public final class OngoingCallContext { context.nativeSetNetworkType(networkType) } }) - }/* else if version == OngoingCallThreadLocalContextWebrtc.version() { + } else if version == OngoingCallThreadLocalContextWebrtc.version() { var voipProxyServer: VoipProxyServerWebrtc? if let proxyServer = proxyServer { switch proxyServer.connection { @@ -479,7 +480,7 @@ public final class OngoingCallContext { context.nativeSetNetworkType(networkType) } }) - }*/ else { + } else { var voipProxyServer: VoipProxyServer? if let proxyServer = proxyServer { switch proxyServer.connection { diff --git a/submodules/TgVoip/BUILD b/submodules/TgVoip/BUILD index d316e32ecc..6d87f66345 100644 --- a/submodules/TgVoip/BUILD +++ b/submodules/TgVoip/BUILD @@ -41,6 +41,23 @@ replace_symbols = [ "WebRtcAgc_CalculateGainTable", "WebRtcAgc_InitVad", "WebRtcAgc_ProcessVad", + "TimeDiff", + "TimeAfter", + "TimeMicros", + "TimeUTCMicros", + "SystemTimeNanos", + "TimeNanos", + "SystemTimeMillis", + "TimeMillis", + "TimeUTCMillis", + "GetClockForTesting", + "TimestampWrapAroundHandler", + "Time32", + "TmToSeconds", + "TimeDiff32", + "TimestampWrapAroundHandler", + "g_clock", + "SetClockForTesting", ] objc_library( diff --git a/submodules/TgVoipWebrtc/BUILD b/submodules/TgVoipWebrtc/BUILD index eba68a38e4..4a1e45b13f 100644 --- a/submodules/TgVoipWebrtc/BUILD +++ b/submodules/TgVoipWebrtc/BUILD @@ -25,6 +25,7 @@ objc_library( "-DWEBRTC_IOS", "-DWEBRTC_MAC", "-DWEBRTC_POSIX", + "-std=c++14", ], includes = [ "PublicHeaders", diff --git a/submodules/TgVoipWebrtc/Impl/Controller.cpp b/submodules/TgVoipWebrtc/Impl/Controller.cpp index b00c97819b..fd5001073e 100644 --- a/submodules/TgVoipWebrtc/Impl/Controller.cpp +++ b/submodules/TgVoipWebrtc/Impl/Controller.cpp @@ -3,7 +3,7 @@ #include "Layer92.h" #include "modules/rtp_rtcp/source/rtp_utility.h" -#include "rtc_base/time_utils.cc" +#include "rtc_base/time_utils.h" #include "rtc_base/message_handler.h" #include @@ -89,13 +89,7 @@ void Controller::NewMessage(const message::Base& msg) { state = State::Established; SignalNewState(state); thread->PostTask(RTC_FROM_HERE, [this]() { -#ifdef TGVOIP_PREPROCESSED_OUTPUT - preproc = std::make_unique(not is_outgoing, false, true); - preproc->Play.connect(this, &Controller::Preprocessed); -#endif media = std::make_unique(is_outgoing); - media->Record.connect(this, &Controller::Record); - media->Play.connect(this, &Controller::Play); media->Send.connect(this, &Controller::SendRtp); }); StartRepeating([this]() { @@ -144,9 +138,6 @@ void Controller::StopRepeating() { void Controller::SetFail() { thread->PostTask(RTC_FROM_HERE, [this]() { media = nullptr; -#ifdef TGVOIP_PREPROCESSED_OUTPUT - preproc = nullptr; -#endif }); if (state != State::Failed) { state = State::Failed; @@ -155,29 +146,7 @@ void Controller::SetFail() { StopRepeating(); } -void Controller::Play(const int16_t *data, size_t size) { - SignalPlay(data, size); -} - -void Controller::Record(int16_t *data, size_t size) { - SignalRecord(data, size); - last_send_time = rtc::TimeMillis(); -} - -#ifdef TGVOIP_PREPROCESSED_OUTPUT -void Controller::Preprocessed(const int16_t *data, size_t size) { - if (rtc::TimeMillis() - last_send_time < 100) - SignalPreprocessed(data, size); -} -#endif - void Controller::SendRtp(rtc::CopyOnWriteBuffer packet) { -#ifdef TGVOIP_PREPROCESSED_OUTPUT - thread->PostTask(RTC_FROM_HERE, [this, packet]() { - if (preproc) - preproc->Receive(packet); - }); -#endif message::RtpStream msg; msg.data = packet; msg.network_type = local_network_type; diff --git a/submodules/TgVoipWebrtc/Impl/Controller.h b/submodules/TgVoipWebrtc/Impl/Controller.h index ebd1a7d27c..cce271b96f 100644 --- a/submodules/TgVoipWebrtc/Impl/Controller.h +++ b/submodules/TgVoipWebrtc/Impl/Controller.h @@ -41,20 +41,12 @@ public: static std::map network_params; static MediaEngineWebrtc::NetworkParams default_network_params; static MediaEngineWebrtc::NetworkParams datasaving_network_params; - sigslot::signal2 SignalRecord; -#ifdef TGVOIP_PREPROCESSED_OUTPUT - sigslot::signal2 SignalPreprocessed; -#endif - sigslot::signal2 SignalPlay; sigslot::signal1 SignalNewState; private: std::unique_ptr thread; std::unique_ptr connector; std::unique_ptr media; -#ifdef TGVOIP_PREPROCESSED_OUTPUT - std::unique_ptr preproc; -#endif State state; webrtc::RepeatingTaskHandle repeatable; int64_t last_recv_time; @@ -73,9 +65,6 @@ private: void SetFail(); void Play(const int16_t *data, size_t size); void Record(int16_t *data, size_t size); -#ifdef TGVOIP_PREPROCESSED_OUTPUT - void Preprocessed(const int16_t *data, size_t size); -#endif void SendRtp(rtc::CopyOnWriteBuffer packet); void UpdateNetworkParams(const message::RtpStream& rtp); }; diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineBase.h b/submodules/TgVoipWebrtc/Impl/MediaEngineBase.h index d85aa8c785..fc2d1cde56 100644 --- a/submodules/TgVoipWebrtc/Impl/MediaEngineBase.h +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineBase.h @@ -14,8 +14,6 @@ public: sigslot::signal1 Send; virtual void Receive(rtc::CopyOnWriteBuffer) = 0; - sigslot::signal2 Play; - sigslot::signal2 Record; }; #endif //DEMO_MEDIAENGINEBASE_H diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp index 4b6651c9a3..b79ab69a45 100644 --- a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp @@ -12,6 +12,7 @@ #include "modules/audio_device/include/audio_device_default.h" #include "rtc_base/task_utils/repeating_task.h" #include "system_wrappers/include/field_trial.h" +#include "api/video/builtin_video_bitrate_allocator_factory.h" #if WEBRTC_ENABLE_PROTOBUF #include "modules/audio_coding/audio_network_adaptor/config.pb.h" @@ -28,12 +29,16 @@ const uint8_t sdp_channels = 2; const uint32_t sdp_bitrate = 0; const uint32_t caller_ssrc = 1; const uint32_t called_ssrc = 2; +const uint32_t caller_ssrc_video = 1; +const uint32_t called_ssrc_video = 2; const int extension_sequence = 1; } MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) : ssrc_send(outgoing ? caller_ssrc : called_ssrc) , ssrc_recv(outgoing ? called_ssrc : caller_ssrc) +, ssrc_send_video(outgoing ? caller_ssrc_video : called_ssrc_video) +, ssrc_recv_video(outgoing ? called_ssrc_video : caller_ssrc_video) , event_log(std::make_unique()) , task_queue_factory(webrtc::CreateDefaultTaskQueueFactory()) , data_sender(*this) { @@ -41,17 +46,10 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) "WebRTC-Audio-SendSideBwe/Enabled/" "WebRTC-Audio-Allocation/min:6kbps,max:32kbps/" "WebRTC-Audio-OpusMinPacketLossRate/Enabled-1/" -// "WebRTC-Audio-OpusPlcUsePrevDecodedSamples/Enabled/" -// "WebRTC-Audio-NewOpusPacketLossRateOptimization/Enabled-1-20-1.0/" -// "WebRTC-SendSideBwe-WithOverhead/Enabled/" -// "WebRTC-Bwe-SeparateAudioPackets/enabled:true,packet_threshold:15,time_threshold:1000ms/" -// "WebRTC-Audio-AlrProbing/Disabled/" ); + video_bitrate_allocator_factory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory(); cricket::MediaEngineDependencies media_deps; media_deps.task_queue_factory = task_queue_factory.get(); -#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO - media_deps.adm = new rtc::RefCountedObject>(); -#endif media_deps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory(); media_deps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory(); media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create(); @@ -62,12 +60,9 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) call_config.trials = &field_trials; call_config.audio_state = media_engine->voice().GetAudioState(); call.reset(webrtc::Call::Create(call_config)); -#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO - audio_processor = std::make_unique(call_config.audio_state->audio_transport(), - task_queue_factory.get(), *this, send, recv); -#endif voice_channel.reset(media_engine->voice().CreateMediaChannel( call.get(), cricket::MediaConfig(), cricket::AudioOptions(), webrtc::CryptoOptions::NoGcm())); + video_channel.reset(media_engine->video().CreateMediaChannel(call.get(), cricket::MediaConfig(), cricket::VideoOptions(), webrtc::CryptoOptions::NoGcm(), video_bitrate_allocator_factory.get())); if (send) { voice_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send)); SetNetworkParams({6, 32, 6, 120, false, false, false}); @@ -76,6 +71,12 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) voice_channel->OnReadyToSend(true); voice_channel->SetSend(true); } + if (false && send) { + video_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send_video)); + video_channel->SetInterface(&data_sender, webrtc::MediaTransportConfig()); + video_channel->OnReadyToSend(true); + video_channel->SetSend(true); + } if (recv) { cricket::AudioRecvParameters recv_parameters; recv_parameters.codecs.emplace_back(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); @@ -86,6 +87,16 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) voice_channel->SetRecvParameters(recv_parameters); voice_channel->SetPlayout(true); } + if (false && recv) { + cricket::VideoRecvParameters recv_parameters; + //recv_parameters.codecs.emplace_back(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); + recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); + recv_parameters.rtcp.reduced_size = true; + recv_parameters.rtcp.remote_estimate = true; + video_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv_video)); + video_channel->SetRecvParameters(recv_parameters); + //video_channel->SetPlayout(true); + } } MediaEngineWebrtc::~MediaEngineWebrtc() = default; @@ -107,19 +118,8 @@ void MediaEngineWebrtc::SetNetworkParams(const MediaEngineWebrtc::NetworkParams& opus_codec.SetParam(cricket::kCodecParamMaxBitrate, params.max_bitrate_kbps); opus_codec.SetParam(cricket::kCodecParamUseInbandFec, 1); opus_codec.SetParam(cricket::kCodecParamPTime, params.ptime_ms); -// opus_codec.SetParam(cricket::kCodecParamUseDtx, "1"); -// opus_codec.SetParam(cricket::kCodecParamMaxAverageBitrate, 6); - std::string config_string; -#if WEBRTC_ENABLE_PROTOBUF - webrtc::audio_network_adaptor::config::ControllerManager cont_conf; -// cont_conf.add_controllers()->mutable_bitrate_controller(); - config_string = cont_conf.SerializeAsString(); -#endif + cricket::AudioSendParameters send_parameters; - if (!config_string.empty()) { - send_parameters.options.audio_network_adaptor_config = config_string; - send_parameters.options.audio_network_adaptor = true; - } send_parameters.codecs.push_back(opus_codec); send_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); send_parameters.options.echo_cancellation = params.echo_cancellation; @@ -157,48 +157,3 @@ int MediaEngineWebrtc::Sender::SetOption(cricket::MediaChannel::NetworkInterface } MediaEngineWebrtc::Sender::Sender(MediaEngineWebrtc& engine) : engine(engine) {} - -MediaEngineWebrtc::AudioProcessor::AudioProcessor(webrtc::AudioTransport *transport_, - webrtc::TaskQueueFactory *task_queue_factory, MediaEngineBase& engine_, bool send_, bool recv_) - : send(send_) - , recv(recv_) - , transport(transport_) - , delay_us(frame_samples * 1000000 / clockrate) - , buf_send(nullptr) - , buf_recv(nullptr) - , engine(engine_) - , task_queue_send(std::make_unique(task_queue_factory->CreateTaskQueue( - "AudioProcessorSend", webrtc::TaskQueueFactory::Priority::NORMAL))) - , task_queue_recv(std::make_unique(task_queue_factory->CreateTaskQueue( - "AudioProcessorRecv", webrtc::TaskQueueFactory::Priority::NORMAL))) { - if (send) { - buf_send = new int16_t[frame_samples * channels]; - webrtc::RepeatingTaskHandle::Start(task_queue_send->Get(), [this]() { - static uint32_t new_mic_level = 0; - memset(buf_send, 0, frame_samples * channels * sample_bytes); - engine.Record(buf_send, frame_samples * channels); - transport->RecordedDataIsAvailable(buf_send, frame_samples, sample_bytes, channels, clockrate, - 0, 0, 0, false, new_mic_level); - return webrtc::TimeDelta::us(delay_us); - }); - } - if (recv) { - buf_recv = new int16_t[frame_samples * channels]; - webrtc::RepeatingTaskHandle::Start(task_queue_recv->Get(), [this]() { - static int64_t elapsed_time_ms = -1; - static int64_t ntp_time_ms = -1; - size_t samples_out = 0; - transport->NeedMorePlayData(frame_samples, sample_bytes, channels, clockrate, buf_recv, - samples_out, &elapsed_time_ms, &ntp_time_ms); - engine.Play(buf_recv, samples_out * channels); - return webrtc::TimeDelta::us(delay_us); - }); - } -} - -MediaEngineWebrtc::AudioProcessor::~AudioProcessor() { - task_queue_send = nullptr; - task_queue_recv = nullptr; - delete[] buf_send; - delete[] buf_recv; -} diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h index f6332d9f83..06f1028088 100644 --- a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h @@ -61,6 +61,8 @@ private: const uint32_t ssrc_send; const uint32_t ssrc_recv; + const uint32_t ssrc_send_video; + const uint32_t ssrc_recv_video; std::unique_ptr call; std::unique_ptr media_engine; std::unique_ptr event_log; @@ -69,9 +71,8 @@ private: webrtc::LocalAudioSinkAdapter audio_source; Sender data_sender; std::unique_ptr voice_channel; -#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO - std::unique_ptr audio_processor; -#endif + std::unique_ptr video_channel; + std::unique_ptr video_bitrate_allocator_factory; }; From 04796cf4772581fcc7e5f6f64d1271af5891fb61 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 9 Jun 2020 10:37:56 +0400 Subject: [PATCH 6/8] Temp --- .../Postbox/Sources/ChatListViewState.swift | 4 +- submodules/TelegramVoip/BUILD | 2 +- .../Sources/OngoingCallContext.swift | 39 +- submodules/TgVoipWebrtc/BUILD | 5 + .../Impl/{Connector.cpp => Connector.mm} | 0 submodules/TgVoipWebrtc/Impl/Controller.h | 3 + .../Impl/{Controller.cpp => Controller.mm} | 21 +- .../TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp | 159 ------ .../TgVoipWebrtc/Impl/MediaEngineWebrtc.h | 8 + .../TgVoipWebrtc/Impl/MediaEngineWebrtc.mm | 359 ++++++++++++++ submodules/TgVoipWebrtc/Impl/PlatformCodecs.h | 6 + .../TgVoipWebrtc/Impl/PlatformCodecs.mm | 2 + submodules/TgVoipWebrtc/Impl/TgVoip.h | 4 + .../Impl/{TgVoip.cpp => TgVoip.mm} | 4 + .../TgVoipWebrtc/Impl/VideoCameraCapturer.h | 23 + .../TgVoipWebrtc/Impl/VideoCameraCapturer.mm | 459 ++++++++++++++++++ submodules/TgVoipWebrtc/Impl/VideoMetalView.h | 26 + .../TgVoipWebrtc/Impl/VideoMetalView.mm | 278 +++++++++++ .../TgVoipWebrtc/Impl/VideoRendererAdapter.h | 0 .../TgVoipWebrtc/Impl/VideoRendererAdapter.mm | 0 .../TgVoip/OngoingCallThreadLocalContext.h | 2 + .../Sources/OngoingCallThreadLocalContext.mm | 13 + 22 files changed, 1228 insertions(+), 189 deletions(-) rename submodules/TgVoipWebrtc/Impl/{Connector.cpp => Connector.mm} (100%) rename submodules/TgVoipWebrtc/Impl/{Controller.cpp => Controller.mm} (93%) delete mode 100644 submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp create mode 100644 submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm create mode 100644 submodules/TgVoipWebrtc/Impl/PlatformCodecs.h create mode 100644 submodules/TgVoipWebrtc/Impl/PlatformCodecs.mm rename submodules/TgVoipWebrtc/Impl/{TgVoip.cpp => TgVoip.mm} (99%) create mode 100644 submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.h create mode 100644 submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.mm create mode 100644 submodules/TgVoipWebrtc/Impl/VideoMetalView.h create mode 100644 submodules/TgVoipWebrtc/Impl/VideoMetalView.mm create mode 100644 submodules/TgVoipWebrtc/Impl/VideoRendererAdapter.h create mode 100644 submodules/TgVoipWebrtc/Impl/VideoRendererAdapter.mm diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index a0ec7f2f07..50294314e0 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -870,8 +870,8 @@ private final class ChatListViewSpaceState { private func checkReplayEntries(postbox: Postbox) { #if DEBUG let cleanState = ChatListViewSpaceState(postbox: postbox, space: self.space, anchorIndex: self.anchorIndex, summaryComponents: self.summaryComponents, halfLimit: self.halfLimit) - assert(self.orderedEntries.lowerOrAtAnchor.map { $0.index } == cleanState.orderedEntries.lowerOrAtAnchor.map { $0.index }) - assert(self.orderedEntries.higherThanAnchor.map { $0.index } == cleanState.orderedEntries.higherThanAnchor.map { $0.index }) + //assert(self.orderedEntries.lowerOrAtAnchor.map { $0.index } == cleanState.orderedEntries.lowerOrAtAnchor.map { $0.index }) + //assert(self.orderedEntries.higherThanAnchor.map { $0.index } == cleanState.orderedEntries.higherThanAnchor.map { $0.index }) #endif } diff --git a/submodules/TelegramVoip/BUILD b/submodules/TelegramVoip/BUILD index d3c64da09d..c4b6b81c56 100644 --- a/submodules/TelegramVoip/BUILD +++ b/submodules/TelegramVoip/BUILD @@ -13,7 +13,7 @@ swift_library( "//submodules/Postbox:Postbox", "//submodules/TelegramUIPreferences:TelegramUIPreferences", "//submodules/TgVoip:TgVoip", - "//submodules/TgVoipWebrtcCustom:TgVoipWebrtcCustom", + #"//submodules/TgVoipWebrtcCustom:TgVoipWebrtcCustom", "//submodules/TgVoipWebrtc:TgVoipWebrtc", ], visibility = [ diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index d6cd87c809..6d1e151fea 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -8,7 +8,7 @@ import TelegramUIPreferences import TgVoip import TgVoipWebrtc -import TgVoipWebrtcCustom +//import TgVoipWebrtcCustom private func callConnectionDescription(_ connection: CallSessionConnection) -> OngoingCallConnectionDescription { return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) @@ -18,9 +18,9 @@ private func callConnectionDescriptionWebrtc(_ connection: CallSessionConnection return OngoingCallConnectionDescriptionWebrtc(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) } -private func callConnectionDescriptionWebrtcCustom(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtcCustom { +/*private func callConnectionDescriptionWebrtcCustom(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtcCustom { return OngoingCallConnectionDescriptionWebrtcCustom(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag) -} +}*/ private let callLogsLimit = 20 @@ -85,11 +85,11 @@ private let setupLogs: Bool = { Logger.shared.log("TGVOIP", value) } }) - OngoingCallThreadLocalContextWebrtcCustom.setupLoggingFunction({ value in + /*OngoingCallThreadLocalContextWebrtcCustom.setupLoggingFunction({ value in if let value = value { Logger.shared.log("TGVOIP", value) } - }) + })*/ return true }() @@ -100,7 +100,7 @@ public enum OngoingCallContextState { case failed } -private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue, OngoingCallThreadLocalContextQueueWebrtc , OngoingCallThreadLocalContextQueueWebrtcCustom { +private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue, OngoingCallThreadLocalContextQueueWebrtc /*, OngoingCallThreadLocalContextQueueWebrtcCustom*/ { private let queue: Queue init(queue: Queue) { @@ -164,7 +164,7 @@ private func ongoingNetworkTypeForTypeWebrtc(_ type: NetworkType) -> OngoingCall } } -private func ongoingNetworkTypeForTypeWebrtcCustom(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtcCustom { +/*private func ongoingNetworkTypeForTypeWebrtcCustom(_ type: NetworkType) -> OngoingCallNetworkTypeWebrtcCustom { switch type { case .none: return .wifi @@ -182,7 +182,7 @@ private func ongoingNetworkTypeForTypeWebrtcCustom(_ type: NetworkType) -> Ongoi return .cellularLte } } -} +}*/ private func ongoingDataSavingForType(_ type: VoiceCallDataSaving) -> OngoingCallDataSaving { switch type { @@ -210,7 +210,7 @@ private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> Ongo } } -private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtcCustom { +/*private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtcCustom { switch type { case .never: return .never @@ -221,7 +221,7 @@ private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) - default: return .never } -} +}*/ private protocol OngoingCallThreadLocalContextProtocol: class { func nativeSetNetworkType(_ type: NetworkType) @@ -292,7 +292,7 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt } } -extension OngoingCallThreadLocalContextWebrtcCustom: OngoingCallThreadLocalContextProtocol { +/*extension OngoingCallThreadLocalContextWebrtcCustom: OngoingCallThreadLocalContextProtocol { func nativeSetNetworkType(_ type: NetworkType) { self.setNetworkType(ongoingNetworkTypeForTypeWebrtcCustom(type)) } @@ -316,7 +316,7 @@ extension OngoingCallThreadLocalContextWebrtcCustom: OngoingCallThreadLocalConte func nativeGetDerivedState() -> Data { return self.getDerivedState() } -} +}*/ private extension OngoingCallContextState { init(_ state: OngoingCallState) { @@ -352,7 +352,7 @@ private extension OngoingCallContextState { } } -private extension OngoingCallContextState { +/*private extension OngoingCallContextState { init(_ state: OngoingCallStateWebrtcCustom) { switch state { case .initializing: @@ -367,7 +367,7 @@ private extension OngoingCallContextState { self = .failed } } -} +}*/ public final class OngoingCallContext { public let internalId: CallSessionInternalId @@ -426,7 +426,7 @@ public final class OngoingCallContext { |> take(1) |> deliverOn(queue)).start(next: { [weak self] _ in if let strongSelf = self { - if version == OngoingCallThreadLocalContextWebrtcCustom.version() { + /*if version == OngoingCallThreadLocalContextWebrtcCustom.version() { var voipProxyServer: VoipProxyServerWebrtcCustom? if let proxyServer = proxyServer { switch proxyServer.connection { @@ -454,7 +454,7 @@ public final class OngoingCallContext { context.nativeSetNetworkType(networkType) } }) - } else if version == OngoingCallThreadLocalContextWebrtc.version() { + } else */if version == OngoingCallThreadLocalContextWebrtc.version() { var voipProxyServer: VoipProxyServerWebrtc? if let proxyServer = proxyServer { switch proxyServer.connection { @@ -513,9 +513,9 @@ public final class OngoingCallContext { self.signalingDataDisposable = (callSessionManager.callSignalingData(internalId: internalId) |> deliverOn(self.queue)).start(next: { [weak self] data in self?.withContext { context in - if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { + /*if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { context.receiveSignaling(data) - } + }*/ } }) } @@ -585,11 +585,10 @@ public final class OngoingCallContext { public func getVideoView(completion: @escaping (UIView?) -> Void) { self.withContext { context in - if let context = context as? OngoingCallThreadLocalContextWebrtcCustom { + if let context = context as? OngoingCallThreadLocalContextWebrtc { context.getRemoteCameraView(completion) } completion(nil) } } } - diff --git a/submodules/TgVoipWebrtc/BUILD b/submodules/TgVoipWebrtc/BUILD index 4a1e45b13f..1b7572c72f 100644 --- a/submodules/TgVoipWebrtc/BUILD +++ b/submodules/TgVoipWebrtc/BUILD @@ -14,6 +14,8 @@ objc_library( "Sources/**/*.h", "Impl/*.h", "Impl/*.cpp", + "Impl/*.mm", + "Impl/*.m", ]), hdrs = glob([ "PublicHeaders/**/*.h", @@ -22,6 +24,9 @@ objc_library( "-I{}/Impl".format(package_name()), "-Ithird-party/webrtc/webrtc-ios/src", "-Ithird-party/webrtc/webrtc-ios/src/third_party/abseil-cpp", + "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc", + "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/base", + "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/components/renderer/metal", "-DWEBRTC_IOS", "-DWEBRTC_MAC", "-DWEBRTC_POSIX", diff --git a/submodules/TgVoipWebrtc/Impl/Connector.cpp b/submodules/TgVoipWebrtc/Impl/Connector.mm similarity index 100% rename from submodules/TgVoipWebrtc/Impl/Connector.cpp rename to submodules/TgVoipWebrtc/Impl/Connector.mm diff --git a/submodules/TgVoipWebrtc/Impl/Controller.h b/submodules/TgVoipWebrtc/Impl/Controller.h index cce271b96f..3870f5311b 100644 --- a/submodules/TgVoipWebrtc/Impl/Controller.h +++ b/submodules/TgVoipWebrtc/Impl/Controller.h @@ -11,6 +11,8 @@ #include "rtc_base/task_utils/repeating_task.h" #include "rtc_base/third_party/sigslot/sigslot.h" +#import "VideoMetalView.h" + class Controller : public sigslot::has_slots<> { public: enum EndpointType { @@ -35,6 +37,7 @@ public: void SetNetworkType(message::NetworkType network_type); void SetDataSaving(bool data_saving); void SetMute(bool mute); + void AttachVideoView(VideoMetalView *videoView); void SetProxy(rtc::ProxyType type, const rtc::SocketAddress& addr, const std::string& username, const std::string& password); diff --git a/submodules/TgVoipWebrtc/Impl/Controller.cpp b/submodules/TgVoipWebrtc/Impl/Controller.mm similarity index 93% rename from submodules/TgVoipWebrtc/Impl/Controller.cpp rename to submodules/TgVoipWebrtc/Impl/Controller.mm index fd5001073e..808c4aa7ac 100644 --- a/submodules/TgVoipWebrtc/Impl/Controller.cpp +++ b/submodules/TgVoipWebrtc/Impl/Controller.mm @@ -68,8 +68,8 @@ void Controller::NewMessage(const message::Base& msg) { msg.minVer = ProtocolBase::minimal_version; msg.ver = ProtocolBase::actual_version; connector->SendMessage(msg); - if (rtc::TimeMillis() - last_recv_time > init_timeout) - SetFail(); + //if (rtc::TimeMillis() - last_recv_time > init_timeout) + // SetFail(); return webrtc::TimeDelta::seconds(1); }); } else if ((msg.ID == message::tInit || msg.ID == message::tInitAck) && state == State::WaitInit) { @@ -81,8 +81,8 @@ void Controller::NewMessage(const message::Base& msg) { msg.minVer = ProtocolBase::minimal_version; msg.ver = ProtocolBase::actual_version; connector->SendMessage(msg); - if (rtc::TimeMillis() - last_recv_time > init_timeout) - SetFail(); + //if (rtc::TimeMillis() - last_recv_time > init_timeout) + // SetFail(); return webrtc::TimeDelta::seconds(1); }); } else if ((msg.ID == message::tInitAck || msg.ID == message::tRtpStream) && state == State::WaitInitAck) { @@ -97,8 +97,9 @@ void Controller::NewMessage(const message::Base& msg) { connector->ResetActiveEndpoint(); state = State::Reconnecting; SignalNewState(state); - } else if (state == State::Reconnecting && rtc::TimeMillis() - last_recv_time > reconnect_timeout) - SetFail(); + } else if (state == State::Reconnecting && rtc::TimeMillis() - last_recv_time > reconnect_timeout) { + //SetFail(); + } return webrtc::TimeDelta::seconds(1); }); } if ((msg.ID == message::tRtpStream) && (state == State::Established || state == State::Reconnecting)) { @@ -125,7 +126,7 @@ void Controller::NewMessage(const message::Base& msg) { template void Controller::StartRepeating(Closure&& closure) { - StopRepeating(); + //StopRepeating(); repeatable = webrtc::RepeatingTaskHandle::Start(thread.get(), std::forward(closure)); } @@ -173,6 +174,12 @@ void Controller::UpdateNetworkParams(const message::RtpStream& rtp) { } } +void Controller::AttachVideoView(VideoMetalView *videoView) { + thread->PostTask(RTC_FROM_HERE, [this, videoView]() { + media->AttachVideoView(videoView); + }); +} + void Controller::SetNetworkType(message::NetworkType network_type) { local_network_type = network_type; } diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp deleted file mode 100644 index b79ab69a45..0000000000 --- a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include "MediaEngineWebrtc.h" - -#include "api/audio_codecs/audio_decoder_factory_template.h" -#include "api/audio_codecs/audio_encoder_factory_template.h" -#include "api/audio_codecs/opus/audio_decoder_opus.h" -#include "api/audio_codecs/opus/audio_encoder_opus.h" -#include "api/rtp_parameters.h" -#include "api/task_queue/default_task_queue_factory.h" -#include "media/base/codec.h" -#include "media/base/media_constants.h" -#include "media/engine/webrtc_media_engine.h" -#include "modules/audio_device/include/audio_device_default.h" -#include "rtc_base/task_utils/repeating_task.h" -#include "system_wrappers/include/field_trial.h" -#include "api/video/builtin_video_bitrate_allocator_factory.h" - -#if WEBRTC_ENABLE_PROTOBUF -#include "modules/audio_coding/audio_network_adaptor/config.pb.h" -#endif - -namespace { -const size_t frame_samples = 480; -const uint8_t channels = 1; -const uint8_t sample_bytes = 2; -const uint32_t clockrate = 48000; -const uint16_t sdp_payload = 111; -const char* sdp_name = "opus"; -const uint8_t sdp_channels = 2; -const uint32_t sdp_bitrate = 0; -const uint32_t caller_ssrc = 1; -const uint32_t called_ssrc = 2; -const uint32_t caller_ssrc_video = 1; -const uint32_t called_ssrc_video = 2; -const int extension_sequence = 1; -} - -MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) -: ssrc_send(outgoing ? caller_ssrc : called_ssrc) -, ssrc_recv(outgoing ? called_ssrc : caller_ssrc) -, ssrc_send_video(outgoing ? caller_ssrc_video : called_ssrc_video) -, ssrc_recv_video(outgoing ? called_ssrc_video : caller_ssrc_video) -, event_log(std::make_unique()) -, task_queue_factory(webrtc::CreateDefaultTaskQueueFactory()) -, data_sender(*this) { - webrtc::field_trial::InitFieldTrialsFromString( - "WebRTC-Audio-SendSideBwe/Enabled/" - "WebRTC-Audio-Allocation/min:6kbps,max:32kbps/" - "WebRTC-Audio-OpusMinPacketLossRate/Enabled-1/" - ); - video_bitrate_allocator_factory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory(); - cricket::MediaEngineDependencies media_deps; - media_deps.task_queue_factory = task_queue_factory.get(); - media_deps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory(); - media_deps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory(); - media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create(); - media_engine = cricket::CreateMediaEngine(std::move(media_deps)); - media_engine->Init(); - webrtc::Call::Config call_config(event_log.get()); - call_config.task_queue_factory = task_queue_factory.get(); - call_config.trials = &field_trials; - call_config.audio_state = media_engine->voice().GetAudioState(); - call.reset(webrtc::Call::Create(call_config)); - voice_channel.reset(media_engine->voice().CreateMediaChannel( - call.get(), cricket::MediaConfig(), cricket::AudioOptions(), webrtc::CryptoOptions::NoGcm())); - video_channel.reset(media_engine->video().CreateMediaChannel(call.get(), cricket::MediaConfig(), cricket::VideoOptions(), webrtc::CryptoOptions::NoGcm(), video_bitrate_allocator_factory.get())); - if (send) { - voice_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send)); - SetNetworkParams({6, 32, 6, 120, false, false, false}); - SetMute(false); - voice_channel->SetInterface(&data_sender, webrtc::MediaTransportConfig()); - voice_channel->OnReadyToSend(true); - voice_channel->SetSend(true); - } - if (false && send) { - video_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send_video)); - video_channel->SetInterface(&data_sender, webrtc::MediaTransportConfig()); - video_channel->OnReadyToSend(true); - video_channel->SetSend(true); - } - if (recv) { - cricket::AudioRecvParameters recv_parameters; - recv_parameters.codecs.emplace_back(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); - recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); - recv_parameters.rtcp.reduced_size = true; - recv_parameters.rtcp.remote_estimate = true; - voice_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv)); - voice_channel->SetRecvParameters(recv_parameters); - voice_channel->SetPlayout(true); - } - if (false && recv) { - cricket::VideoRecvParameters recv_parameters; - //recv_parameters.codecs.emplace_back(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); - recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); - recv_parameters.rtcp.reduced_size = true; - recv_parameters.rtcp.remote_estimate = true; - video_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv_video)); - video_channel->SetRecvParameters(recv_parameters); - //video_channel->SetPlayout(true); - } -} - -MediaEngineWebrtc::~MediaEngineWebrtc() = default; - -void MediaEngineWebrtc::Receive(rtc::CopyOnWriteBuffer packet) { - if (voice_channel) - voice_channel->OnPacketReceived(packet, -1); -} - -void MediaEngineWebrtc::OnSentPacket(const rtc::SentPacket& sent_packet) { - call->OnSentPacket(sent_packet); -} - -void MediaEngineWebrtc::SetNetworkParams(const MediaEngineWebrtc::NetworkParams& params) { - cricket::AudioCodec opus_codec(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); - opus_codec.AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc)); - opus_codec.SetParam(cricket::kCodecParamMinBitrate, params.min_bitrate_kbps); - opus_codec.SetParam(cricket::kCodecParamStartBitrate, params.start_bitrate_kbps); - opus_codec.SetParam(cricket::kCodecParamMaxBitrate, params.max_bitrate_kbps); - opus_codec.SetParam(cricket::kCodecParamUseInbandFec, 1); - opus_codec.SetParam(cricket::kCodecParamPTime, params.ptime_ms); - - cricket::AudioSendParameters send_parameters; - send_parameters.codecs.push_back(opus_codec); - send_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); - send_parameters.options.echo_cancellation = params.echo_cancellation; -// send_parameters.options.experimental_ns = false; - send_parameters.options.noise_suppression = params.noise_suppression; - send_parameters.options.auto_gain_control = params.auto_gain_control; - send_parameters.options.highpass_filter = false; - send_parameters.options.typing_detection = false; -// send_parameters.max_bandwidth_bps = 16000; - send_parameters.rtcp.reduced_size = true; - send_parameters.rtcp.remote_estimate = true; - voice_channel->SetSendParameters(send_parameters); -} - -void MediaEngineWebrtc::SetMute(bool mute) { - voice_channel->SetAudioSend(ssrc_send, !mute, nullptr, &audio_source); -} - -bool MediaEngineWebrtc::Sender::SendPacket(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) { - engine.Send(*packet); - rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent); - engine.OnSentPacket(sent_packet); - return true; -} - -bool MediaEngineWebrtc::Sender::SendRtcp(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) { - engine.Send(*packet); - rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent); - engine.OnSentPacket(sent_packet); - return true; -} - -int MediaEngineWebrtc::Sender::SetOption(cricket::MediaChannel::NetworkInterface::SocketType, rtc::Socket::Option, int) { - return -1; // in general, the result is not important yet -} - -MediaEngineWebrtc::Sender::Sender(MediaEngineWebrtc& engine) : engine(engine) {} diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h index 06f1028088..90b1a27694 100644 --- a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.h @@ -12,6 +12,9 @@ #include +#import "VideoCameraCapturer.h" +#import "VideoMetalView.h" + class MediaEngineWebrtc : public MediaEngineBase { public: struct NetworkParams { @@ -30,6 +33,7 @@ public: void OnSentPacket(const rtc::SentPacket& sent_packet); void SetNetworkParams(const NetworkParams& params); void SetMute(bool mute); + void AttachVideoView(VideoMetalView *videoView); private: class Sender final : public cricket::MediaChannel::NetworkInterface { @@ -73,6 +77,10 @@ private: std::unique_ptr voice_channel; std::unique_ptr video_channel; std::unique_ptr video_bitrate_allocator_factory; + std::unique_ptr signaling_thread; + std::unique_ptr worker_thread; + rtc::scoped_refptr _nativeVideoSource; + VideoCameraCapturer *_videoCapturer; }; diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm new file mode 100644 index 0000000000..1229fc91b0 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm @@ -0,0 +1,359 @@ +#include "MediaEngineWebrtc.h" + +#include "absl/strings/match.h" +#include "api/audio_codecs/audio_decoder_factory_template.h" +#include "api/audio_codecs/audio_encoder_factory_template.h" +#include "api/audio_codecs/opus/audio_decoder_opus.h" +#include "api/audio_codecs/opus/audio_encoder_opus.h" +#include "api/rtp_parameters.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "media/base/codec.h" +#include "media/base/media_constants.h" +#include "media/engine/webrtc_media_engine.h" +#include "modules/audio_device/include/audio_device_default.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "system_wrappers/include/field_trial.h" +#include "api/video/builtin_video_bitrate_allocator_factory.h" +#include "api/video/video_bitrate_allocation.h" + +#include "sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.h" +#include "sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.h" +#include "sdk/objc/native/api/video_encoder_factory.h" +#include "sdk/objc/native/api/video_decoder_factory.h" + +#if WEBRTC_ENABLE_PROTOBUF +#include "modules/audio_coding/audio_network_adaptor/config.pb.h" +#endif + +#include "PlatformCodecs.h" + +#include "sdk/objc/native/src/objc_video_track_source.h" +#include "api/video_track_source_proxy.h" +#include "sdk/objc/api/RTCVideoRendererAdapter.h" +#include "sdk/objc/native/api/video_frame.h" + +namespace { +const size_t frame_samples = 480; +const uint8_t channels = 1; +const uint8_t sample_bytes = 2; +const uint32_t clockrate = 48000; +const uint16_t sdp_payload = 111; +const char* sdp_name = "opus"; +const uint8_t sdp_channels = 2; +const uint32_t sdp_bitrate = 0; +const uint32_t caller_ssrc = 1; +const uint32_t called_ssrc = 2; +const uint32_t caller_ssrc_video = 1; +const uint32_t called_ssrc_video = 2; +const int extension_sequence = 1; +} + +static void AddDefaultFeedbackParams(cricket::VideoCodec* codec) { + // Don't add any feedback params for RED and ULPFEC. + if (codec->name == cricket::kRedCodecName || codec->name == cricket::kUlpfecCodecName) + return; + codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty)); + codec->AddFeedbackParam( + cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc, cricket::kParamValueEmpty)); + // Don't add any more feedback params for FLEXFEC. + if (codec->name == cricket::kFlexfecCodecName) + return; + codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir)); + codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)); + codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamNack, cricket::kRtcpFbNackParamPli)); + if (codec->name == cricket::kVp8CodecName && + webrtc::field_trial::IsEnabled("WebRTC-RtcpLossNotification")) { + codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamLntf, cricket::kParamValueEmpty)); + } +} + +static std::vector AssignPayloadTypesAndDefaultCodecs(std::vector input_formats) { + if (input_formats.empty()) + return std::vector(); + static const int kFirstDynamicPayloadType = 96; + static const int kLastDynamicPayloadType = 127; + int payload_type = kFirstDynamicPayloadType; + + //input_formats.push_back(webrtc::SdpVideoFormat(cricket::kH264CodecName)); + input_formats.push_back(webrtc::SdpVideoFormat(cricket::kRedCodecName)); + input_formats.push_back(webrtc::SdpVideoFormat(cricket::kUlpfecCodecName)); + + /*if (IsFlexfecAdvertisedFieldTrialEnabled()) { + webrtc::SdpVideoFormat flexfec_format(kFlexfecCodecName); + // This value is currently arbitrarily set to 10 seconds. (The unit + // is microseconds.) This parameter MUST be present in the SDP, but + // we never use the actual value anywhere in our code however. + // TODO(brandtr): Consider honouring this value in the sender and receiver. + flexfec_format.parameters = {{kFlexfecFmtpRepairWindow, "10000000"}}; + input_formats.push_back(flexfec_format); + }*/ + + std::vector output_codecs; + for (const webrtc::SdpVideoFormat& format : input_formats) { + cricket::VideoCodec codec(format); + codec.id = payload_type; + AddDefaultFeedbackParams(&codec); + output_codecs.push_back(codec); + + // Increment payload type. + ++payload_type; + if (payload_type > kLastDynamicPayloadType) { + RTC_LOG(LS_ERROR) << "Out of dynamic payload types, skipping the rest."; + break; + } + + // Add associated RTX codec for non-FEC codecs. + if (!absl::EqualsIgnoreCase(codec.name, cricket::kUlpfecCodecName) && + !absl::EqualsIgnoreCase(codec.name, cricket::kFlexfecCodecName)) { + output_codecs.push_back( + cricket::VideoCodec::CreateRtxCodec(payload_type, codec.id)); + + // Increment payload type. + ++payload_type; + if (payload_type > kLastDynamicPayloadType) { + RTC_LOG(LS_ERROR) << "Out of dynamic payload types, skipping the rest."; + break; + } + } + } + return output_codecs; +} + +MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) +: ssrc_send(outgoing ? caller_ssrc : called_ssrc) +, ssrc_recv(outgoing ? called_ssrc : caller_ssrc) +, ssrc_send_video(outgoing ? caller_ssrc_video : called_ssrc_video) +, ssrc_recv_video(outgoing ? called_ssrc_video : caller_ssrc_video) +, event_log(std::make_unique()) +, task_queue_factory(webrtc::CreateDefaultTaskQueueFactory()) +, data_sender(*this) +, signaling_thread(rtc::Thread::Create()) +, worker_thread(rtc::Thread::Create()) { + signaling_thread->Start(); + worker_thread->Start(); + + webrtc::field_trial::InitFieldTrialsFromString( + "WebRTC-Audio-SendSideBwe/Enabled/" + "WebRTC-Audio-Allocation/min:6kbps,max:32kbps/" + "WebRTC-Audio-OpusMinPacketLossRate/Enabled-1/" + ); + video_bitrate_allocator_factory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory(); + cricket::MediaEngineDependencies media_deps; + media_deps.task_queue_factory = task_queue_factory.get(); + media_deps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory(); + media_deps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory(); + + auto video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCVideoEncoderFactoryH264 alloc] init]); + std::vector videoCodecs = AssignPayloadTypesAndDefaultCodecs(video_encoder_factory->GetSupportedFormats()); + + media_deps.video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCVideoEncoderFactoryH264 alloc] init]); + media_deps.video_decoder_factory = webrtc::ObjCToNativeVideoDecoderFactory([[RTCVideoDecoderFactoryH264 alloc] init]); + + media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create(); + media_engine = cricket::CreateMediaEngine(std::move(media_deps)); + media_engine->Init(); + webrtc::Call::Config call_config(event_log.get()); + call_config.task_queue_factory = task_queue_factory.get(); + call_config.trials = &field_trials; + call_config.audio_state = media_engine->voice().GetAudioState(); + call.reset(webrtc::Call::Create(call_config)); + voice_channel.reset(media_engine->voice().CreateMediaChannel( + call.get(), cricket::MediaConfig(), cricket::AudioOptions(), webrtc::CryptoOptions::NoGcm())); + video_channel.reset(media_engine->video().CreateMediaChannel(call.get(), cricket::MediaConfig(), cricket::VideoOptions(), webrtc::CryptoOptions::NoGcm(), video_bitrate_allocator_factory.get())); + + if (false && send) { + voice_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send)); + SetNetworkParams({6, 32, 6, 120, false, false, false}); + SetMute(false); + voice_channel->SetInterface(&data_sender, webrtc::MediaTransportConfig()); + voice_channel->OnReadyToSend(true); + voice_channel->SetSend(true); + } + if (send) { + video_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send_video)); + + for (auto codec : videoCodecs) { + if (codec.id == 96 && codec.name == cricket::kH264CodecName) { + rtc::scoped_refptr objCVideoTrackSource(new rtc::RefCountedObject()); + _nativeVideoSource = webrtc::VideoTrackSourceProxy::Create(signaling_thread.get(), worker_thread.get(), objCVideoTrackSource); + + codec.SetParam(cricket::kCodecParamMinBitrate, 300000); + codec.SetParam(cricket::kCodecParamStartBitrate, 300000); + codec.SetParam(cricket::kCodecParamMaxBitrate, 600000); + +#if TARGET_IPHONE_SIMULATOR +#else + _videoCapturer = [[VideoCameraCapturer alloc] initWithSource:_nativeVideoSource]; + + AVCaptureDevice *frontCamera = nil; + for (AVCaptureDevice *device in [VideoCameraCapturer captureDevices]) { + if (device.position == AVCaptureDevicePositionFront) { + frontCamera = device; + break; + } + } + + if (frontCamera == nil) { + assert(false); + return; + } + + NSArray *sortedFormats = [[VideoCameraCapturer supportedFormatsForDevice:frontCamera] sortedArrayUsingComparator:^NSComparisonResult(AVCaptureDeviceFormat* lhs, AVCaptureDeviceFormat *rhs) { + int32_t width1 = CMVideoFormatDescriptionGetDimensions(lhs.formatDescription).width; + int32_t width2 = CMVideoFormatDescriptionGetDimensions(rhs.formatDescription).width; + return width1 < width2 ? NSOrderedAscending : NSOrderedDescending; + }]; + + AVCaptureDeviceFormat *bestFormat = nil; + for (AVCaptureDeviceFormat *format in sortedFormats) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + if (dimensions.width >= 1000 || dimensions.height >= 1000) { + bestFormat = format; + break; + } + } + + if (bestFormat == nil) { + assert(false); + return; + } + + AVFrameRateRange *frameRateRange = [[bestFormat.videoSupportedFrameRateRanges sortedArrayUsingComparator:^NSComparisonResult(AVFrameRateRange *lhs, AVFrameRateRange *rhs) { + if (lhs.maxFrameRate < rhs.maxFrameRate) { + return NSOrderedAscending; + } else { + return NSOrderedDescending; + } + }] lastObject]; + + if (frameRateRange == nil) { + assert(false); + return; + } + + [_videoCapturer startCaptureWithDevice:frontCamera format:bestFormat fps:27]; +#endif + + cricket::VideoSendParameters send_parameters; + send_parameters.codecs.push_back(codec); + send_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); + //send_parameters.options.echo_cancellation = params.echo_cancellation; + //send_parameters.options.noise_suppression = params.noise_suppression; + //send_parameters.options.auto_gain_control = params.auto_gain_control; + //send_parameters.options.highpass_filter = false; + //send_parameters.options.typing_detection = false; + send_parameters.max_bandwidth_bps = 300000; + send_parameters.rtcp.reduced_size = true; + send_parameters.rtcp.remote_estimate = true; + video_channel->SetSendParameters(send_parameters); + + video_channel->SetVideoSend(ssrc_send_video, NULL, _nativeVideoSource.get()); + + video_channel->SetInterface(&data_sender, webrtc::MediaTransportConfig()); + video_channel->OnReadyToSend(true); + video_channel->SetSend(true); + + break; + } + } + } + if (false && recv) { + cricket::AudioRecvParameters recv_parameters; + recv_parameters.codecs.emplace_back(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); + recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); + recv_parameters.rtcp.reduced_size = true; + recv_parameters.rtcp.remote_estimate = true; + voice_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv)); + voice_channel->SetRecvParameters(recv_parameters); + voice_channel->SetPlayout(true); + } + if (recv) { + for (auto codec : videoCodecs) { + if (codec.id == 96 && codec.name == cricket::kH264CodecName) { + codec.SetParam(cricket::kCodecParamMinBitrate, 300000); + codec.SetParam(cricket::kCodecParamStartBitrate, 300000); + codec.SetParam(cricket::kCodecParamMaxBitrate, 600000); + + cricket::VideoRecvParameters recv_parameters; + recv_parameters.codecs.emplace_back(codec); + recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); + recv_parameters.rtcp.reduced_size = true; + recv_parameters.rtcp.remote_estimate = true; + video_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv_video)); + video_channel->SetRecvParameters(recv_parameters); + + break; + } + } + } +} + +MediaEngineWebrtc::~MediaEngineWebrtc() = default; + +void MediaEngineWebrtc::Receive(rtc::CopyOnWriteBuffer packet) { + if (voice_channel) { + //voice_channel->OnPacketReceived(packet, -1); + } + if (video_channel) { + video_channel->OnPacketReceived(packet, -1); + } +} + +void MediaEngineWebrtc::OnSentPacket(const rtc::SentPacket& sent_packet) { + call->OnSentPacket(sent_packet); +} + +void MediaEngineWebrtc::SetNetworkParams(const MediaEngineWebrtc::NetworkParams& params) { + cricket::AudioCodec opus_codec(sdp_payload, sdp_name, clockrate, sdp_bitrate, sdp_channels); + opus_codec.AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc)); + opus_codec.SetParam(cricket::kCodecParamMinBitrate, params.min_bitrate_kbps); + opus_codec.SetParam(cricket::kCodecParamStartBitrate, params.start_bitrate_kbps); + opus_codec.SetParam(cricket::kCodecParamMaxBitrate, params.max_bitrate_kbps); + opus_codec.SetParam(cricket::kCodecParamUseInbandFec, 1); + opus_codec.SetParam(cricket::kCodecParamPTime, params.ptime_ms); + + cricket::AudioSendParameters send_parameters; + send_parameters.codecs.push_back(opus_codec); + send_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); + send_parameters.options.echo_cancellation = params.echo_cancellation; +// send_parameters.options.experimental_ns = false; + send_parameters.options.noise_suppression = params.noise_suppression; + send_parameters.options.auto_gain_control = params.auto_gain_control; + send_parameters.options.highpass_filter = false; + send_parameters.options.typing_detection = false; +// send_parameters.max_bandwidth_bps = 16000; + send_parameters.rtcp.reduced_size = true; + send_parameters.rtcp.remote_estimate = true; + voice_channel->SetSendParameters(send_parameters); +} + +void MediaEngineWebrtc::SetMute(bool mute) { + voice_channel->SetAudioSend(ssrc_send, !mute, nullptr, &audio_source); +} + +void MediaEngineWebrtc::AttachVideoView(VideoMetalView *videoView) { + //VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 240.0f)]; + //remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill; + + video_channel->SetSink(ssrc_recv_video, [videoView getSink]); +} + +bool MediaEngineWebrtc::Sender::SendPacket(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) { + engine.Send(*packet); + rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent); + engine.OnSentPacket(sent_packet); + return true; +} + +bool MediaEngineWebrtc::Sender::SendRtcp(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) { + engine.Send(*packet); + rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent); + engine.OnSentPacket(sent_packet); + return true; +} + +int MediaEngineWebrtc::Sender::SetOption(cricket::MediaChannel::NetworkInterface::SocketType, rtc::Socket::Option, int) { + return -1; // in general, the result is not important yet +} + +MediaEngineWebrtc::Sender::Sender(MediaEngineWebrtc& engine) : engine(engine) {} diff --git a/submodules/TgVoipWebrtc/Impl/PlatformCodecs.h b/submodules/TgVoipWebrtc/Impl/PlatformCodecs.h new file mode 100644 index 0000000000..9c61de32f6 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/PlatformCodecs.h @@ -0,0 +1,6 @@ +#ifndef PLATFORM_CODECS_H +#define PLATFORM_CODECS_H + + + +#endif //PLATFORM_CODECS_H diff --git a/submodules/TgVoipWebrtc/Impl/PlatformCodecs.mm b/submodules/TgVoipWebrtc/Impl/PlatformCodecs.mm new file mode 100644 index 0000000000..d572231b37 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/PlatformCodecs.mm @@ -0,0 +1,2 @@ +#include "PlatformCodecs.h" + diff --git a/submodules/TgVoipWebrtc/Impl/TgVoip.h b/submodules/TgVoipWebrtc/Impl/TgVoip.h index a0408040af..2f1b1e1759 100644 --- a/submodules/TgVoipWebrtc/Impl/TgVoip.h +++ b/submodules/TgVoipWebrtc/Impl/TgVoip.h @@ -8,6 +8,8 @@ #include #include +#import "VideoMetalView.h" + #ifdef TGVOIP_NAMESPACE namespace TGVOIP_NAMESPACE { #endif @@ -159,6 +161,8 @@ public: virtual void setMuteMicrophone(bool muteMicrophone) = 0; virtual void setAudioOutputGainControlEnabled(bool enabled) = 0; virtual void setEchoCancellationStrength(int strength) = 0; + + virtual void AttachVideoView(VideoMetalView *videoView) = 0; virtual std::string getLastError() = 0; virtual std::string getDebugInfo() = 0; diff --git a/submodules/TgVoipWebrtc/Impl/TgVoip.cpp b/submodules/TgVoipWebrtc/Impl/TgVoip.mm similarity index 99% rename from submodules/TgVoipWebrtc/Impl/TgVoip.cpp rename to submodules/TgVoipWebrtc/Impl/TgVoip.mm index 6983708196..0b4e5d87eb 100644 --- a/submodules/TgVoipWebrtc/Impl/TgVoip.cpp +++ b/submodules/TgVoipWebrtc/Impl/TgVoip.mm @@ -232,6 +232,10 @@ public: void setMuteMicrophone(bool muteMicrophone) override { controller_->SetMute(muteMicrophone); } + + void AttachVideoView(VideoMetalView *videoView) override { + controller_->AttachVideoView(videoView); + } void setAudioOutputGainControlEnabled(bool enabled) override { } diff --git a/submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.h b/submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.h new file mode 100644 index 0000000000..a19023032d --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.h @@ -0,0 +1,23 @@ +#ifndef VIDEOCAMERACAPTURER_H +#define VIDEOCAMERACAPTURER_H + +#import +#import + +#include +#include "api/scoped_refptr.h" +#include "api/media_stream_interface.h" + +@interface VideoCameraCapturer : NSObject + ++ (NSArray *)captureDevices; ++ (NSArray *)supportedFormatsForDevice:(AVCaptureDevice *)device; + +- (instancetype)initWithSource:(rtc::scoped_refptr)source; + +- (void)startCaptureWithDevice:(AVCaptureDevice *)device format:(AVCaptureDeviceFormat *)format fps:(NSInteger)fps; +- (void)stopCapture; + +@end + +#endif diff --git a/submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.mm b/submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.mm new file mode 100644 index 0000000000..ec2ed05650 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/VideoCameraCapturer.mm @@ -0,0 +1,459 @@ +#include "VideoCameraCapturer.h" + +#import + +#import "base/RTCLogging.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#import "sdk/objc/native/src/objc_video_track_source.h" +#import "api/video_track_source_proxy.h" + +#import "helpers/UIDevice+RTCDevice.h" + +#import "helpers/AVCaptureSession+DevicePosition.h" +#import "helpers/RTCDispatcher+Private.h" +#import "base/RTCVideoFrame.h" + +static const int64_t kNanosecondsPerSecond = 1000000000; + +static webrtc::ObjCVideoTrackSource *getObjCVideoSource(const rtc::scoped_refptr nativeSource) { + webrtc::VideoTrackSourceProxy *proxy_source = + static_cast(nativeSource.get()); + return static_cast(proxy_source->internal()); +} + +@interface VideoCameraCapturer () { + rtc::scoped_refptr _source; + + dispatch_queue_t _frameQueue; + AVCaptureDevice *_currentDevice; + BOOL _hasRetriedOnFatalError; + BOOL _isRunning; + BOOL _willBeRunning; + + AVCaptureVideoDataOutput *_videoDataOutput; + AVCaptureSession *_captureSession; + FourCharCode _preferredOutputPixelFormat; + FourCharCode _outputPixelFormat; + RTCVideoRotation _rotation; + UIDeviceOrientation _orientation; +} + +@end + +@implementation VideoCameraCapturer + +- (instancetype)initWithSource:(rtc::scoped_refptr)source { + self = [super init]; + if (self != nil) { + _source = source; + if (![self setupCaptureSession:[[AVCaptureSession alloc] init]]) { + return nil; + } + + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + _orientation = UIDeviceOrientationPortrait; + _rotation = RTCVideoRotation_90; + [center addObserver:self + selector:@selector(deviceOrientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + [center addObserver:self + selector:@selector(handleCaptureSessionInterruption:) + name:AVCaptureSessionWasInterruptedNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleCaptureSessionInterruptionEnded:) + name:AVCaptureSessionInterruptionEndedNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleApplicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:[UIApplication sharedApplication]]; + [center addObserver:self + selector:@selector(handleCaptureSessionRuntimeError:) + name:AVCaptureSessionRuntimeErrorNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleCaptureSessionDidStartRunning:) + name:AVCaptureSessionDidStartRunningNotification + object:_captureSession]; + [center addObserver:self + selector:@selector(handleCaptureSessionDidStopRunning:) + name:AVCaptureSessionDidStopRunningNotification + object:_captureSession]; + } + return self; +} + +- (void)dealloc { + NSAssert(!_willBeRunning, @"Session was still running in RTCCameraVideoCapturer dealloc. Forgot to call stopCapture?"); + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + ++ (NSArray *)captureDevices { + AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession + discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInWideAngleCamera ] + mediaType:AVMediaTypeVideo + position:AVCaptureDevicePositionUnspecified]; + return session.devices; +} + ++ (NSArray *)supportedFormatsForDevice:(AVCaptureDevice *)device { + // Support opening the device in any format. We make sure it's converted to a format we + // can handle, if needed, in the method `-setupVideoDataOutput`. + return device.formats; +} + +- (FourCharCode)preferredOutputPixelFormat { + return _preferredOutputPixelFormat; +} + +- (void)startCaptureWithDevice:(AVCaptureDevice *)device + format:(AVCaptureDeviceFormat *)format + fps:(NSInteger)fps { + [self startCaptureWithDevice:device format:format fps:fps completionHandler:nil]; +} + +- (void)stopCapture { + [self stopCaptureWithCompletionHandler:nil]; +} + +- (void)startCaptureWithDevice:(AVCaptureDevice *)device + format:(AVCaptureDeviceFormat *)format + fps:(NSInteger)fps + completionHandler:(nullable void (^)(NSError *))completionHandler { + _willBeRunning = YES; + [RTCDispatcher + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + RTCLogInfo("startCaptureWithDevice %@ @ %ld fps", format, (long)fps); + + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + }); + + _currentDevice = device; + + NSError *error = nil; + if (![_currentDevice lockForConfiguration:&error]) { + RTCLogError(@"Failed to lock device %@. Error: %@", + _currentDevice, + error.userInfo); + if (completionHandler) { + completionHandler(error); + } + _willBeRunning = NO; + return; + } + [self reconfigureCaptureSessionInput]; + [self updateOrientation]; + [self updateDeviceCaptureFormat:format fps:fps]; + [self updateVideoDataOutputPixelFormat:format]; + [_captureSession startRunning]; + [_currentDevice unlockForConfiguration]; + _isRunning = YES; + if (completionHandler) { + completionHandler(nil); + } + }]; +} + +- (void)stopCaptureWithCompletionHandler:(nullable void (^)(void))completionHandler { + _willBeRunning = NO; + [RTCDispatcher + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + RTCLogInfo("Stop"); + _currentDevice = nil; + for (AVCaptureDeviceInput *oldInput in [_captureSession.inputs copy]) { + [_captureSession removeInput:oldInput]; + } + [_captureSession stopRunning]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + }); + _isRunning = NO; + if (completionHandler) { + completionHandler(); + } + }]; +} + +#pragma mark iOS notifications + +#if TARGET_OS_IPHONE +- (void)deviceOrientationDidChange:(NSNotification *)notification { + [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + [self updateOrientation]; + }]; +} +#endif + +#pragma mark AVCaptureVideoDataOutputSampleBufferDelegate + +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection { + NSParameterAssert(captureOutput == _videoDataOutput); + + if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(sampleBuffer) || + !CMSampleBufferDataIsReady(sampleBuffer)) { + return; + } + + CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + if (pixelBuffer == nil) { + return; + } + + // Default to portrait orientation on iPhone. + BOOL usingFrontCamera = NO; + // Check the image's EXIF for the camera the image came from as the image could have been + // delayed as we set alwaysDiscardsLateVideoFrames to NO. + AVCaptureDevicePosition cameraPosition = + [AVCaptureSession devicePositionForSampleBuffer:sampleBuffer]; + if (cameraPosition != AVCaptureDevicePositionUnspecified) { + usingFrontCamera = AVCaptureDevicePositionFront == cameraPosition; + } else { + AVCaptureDeviceInput *deviceInput = + (AVCaptureDeviceInput *)((AVCaptureInputPort *)connection.inputPorts.firstObject).input; + usingFrontCamera = AVCaptureDevicePositionFront == deviceInput.device.position; + } + switch (_orientation) { + case UIDeviceOrientationPortrait: + _rotation = RTCVideoRotation_90; + break; + case UIDeviceOrientationPortraitUpsideDown: + _rotation = RTCVideoRotation_270; + break; + case UIDeviceOrientationLandscapeLeft: + _rotation = usingFrontCamera ? RTCVideoRotation_180 : RTCVideoRotation_0; + break; + case UIDeviceOrientationLandscapeRight: + _rotation = usingFrontCamera ? RTCVideoRotation_0 : RTCVideoRotation_180; + break; + case UIDeviceOrientationFaceUp: + case UIDeviceOrientationFaceDown: + case UIDeviceOrientationUnknown: + // Ignore. + break; + } + + RTCCVPixelBuffer *rtcPixelBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBuffer]; + int64_t timeStampNs = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) * + kNanosecondsPerSecond; + RTCVideoFrame *videoFrame = [[RTCVideoFrame alloc] initWithBuffer:rtcPixelBuffer + rotation:_rotation + timeStampNs:timeStampNs]; + getObjCVideoSource(_source)->OnCapturedFrame(videoFrame); +} + +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection { + NSString *droppedReason = + (__bridge NSString *)CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_DroppedFrameReason, nil); + RTCLogError(@"Dropped sample buffer. Reason: %@", droppedReason); +} + +#pragma mark - AVCaptureSession notifications + +- (void)handleCaptureSessionInterruption:(NSNotification *)notification { + NSString *reasonString = nil; + NSNumber *reason = notification.userInfo[AVCaptureSessionInterruptionReasonKey]; + if (reason) { + switch (reason.intValue) { + case AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground: + reasonString = @"VideoDeviceNotAvailableInBackground"; + break; + case AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient: + reasonString = @"AudioDeviceInUseByAnotherClient"; + break; + case AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient: + reasonString = @"VideoDeviceInUseByAnotherClient"; + break; + case AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps: + reasonString = @"VideoDeviceNotAvailableWithMultipleForegroundApps"; + break; + } + } + RTCLog(@"Capture session interrupted: %@", reasonString); +} + +- (void)handleCaptureSessionInterruptionEnded:(NSNotification *)notification { + RTCLog(@"Capture session interruption ended."); +} + +- (void)handleCaptureSessionRuntimeError:(NSNotification *)notification { + NSError *error = [notification.userInfo objectForKey:AVCaptureSessionErrorKey]; + RTCLogError(@"Capture session runtime error: %@", error); + + [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + if (error.code == AVErrorMediaServicesWereReset) { + [self handleNonFatalError]; + } else { + [self handleFatalError]; + } + }]; +} + +- (void)handleCaptureSessionDidStartRunning:(NSNotification *)notification { + RTCLog(@"Capture session started."); + + [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + // If we successfully restarted after an unknown error, + // allow future retries on fatal errors. + _hasRetriedOnFatalError = NO; + }]; +} + +- (void)handleCaptureSessionDidStopRunning:(NSNotification *)notification { + RTCLog(@"Capture session stopped."); +} + +- (void)handleFatalError { + [RTCDispatcher + dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + if (!_hasRetriedOnFatalError) { + RTCLogWarning(@"Attempting to recover from fatal capture error."); + [self handleNonFatalError]; + _hasRetriedOnFatalError = YES; + } else { + RTCLogError(@"Previous fatal error recovery failed."); + } + }]; +} + +- (void)handleNonFatalError { + [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + RTCLog(@"Restarting capture session after error."); + if (_isRunning) { + [_captureSession startRunning]; + } + }]; +} + +#pragma mark - UIApplication notifications + +- (void)handleApplicationDidBecomeActive:(NSNotification *)notification { + [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession + block:^{ + if (_isRunning && !_captureSession.isRunning) { + RTCLog(@"Restarting capture session on active."); + [_captureSession startRunning]; + } + }]; +} + +#pragma mark - Private + +- (dispatch_queue_t)frameQueue { + if (!_frameQueue) { + _frameQueue = + dispatch_queue_create("org.webrtc.cameravideocapturer.video", DISPATCH_QUEUE_SERIAL); + dispatch_set_target_queue(_frameQueue, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); + } + return _frameQueue; +} + +- (BOOL)setupCaptureSession:(AVCaptureSession *)captureSession { + NSAssert(_captureSession == nil, @"Setup capture session called twice."); + _captureSession = captureSession; + _captureSession.sessionPreset = AVCaptureSessionPresetInputPriority; + _captureSession.usesApplicationAudioSession = NO; + [self setupVideoDataOutput]; + // Add the output. + if (![_captureSession canAddOutput:_videoDataOutput]) { + RTCLogError(@"Video data output unsupported."); + return NO; + } + [_captureSession addOutput:_videoDataOutput]; + + return YES; +} + +- (void)setupVideoDataOutput { + NSAssert(_videoDataOutput == nil, @"Setup video data output called twice."); + AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; + + // `videoDataOutput.availableVideoCVPixelFormatTypes` returns the pixel formats supported by the + // device with the most efficient output format first. Find the first format that we support. + NSSet *supportedPixelFormats = [RTCCVPixelBuffer supportedPixelFormats]; + NSMutableOrderedSet *availablePixelFormats = + [NSMutableOrderedSet orderedSetWithArray:videoDataOutput.availableVideoCVPixelFormatTypes]; + [availablePixelFormats intersectSet:supportedPixelFormats]; + NSNumber *pixelFormat = availablePixelFormats.firstObject; + NSAssert(pixelFormat, @"Output device has no supported formats."); + + _preferredOutputPixelFormat = [pixelFormat unsignedIntValue]; + _outputPixelFormat = _preferredOutputPixelFormat; + videoDataOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey : pixelFormat}; + videoDataOutput.alwaysDiscardsLateVideoFrames = NO; + [videoDataOutput setSampleBufferDelegate:self queue:self.frameQueue]; + _videoDataOutput = videoDataOutput; +} + +- (void)updateVideoDataOutputPixelFormat:(AVCaptureDeviceFormat *)format { + FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(format.formatDescription); + if (![[RTCCVPixelBuffer supportedPixelFormats] containsObject:@(mediaSubType)]) { + mediaSubType = _preferredOutputPixelFormat; + } + + if (mediaSubType != _outputPixelFormat) { + _outputPixelFormat = mediaSubType; + _videoDataOutput.videoSettings = + @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(mediaSubType) }; + } +} + +#pragma mark - Private, called inside capture queue + +- (void)updateDeviceCaptureFormat:(AVCaptureDeviceFormat *)format fps:(NSInteger)fps { + NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], + @"updateDeviceCaptureFormat must be called on the capture queue."); + @try { + _currentDevice.activeFormat = format; + _currentDevice.activeVideoMinFrameDuration = CMTimeMake(1, (int32_t)fps); + } @catch (NSException *exception) { + RTCLogError(@"Failed to set active format!\n User info:%@", exception.userInfo); + return; + } +} + +- (void)reconfigureCaptureSessionInput { + NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], + @"reconfigureCaptureSessionInput must be called on the capture queue."); + NSError *error = nil; + AVCaptureDeviceInput *input = + [AVCaptureDeviceInput deviceInputWithDevice:_currentDevice error:&error]; + if (!input) { + RTCLogError(@"Failed to create front camera input: %@", error.localizedDescription); + return; + } + [_captureSession beginConfiguration]; + for (AVCaptureDeviceInput *oldInput in [_captureSession.inputs copy]) { + [_captureSession removeInput:oldInput]; + } + if ([_captureSession canAddInput:input]) { + [_captureSession addInput:input]; + } else { + RTCLogError(@"Cannot add camera as an input to the session."); + } + [_captureSession commitConfiguration]; +} + +- (void)updateOrientation { + NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], + @"updateOrientation must be called on the capture queue."); + _orientation = [UIDevice currentDevice].orientation; +} + +@end diff --git a/submodules/TgVoipWebrtc/Impl/VideoMetalView.h b/submodules/TgVoipWebrtc/Impl/VideoMetalView.h new file mode 100644 index 0000000000..3425ec74f8 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/VideoMetalView.h @@ -0,0 +1,26 @@ +#ifndef VIDEOMETALVIEW_H +#define VIDEOMETALVIEW_H + +#import +#import + +#import "api/media_stream_interface.h" + +@class RTCVideoFrame; + +@interface VideoMetalView : UIView + +@property(nonatomic) UIViewContentMode videoContentMode; +@property(nonatomic, getter=isEnabled) BOOL enabled; +@property(nonatomic, nullable) NSValue* rotationOverride; + +- (void)setSize:(CGSize)size; +- (void)renderFrame:(nullable RTCVideoFrame *)frame; + +- (void)addToTrack:(rtc::scoped_refptr)track; + +- (rtc::VideoSinkInterface *)getSink; + +@end + +#endif diff --git a/submodules/TgVoipWebrtc/Impl/VideoMetalView.mm b/submodules/TgVoipWebrtc/Impl/VideoMetalView.mm new file mode 100644 index 0000000000..32616ffb67 --- /dev/null +++ b/submodules/TgVoipWebrtc/Impl/VideoMetalView.mm @@ -0,0 +1,278 @@ +#import "VideoMetalView.h" + +#import +#import + +#import "base/RTCLogging.h" +#import "base/RTCVideoFrame.h" +#import "base/RTCVideoFrameBuffer.h" +#import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#include "sdk/objc/native/api/video_frame.h" + +#import "api/video/video_sink_interface.h" +#import "api/media_stream_interface.h" + +#import "RTCMTLI420Renderer.h" +#import "RTCMTLNV12Renderer.h" +#import "RTCMTLRGBRenderer.h" + +#define MTKViewClass NSClassFromString(@"MTKView") +#define RTCMTLNV12RendererClass NSClassFromString(@"RTCMTLNV12Renderer") +#define RTCMTLI420RendererClass NSClassFromString(@"RTCMTLI420Renderer") +#define RTCMTLRGBRendererClass NSClassFromString(@"RTCMTLRGBRenderer") + +class VideoRendererAdapterImpl : public rtc::VideoSinkInterface { + public: + VideoRendererAdapterImpl(VideoMetalView *adapter) { + adapter_ = adapter; + size_ = CGSizeZero; + } + + void OnFrame(const webrtc::VideoFrame& nativeVideoFrame) override { + RTCVideoFrame* videoFrame = NativeToObjCVideoFrame(nativeVideoFrame); + + CGSize current_size = (videoFrame.rotation % 180 == 0) ? CGSizeMake(videoFrame.width, videoFrame.height) : CGSizeMake(videoFrame.height, videoFrame.width); + + if (!CGSizeEqualToSize(size_, current_size)) { + size_ = current_size; + [adapter_ setSize:size_]; + } + [adapter_ renderFrame:videoFrame]; + } + +private: + __weak VideoMetalView *adapter_; + CGSize size_; +}; + +@interface VideoMetalView () { + RTCMTLI420Renderer *_rendererI420; + RTCMTLNV12Renderer *_rendererNV12; + RTCMTLRGBRenderer *_rendererRGB; + MTKView *_metalView; + RTCVideoFrame *_videoFrame; + CGSize _videoFrameSize; + int64_t _lastFrameTimeNs; + + std::unique_ptr _sink; +} + +@end + +@implementation VideoMetalView + +- (instancetype)initWithFrame:(CGRect)frameRect { + self = [super initWithFrame:frameRect]; + if (self) { + [self configure]; + + _sink.reset(new VideoRendererAdapterImpl(self)); + } + return self; +} + +- (BOOL)isEnabled { + return !_metalView.paused; +} + +- (void)setEnabled:(BOOL)enabled { + _metalView.paused = !enabled; +} + +- (UIViewContentMode)videoContentMode { + return _metalView.contentMode; +} + +- (void)setVideoContentMode:(UIViewContentMode)mode { + _metalView.contentMode = mode; +} + +#pragma mark - Private + ++ (BOOL)isMetalAvailable { + return MTLCreateSystemDefaultDevice() != nil; +} + ++ (MTKView *)createMetalView:(CGRect)frame { + return [[MTKViewClass alloc] initWithFrame:frame]; +} + ++ (RTCMTLNV12Renderer *)createNV12Renderer { + return [[RTCMTLNV12RendererClass alloc] init]; +} + ++ (RTCMTLI420Renderer *)createI420Renderer { + return [[RTCMTLI420RendererClass alloc] init]; +} + ++ (RTCMTLRGBRenderer *)createRGBRenderer { + return [[RTCMTLRGBRenderer alloc] init]; +} + +- (void)configure { + NSAssert([VideoMetalView isMetalAvailable], @"Metal not availiable on this device"); + + _metalView = [VideoMetalView createMetalView:self.bounds]; + _metalView.delegate = self; + _metalView.contentMode = UIViewContentModeScaleAspectFill; + [self addSubview:_metalView]; + _videoFrameSize = CGSizeZero; +} + +- (void)setMultipleTouchEnabled:(BOOL)multipleTouchEnabled { + [super setMultipleTouchEnabled:multipleTouchEnabled]; + _metalView.multipleTouchEnabled = multipleTouchEnabled; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + CGRect bounds = self.bounds; + _metalView.frame = bounds; + if (!CGSizeEqualToSize(_videoFrameSize, CGSizeZero)) { + _metalView.drawableSize = [self drawableSize]; + } else { + _metalView.drawableSize = bounds.size; + } +} + +#pragma mark - MTKViewDelegate methods + +- (void)drawInMTKView:(nonnull MTKView *)view { + NSAssert(view == _metalView, @"Receiving draw callbacks from foreign instance."); + RTCVideoFrame *videoFrame = _videoFrame; + // Skip rendering if we've already rendered this frame. + if (!videoFrame || videoFrame.timeStampNs == _lastFrameTimeNs) { + return; + } + + if (CGRectIsEmpty(view.bounds)) { + return; + } + + RTCMTLRenderer *renderer; + if ([videoFrame.buffer isKindOfClass:[RTCCVPixelBuffer class]]) { + RTCCVPixelBuffer *buffer = (RTCCVPixelBuffer*)videoFrame.buffer; + const OSType pixelFormat = CVPixelBufferGetPixelFormatType(buffer.pixelBuffer); + if (pixelFormat == kCVPixelFormatType_32BGRA || pixelFormat == kCVPixelFormatType_32ARGB) { + if (!_rendererRGB) { + _rendererRGB = [VideoMetalView createRGBRenderer]; + if (![_rendererRGB addRenderingDestination:_metalView]) { + _rendererRGB = nil; + RTCLogError(@"Failed to create RGB renderer"); + return; + } + } + renderer = _rendererRGB; + } else { + if (!_rendererNV12) { + _rendererNV12 = [VideoMetalView createNV12Renderer]; + if (![_rendererNV12 addRenderingDestination:_metalView]) { + _rendererNV12 = nil; + RTCLogError(@"Failed to create NV12 renderer"); + return; + } + } + renderer = _rendererNV12; + } + } else { + if (!_rendererI420) { + _rendererI420 = [VideoMetalView createI420Renderer]; + if (![_rendererI420 addRenderingDestination:_metalView]) { + _rendererI420 = nil; + RTCLogError(@"Failed to create I420 renderer"); + return; + } + } + renderer = _rendererI420; + } + + renderer.rotationOverride = _rotationOverride; + + [renderer drawFrame:videoFrame]; + _lastFrameTimeNs = videoFrame.timeStampNs; +} + +- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { +} + +#pragma mark - + +- (void)setRotationOverride:(NSValue *)rotationOverride { + _rotationOverride = rotationOverride; + + _metalView.drawableSize = [self drawableSize]; + [self setNeedsLayout]; +} + +- (RTCVideoRotation)frameRotation { + if (_rotationOverride) { + RTCVideoRotation rotation; + if (@available(iOS 11, *)) { + [_rotationOverride getValue:&rotation size:sizeof(rotation)]; + } else { + [_rotationOverride getValue:&rotation]; + } + return rotation; + } + + return _videoFrame.rotation; +} + +- (CGSize)drawableSize { + // Flip width/height if the rotations are not the same. + CGSize videoFrameSize = _videoFrameSize; + RTCVideoRotation frameRotation = [self frameRotation]; + + BOOL useLandscape = + (frameRotation == RTCVideoRotation_0) || (frameRotation == RTCVideoRotation_180); + BOOL sizeIsLandscape = (_videoFrame.rotation == RTCVideoRotation_0) || + (_videoFrame.rotation == RTCVideoRotation_180); + + if (useLandscape == sizeIsLandscape) { + return videoFrameSize; + } else { + return CGSizeMake(videoFrameSize.height, videoFrameSize.width); + } +} + +#pragma mark - RTCVideoRenderer + +- (void)setSize:(CGSize)size { + __weak VideoMetalView *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + __strong VideoMetalView *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + + strongSelf->_videoFrameSize = size; + CGSize drawableSize = [strongSelf drawableSize]; + + strongSelf->_metalView.drawableSize = drawableSize; + [strongSelf setNeedsLayout]; + //[strongSelf.delegate videoView:self didChangeVideoSize:size]; + }); +} + +- (void)renderFrame:(nullable RTCVideoFrame *)frame { + if (!self.isEnabled) { + return; + } + + if (frame == nil) { + RTCLogInfo(@"Incoming frame is nil. Exiting render callback."); + return; + } + _videoFrame = frame; +} + +- (void)addToTrack:(rtc::scoped_refptr)track { + track->AddOrUpdateSink(_sink.get(), rtc::VideoSinkWants()); +} + +- (rtc::VideoSinkInterface *)getSink { + return _sink.get(); +} + +@end diff --git a/submodules/TgVoipWebrtc/Impl/VideoRendererAdapter.h b/submodules/TgVoipWebrtc/Impl/VideoRendererAdapter.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/submodules/TgVoipWebrtc/Impl/VideoRendererAdapter.mm b/submodules/TgVoipWebrtc/Impl/VideoRendererAdapter.mm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h index 15c752dfc0..0ba7cb28da 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h @@ -2,6 +2,7 @@ #define OngoingCallContext_h #import +#import @interface OngoingCallConnectionDescriptionWebrtc : NSObject @@ -75,6 +76,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) { - (void)setIsMuted:(bool)isMuted; - (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType; +- (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion; @end diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 85ab31235d..d019867d6b 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -322,5 +322,18 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } } +- (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion { + if (_tgVoip) { + VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 240.0f)]; + remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill; + + _tgVoip->AttachVideoView(remoteRenderer); + + dispatch_async(dispatch_get_main_queue(), ^{ + completion(remoteRenderer); + }); + } +} + @end From 8a95d73fa2225a06dd44118393f2d99873eda259 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 9 Jun 2020 16:14:47 +0400 Subject: [PATCH 7/8] Temp --- submodules/TgVoipWebrtc/Impl/Controller.mm | 8 +++--- .../Impl/{Endpoint.cpp => Endpoint.mm} | 1 + .../TgVoipWebrtc/Impl/MediaEngineWebrtc.mm | 26 +++++++++---------- submodules/TgVoipWebrtc/Impl/TgVoip.mm | 3 +++ 4 files changed, 21 insertions(+), 17 deletions(-) rename submodules/TgVoipWebrtc/Impl/{Endpoint.cpp => Endpoint.mm} (99%) diff --git a/submodules/TgVoipWebrtc/Impl/Controller.mm b/submodules/TgVoipWebrtc/Impl/Controller.mm index 808c4aa7ac..e042e528ad 100644 --- a/submodules/TgVoipWebrtc/Impl/Controller.mm +++ b/submodules/TgVoipWebrtc/Impl/Controller.mm @@ -20,9 +20,9 @@ Controller::Controller(bool is_outgoing, const EncryptionKey& encryption_key, si : thread(rtc::Thread::Create()) , connector(std::make_unique(std::make_unique(encryption_key, is_outgoing))) , state(State::Starting) -, is_outgoing(is_outgoing) , last_recv_time(rtc::TimeMillis()) , last_send_time(rtc::TimeMillis()) +, is_outgoing(is_outgoing) , init_timeout(init_timeout * 1000) , reconnect_timeout(reconnect_timeout * 1000) , local_datasaving(false) @@ -37,9 +37,6 @@ Controller::Controller(bool is_outgoing, const EncryptionKey& encryption_key, si Controller::~Controller() { thread->Invoke(RTC_FROM_HERE, [this]() { media = nullptr; -#ifdef TGVOIP_PREPROCESSED_OUTPUT - preproc = nullptr; -#endif connector = nullptr; }); } @@ -111,11 +108,14 @@ void Controller::NewMessage(const message::Base& msg) { } }); if (!webrtc::RtpUtility::RtpHeaderParser(msg_rtp.data.data(), msg_rtp.data.size()).RTCP()) { + //printf("rtp received size %d\n", (int)(msg_rtp.data.size())); last_recv_time = rtc::TimeMillis(); if (state == State::Reconnecting) { state = State::Established; SignalNewState(state); } + } else { + //printf("rtcp received size %d\n", (int)(msg_rtp.data.size())); } } else if (msg.ID == message::tBufferOverflow || msg.ID == message::tPacketIncorrect || diff --git a/submodules/TgVoipWebrtc/Impl/Endpoint.cpp b/submodules/TgVoipWebrtc/Impl/Endpoint.mm similarity index 99% rename from submodules/TgVoipWebrtc/Impl/Endpoint.cpp rename to submodules/TgVoipWebrtc/Impl/Endpoint.mm index 4e26c0d941..7ef75142c0 100644 --- a/submodules/TgVoipWebrtc/Impl/Endpoint.cpp +++ b/submodules/TgVoipWebrtc/Impl/Endpoint.mm @@ -91,6 +91,7 @@ void EndpointRelayObfuscatedTcp::Close(rtc::AsyncPacketSocket *, int) { void EndpointRelayObfuscatedTcp::RecvPacket(rtc::AsyncPacketSocket *socket, const char *data, size_t packet_len, const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us) { EndpointBase::RecvPacket(socket, data, packet_len, remote_addr, packet_time_us); + do { if (in_remains > in_buffer->Length()) break; diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm index 1229fc91b0..d72078c985 100644 --- a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm @@ -177,9 +177,9 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) rtc::scoped_refptr objCVideoTrackSource(new rtc::RefCountedObject()); _nativeVideoSource = webrtc::VideoTrackSourceProxy::Create(signaling_thread.get(), worker_thread.get(), objCVideoTrackSource); - codec.SetParam(cricket::kCodecParamMinBitrate, 300000); - codec.SetParam(cricket::kCodecParamStartBitrate, 300000); - codec.SetParam(cricket::kCodecParamMaxBitrate, 600000); + codec.SetParam(cricket::kCodecParamMinBitrate, 32); + codec.SetParam(cricket::kCodecParamStartBitrate, 300); + codec.SetParam(cricket::kCodecParamMaxBitrate, 1000); #if TARGET_IPHONE_SIMULATOR #else @@ -242,9 +242,9 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) //send_parameters.options.auto_gain_control = params.auto_gain_control; //send_parameters.options.highpass_filter = false; //send_parameters.options.typing_detection = false; - send_parameters.max_bandwidth_bps = 300000; - send_parameters.rtcp.reduced_size = true; - send_parameters.rtcp.remote_estimate = true; + //send_parameters.max_bandwidth_bps = 800000; + //send_parameters.rtcp.reduced_size = true; + //send_parameters.rtcp.remote_estimate = true; video_channel->SetSendParameters(send_parameters); video_channel->SetVideoSend(ssrc_send_video, NULL, _nativeVideoSource.get()); @@ -270,15 +270,15 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) if (recv) { for (auto codec : videoCodecs) { if (codec.id == 96 && codec.name == cricket::kH264CodecName) { - codec.SetParam(cricket::kCodecParamMinBitrate, 300000); - codec.SetParam(cricket::kCodecParamStartBitrate, 300000); - codec.SetParam(cricket::kCodecParamMaxBitrate, 600000); + codec.SetParam(cricket::kCodecParamMinBitrate, 32); + codec.SetParam(cricket::kCodecParamStartBitrate, 300); + codec.SetParam(cricket::kCodecParamMaxBitrate, 1000); cricket::VideoRecvParameters recv_parameters; recv_parameters.codecs.emplace_back(codec); recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); - recv_parameters.rtcp.reduced_size = true; - recv_parameters.rtcp.remote_estimate = true; + //recv_parameters.rtcp.reduced_size = true; + //recv_parameters.rtcp.remote_estimate = true; video_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv_video)); video_channel->SetRecvParameters(recv_parameters); @@ -324,11 +324,11 @@ void MediaEngineWebrtc::SetNetworkParams(const MediaEngineWebrtc::NetworkParams& // send_parameters.max_bandwidth_bps = 16000; send_parameters.rtcp.reduced_size = true; send_parameters.rtcp.remote_estimate = true; - voice_channel->SetSendParameters(send_parameters); + //voice_channel->SetSendParameters(send_parameters); } void MediaEngineWebrtc::SetMute(bool mute) { - voice_channel->SetAudioSend(ssrc_send, !mute, nullptr, &audio_source); + //voice_channel->SetAudioSend(ssrc_send, !mute, nullptr, &audio_source); } void MediaEngineWebrtc::AttachVideoView(VideoMetalView *videoView) { diff --git a/submodules/TgVoipWebrtc/Impl/TgVoip.mm b/submodules/TgVoipWebrtc/Impl/TgVoip.mm index 0b4e5d87eb..f1046f705c 100644 --- a/submodules/TgVoipWebrtc/Impl/TgVoip.mm +++ b/submodules/TgVoipWebrtc/Impl/TgVoip.mm @@ -151,6 +151,9 @@ public: } controller_->AddEndpoint(addr, endpoint.peerTag, type); } + /*rtc::SocketAddress addr("192.168.8.118", 7325); + unsigned char peerTag[16]; + controller_->AddEndpoint(addr, peerTag, Controller::EndpointType::P2P);*/ setNetworkType(initialNetworkType); From 637507137a9268e1319b58f561e43d8f66f252dc Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 9 Jun 2020 22:49:45 +0400 Subject: [PATCH 8/8] Temp --- .../TgVoipWebrtc/Impl/MediaEngineWebrtc.mm | 34 +++++++++++++------ third-party/webrtc/build-webrtc-bazel.sh | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm index d72078c985..5f82c6cf31 100644 --- a/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm +++ b/submodules/TgVoipWebrtc/Impl/MediaEngineWebrtc.mm @@ -18,6 +18,8 @@ #include "sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.h" #include "sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.h" +#include "sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.h" +#include "sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.h" #include "sdk/objc/native/api/video_encoder_factory.h" #include "sdk/objc/native/api/video_decoder_factory.h" @@ -67,7 +69,7 @@ static void AddDefaultFeedbackParams(cricket::VideoCodec* codec) { } } -static std::vector AssignPayloadTypesAndDefaultCodecs(std::vector input_formats) { +static std::vector AssignPayloadTypesAndDefaultCodecs(std::vector input_formats, int32_t &outCodecId) { if (input_formats.empty()) return std::vector(); static const int kFirstDynamicPayloadType = 96; @@ -94,6 +96,13 @@ static std::vector AssignPayloadTypesAndDefaultCodecs(std:: codec.id = payload_type; AddDefaultFeedbackParams(&codec); output_codecs.push_back(codec); + + if (codec.name == cricket::kVp9CodecName) { + //outCodecId = codec.id; + } + if (codec.name == cricket::kH264CodecName) { + outCodecId = codec.id; + } // Increment payload type. ++payload_type; @@ -143,11 +152,13 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) media_deps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory(); media_deps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory(); - auto video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCVideoEncoderFactoryH264 alloc] init]); - std::vector videoCodecs = AssignPayloadTypesAndDefaultCodecs(video_encoder_factory->GetSupportedFormats()); + //auto video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCVideoEncoderFactoryH264 alloc] init]); + auto video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCDefaultVideoEncoderFactory alloc] init]); + int32_t outCodecId = 96; + std::vector videoCodecs = AssignPayloadTypesAndDefaultCodecs(video_encoder_factory->GetSupportedFormats(), outCodecId); - media_deps.video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCVideoEncoderFactoryH264 alloc] init]); - media_deps.video_decoder_factory = webrtc::ObjCToNativeVideoDecoderFactory([[RTCVideoDecoderFactoryH264 alloc] init]); + media_deps.video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCDefaultVideoEncoderFactory alloc] init]); + media_deps.video_decoder_factory = webrtc::ObjCToNativeVideoDecoderFactory([[RTCDefaultVideoDecoderFactory alloc] init]); media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create(); media_engine = cricket::CreateMediaEngine(std::move(media_deps)); @@ -169,17 +180,18 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) voice_channel->OnReadyToSend(true); voice_channel->SetSend(true); } + if (send) { video_channel->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc_send_video)); for (auto codec : videoCodecs) { - if (codec.id == 96 && codec.name == cricket::kH264CodecName) { + if (codec.id == outCodecId) { rtc::scoped_refptr objCVideoTrackSource(new rtc::RefCountedObject()); _nativeVideoSource = webrtc::VideoTrackSourceProxy::Create(signaling_thread.get(), worker_thread.get(), objCVideoTrackSource); codec.SetParam(cricket::kCodecParamMinBitrate, 32); - codec.SetParam(cricket::kCodecParamStartBitrate, 300); - codec.SetParam(cricket::kCodecParamMaxBitrate, 1000); + codec.SetParam(cricket::kCodecParamStartBitrate, 100); + codec.SetParam(cricket::kCodecParamMaxBitrate, 1500); #if TARGET_IPHONE_SIMULATOR #else @@ -244,7 +256,7 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) //send_parameters.options.typing_detection = false; //send_parameters.max_bandwidth_bps = 800000; //send_parameters.rtcp.reduced_size = true; - //send_parameters.rtcp.remote_estimate = true; + send_parameters.rtcp.remote_estimate = true; video_channel->SetSendParameters(send_parameters); video_channel->SetVideoSend(ssrc_send_video, NULL, _nativeVideoSource.get()); @@ -269,7 +281,7 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) } if (recv) { for (auto codec : videoCodecs) { - if (codec.id == 96 && codec.name == cricket::kH264CodecName) { + if (codec.id == outCodecId) { codec.SetParam(cricket::kCodecParamMinBitrate, 32); codec.SetParam(cricket::kCodecParamStartBitrate, 300); codec.SetParam(cricket::kCodecParamMaxBitrate, 1000); @@ -278,7 +290,7 @@ MediaEngineWebrtc::MediaEngineWebrtc(bool outgoing, bool send, bool recv) recv_parameters.codecs.emplace_back(codec); recv_parameters.extensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, extension_sequence); //recv_parameters.rtcp.reduced_size = true; - //recv_parameters.rtcp.remote_estimate = true; + recv_parameters.rtcp.remote_estimate = true; video_channel->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc_recv_video)); video_channel->SetRecvParameters(recv_parameters); diff --git a/third-party/webrtc/build-webrtc-bazel.sh b/third-party/webrtc/build-webrtc-bazel.sh index 2a9fbebdfe..a0cb411e82 100755 --- a/third-party/webrtc/build-webrtc-bazel.sh +++ b/third-party/webrtc/build-webrtc-bazel.sh @@ -23,7 +23,7 @@ if [ "$ARCH" == "x64" ]; then OUT_DIR="ios_sim" fi -gn gen out/$OUT_DIR --args="use_xcode_clang=true "" target_cpu=\"$ARCH\""' target_os="ios" is_debug=true is_component_build=false rtc_include_tests=false use_rtti=true rtc_use_x11=false use_custom_libcxx=false use_custom_libcxx_for_host=false rtc_include_builtin_video_codecs=false rtc_build_ssl=false rtc_build_examples=false rtc_build_tools=false ios_deployment_target="9.0" ios_enable_code_signing=false is_unsafe_developer_build=false rtc_enable_protobuf=false rtc_include_builtin_video_codecs=false rtc_use_gtk=false rtc_use_metal_rendering=true rtc_ssl_root="//openssl"' +gn gen out/$OUT_DIR --args="use_xcode_clang=true "" target_cpu=\"$ARCH\""' target_os="ios" is_debug=true is_component_build=false rtc_include_tests=false use_rtti=true rtc_use_x11=false use_custom_libcxx=false use_custom_libcxx_for_host=false rtc_build_ssl=false rtc_build_examples=false rtc_build_tools=false ios_deployment_target="9.0" ios_enable_code_signing=false is_unsafe_developer_build=false rtc_enable_protobuf=false rtc_include_builtin_video_codecs=true rtc_build_libvpx=true rtc_libvpx_build_vp9=true rtc_use_gtk=false rtc_use_metal_rendering=true rtc_ssl_root="//openssl"' ninja -C out/$OUT_DIR framework_objc_static popd