From a422fac01fc8da8e3d5ea5ec7f580be51056cba7 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 2 Jun 2020 19:49:54 +0400 Subject: [PATCH] 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.