From 62c92b58276ee5ecdb4bea7de0ef93efe7bf0209 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 20 Jun 2024 23:00:16 +0200 Subject: [PATCH 01/48] WebRTC Version Bump --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d02091a..45c6b22b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ endif () include(ExternalProject) # https://chromiumdash.appspot.com/branches -set(WEBRTC_REVISION m123.6312.3.5) +set(WEBRTC_REVISION m125.6422.2.3) set(BOOST_REVISION 1.85.0) set(BOOST_LIBS filesystem) From 95c9e2452a58d8585c105d7b404ef3093300de84 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 20 Jun 2024 23:01:02 +0200 Subject: [PATCH 02/48] Updated Video Decoders an Encoders --- wrtc/video_factory/software/google/google.cpp | 7 ++++--- wrtc/video_factory/software/vlc/vlc.cpp | 4 +++- wrtc/video_factory/video_decoder_config.cpp | 8 +++----- wrtc/video_factory/video_decoder_config.hpp | 2 +- wrtc/video_factory/video_decoder_factory.cpp | 5 ++--- wrtc/video_factory/video_decoder_factory.hpp | 2 +- wrtc/video_factory/video_encoder_config.cpp | 5 ++--- wrtc/video_factory/video_encoder_config.hpp | 2 +- wrtc/video_factory/video_encoder_factory.cpp | 5 ++--- wrtc/video_factory/video_encoder_factory.hpp | 2 +- 10 files changed, 20 insertions(+), 22 deletions(-) diff --git a/wrtc/video_factory/software/google/google.cpp b/wrtc/video_factory/software/google/google.cpp index ed53be12..f444ee77 100644 --- a/wrtc/video_factory/software/google/google.cpp +++ b/wrtc/video_factory/software/google/google.cpp @@ -4,6 +4,7 @@ #include "google.hpp" +#include #include #include @@ -13,13 +14,13 @@ namespace google { encoders.emplace_back( webrtc::kVideoCodecVP8, [](auto) { - return webrtc::VP8Encoder::Create(); + return CreateVp8Encoder(webrtc::CreateEnvironment()); } ); encoders.emplace_back( webrtc::kVideoCodecVP9, [](auto) { - return webrtc::VP8Encoder::Create(); + return CreateVp9Encoder(webrtc::CreateEnvironment()); } ); } @@ -28,7 +29,7 @@ namespace google { decoders.emplace_back( webrtc::kVideoCodecVP8, [](auto) { - return webrtc::VP8Decoder::Create(); + return CreateVp8Decoder(webrtc::CreateEnvironment()); } ); decoders.emplace_back( diff --git a/wrtc/video_factory/software/vlc/vlc.cpp b/wrtc/video_factory/software/vlc/vlc.cpp index 772e070e..278da0ae 100644 --- a/wrtc/video_factory/software/vlc/vlc.cpp +++ b/wrtc/video_factory/software/vlc/vlc.cpp @@ -4,6 +4,8 @@ #include "vlc.hpp" +#include + #if !defined(__arm__) || defined(__aarch64__) || defined(__ARM_NEON__) #include #include @@ -16,7 +18,7 @@ namespace vlc { encoders.emplace_back( webrtc::kVideoCodecAV1, [](auto) { - return webrtc::CreateLibaomAv1Encoder(); + return CreateLibaomAv1Encoder(webrtc::CreateEnvironment()); } ); #endif diff --git a/wrtc/video_factory/video_decoder_config.cpp b/wrtc/video_factory/video_decoder_config.cpp index 550c527d..d61774a0 100644 --- a/wrtc/video_factory/video_decoder_config.cpp +++ b/wrtc/video_factory/video_decoder_config.cpp @@ -29,13 +29,11 @@ namespace wrtc { decoder = nullptr; } - std::unique_ptr VideoDecoderConfig::CreateVideoCodec(const webrtc::SdpVideoFormat& format) const - { + std::unique_ptr VideoDecoderConfig::CreateVideoCodec(const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) const { if (factory) { - return factory->CreateVideoDecoder(format); - } else { - return decoder(format); + return factory->Create(env, format); } + return decoder(format); } } // wrtc \ No newline at end of file diff --git a/wrtc/video_factory/video_decoder_config.hpp b/wrtc/video_factory/video_decoder_config.hpp index 591150a2..4434800e 100644 --- a/wrtc/video_factory/video_decoder_config.hpp +++ b/wrtc/video_factory/video_decoder_config.hpp @@ -22,7 +22,7 @@ namespace wrtc { explicit VideoDecoderConfig(std::unique_ptr factory): factory(std::move(factory)) {} - [[nodiscard]] std::unique_ptr CreateVideoCodec(const webrtc::SdpVideoFormat& format) const; + [[nodiscard]] std::unique_ptr CreateVideoCodec(const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) const; private: DecoderCallback decoder; diff --git a/wrtc/video_factory/video_decoder_factory.cpp b/wrtc/video_factory/video_decoder_factory.cpp index 43376e11..21f50952 100644 --- a/wrtc/video_factory/video_decoder_factory.cpp +++ b/wrtc/video_factory/video_decoder_factory.cpp @@ -8,13 +8,12 @@ namespace wrtc { // TODO: Needed template like this: // https://github.com/pytgcalls/ntgcalls/blob/85ee93f72f223405174759b23eb222373e0bc775/wrtc/video_factory/base_video_factory.cpp - std::unique_ptr - VideoDecoderFactory::CreateVideoDecoder(const webrtc::SdpVideoFormat &format) { + std::unique_ptr VideoDecoderFactory::Create(const webrtc::Environment& env, const webrtc::SdpVideoFormat &format) { int n = 0; for (const auto& enc : decoders) { for (auto supported_formats = formats_[n++]; const auto& f : supported_formats) { if (f.IsSameCodec(format)) { - return enc.CreateVideoCodec(format); + return enc.CreateVideoCodec(env, format); } } } diff --git a/wrtc/video_factory/video_decoder_factory.hpp b/wrtc/video_factory/video_decoder_factory.hpp index 15200a94..f83d5232 100644 --- a/wrtc/video_factory/video_decoder_factory.hpp +++ b/wrtc/video_factory/video_decoder_factory.hpp @@ -16,7 +16,7 @@ namespace wrtc { std::vector decoders; mutable std::vector> formats_; - std::unique_ptr CreateVideoDecoder(const webrtc::SdpVideoFormat &format) override; + std::unique_ptr Create(const webrtc::Environment& env, const webrtc::SdpVideoFormat &format) override; std::vector GetSupportedFormats() const override; }; diff --git a/wrtc/video_factory/video_encoder_config.cpp b/wrtc/video_factory/video_encoder_config.cpp index a6d280ed..9d79a276 100644 --- a/wrtc/video_factory/video_encoder_config.cpp +++ b/wrtc/video_factory/video_encoder_config.cpp @@ -26,10 +26,9 @@ namespace wrtc { return factory->GetSupportedFormats(); } - std::unique_ptr VideoEncoderConfig::CreateVideoCodec(const webrtc::SdpVideoFormat& format) const - { + std::unique_ptr VideoEncoderConfig::CreateVideoCodec(const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) const { if (factory) { - return factory->CreateVideoEncoder(format); + return factory->Create(env, format); } return encoder(format); } diff --git a/wrtc/video_factory/video_encoder_config.hpp b/wrtc/video_factory/video_encoder_config.hpp index b3eeffc5..adcbd9e0 100644 --- a/wrtc/video_factory/video_encoder_config.hpp +++ b/wrtc/video_factory/video_encoder_config.hpp @@ -25,7 +25,7 @@ namespace wrtc { explicit VideoEncoderConfig(std::unique_ptr factory): factory(std::move(factory)) {} - [[nodiscard]] std::unique_ptr CreateVideoCodec(const webrtc::SdpVideoFormat &format) const; + [[nodiscard]] std::unique_ptr CreateVideoCodec(const webrtc::Environment& env, const webrtc::SdpVideoFormat &format) const; private: EncoderCallback encoder; diff --git a/wrtc/video_factory/video_encoder_factory.cpp b/wrtc/video_factory/video_encoder_factory.cpp index c426ba5b..e4f77740 100644 --- a/wrtc/video_factory/video_encoder_factory.cpp +++ b/wrtc/video_factory/video_encoder_factory.cpp @@ -8,13 +8,12 @@ namespace wrtc { // TODO: Needed template like this: // https://github.com/pytgcalls/ntgcalls/blob/85ee93f72f223405174759b23eb222373e0bc775/wrtc/video_factory/base_video_factory.cpp - std::unique_ptr - VideoEncoderFactory::CreateVideoEncoder(const webrtc::SdpVideoFormat &format) { + std::unique_ptr VideoEncoderFactory::Create(const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) { int n = 0; for (const auto& enc : encoders) { for (auto supported_formats = formats_[n++]; const auto& f : supported_formats) { if (f.IsSameCodec(format)) { - return enc.CreateVideoCodec(format); + return enc.CreateVideoCodec(env, format); } } } diff --git a/wrtc/video_factory/video_encoder_factory.hpp b/wrtc/video_factory/video_encoder_factory.hpp index ee7d1f42..c34abbe3 100644 --- a/wrtc/video_factory/video_encoder_factory.hpp +++ b/wrtc/video_factory/video_encoder_factory.hpp @@ -18,7 +18,7 @@ namespace wrtc { std::vector encoders; mutable std::vector> formats_; - std::unique_ptr CreateVideoEncoder(const webrtc::SdpVideoFormat &format) override; + std::unique_ptr Create(const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) override; std::vector GetSupportedFormats() const override; }; From ea45bbb68024965521dfff9aca0a8adbb2e823fd Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 20 Jun 2024 23:02:46 +0200 Subject: [PATCH 03/48] Fixed missing Environment to SignalingSCTP --- ntgcalls/instances/p2p_call.cpp | 1 + ntgcalls/signaling/signaling.cpp | 3 ++- ntgcalls/signaling/signaling.hpp | 1 + ntgcalls/signaling/signaling_packet_transport.cpp | 7 ++++++- ntgcalls/signaling/signaling_sctp_connection.cpp | 5 ++++- ntgcalls/signaling/signaling_sctp_connection.hpp | 2 ++ wrtc/interfaces/network_interface.cpp | 4 ++++ wrtc/interfaces/network_interface.hpp | 2 ++ 8 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ntgcalls/instances/p2p_call.cpp b/ntgcalls/instances/p2p_call.cpp index ea745bb1..c65b9fa6 100644 --- a/ntgcalls/instances/p2p_call.cpp +++ b/ntgcalls/instances/p2p_call.cpp @@ -114,6 +114,7 @@ namespace ntgcalls { protocolVersion, connection->networkThread(), connection->signalingThread(), + connection->environment(), signaling::EncryptionKey(std::move(encryptionKey), type() == Type::Outgoing), [this](const bytes::binary &data) { (void) onEmitData(data); diff --git a/ntgcalls/signaling/signaling.cpp b/ntgcalls/signaling/signaling.cpp index ed846327..91fe84f0 100644 --- a/ntgcalls/signaling/signaling.cpp +++ b/ntgcalls/signaling/signaling.cpp @@ -12,6 +12,7 @@ namespace signaling { const Version version, rtc::Thread* networkThread, rtc::Thread* signalingThread, + const webrtc::Environment& env, const EncryptionKey &key, const DataEmitter& onEmitData, const DataReceiver& onSignalData @@ -22,7 +23,7 @@ namespace signaling { } if (version & Version::V2Full) { RTC_LOG(LS_INFO) << "Using signaling V2 Full"; - return std::make_unique(networkThread, signalingThread, key, onEmitData, onSignalData, true); + return std::make_unique(networkThread, signalingThread, env, key, onEmitData, onSignalData, true); } throw ntgcalls::SignalingUnsupported("Unsupported protocol version"); } diff --git a/ntgcalls/signaling/signaling.hpp b/ntgcalls/signaling/signaling.hpp index 4ed56f54..90af1a4e 100644 --- a/ntgcalls/signaling/signaling.hpp +++ b/ntgcalls/signaling/signaling.hpp @@ -22,6 +22,7 @@ namespace signaling { Version version, rtc::Thread* networkThread, rtc::Thread* signalingThread, + const webrtc::Environment& env, const EncryptionKey &key, const DataEmitter& onEmitData, const DataReceiver& onSignalData diff --git a/ntgcalls/signaling/signaling_packet_transport.cpp b/ntgcalls/signaling/signaling_packet_transport.cpp index d64f773d..e17412ae 100644 --- a/ntgcalls/signaling/signaling_packet_transport.cpp +++ b/ntgcalls/signaling/signaling_packet_transport.cpp @@ -6,7 +6,12 @@ namespace signaling { void SignalingPacketTransport::receiveData(const bytes::binary& data) { - SignalReadPacket.emit(this, reinterpret_cast(data.data()), data.size(), -1, 0); + NotifyPacketReceived( + rtc::ReceivedPacket( + rtc::ArrayView(data.data(), data.size()), + rtc::SocketAddress() + ) + ); } const std::string& SignalingPacketTransport::transport_name() const { diff --git a/ntgcalls/signaling/signaling_sctp_connection.cpp b/ntgcalls/signaling/signaling_sctp_connection.cpp index 8ba8eb95..563c86ed 100644 --- a/ntgcalls/signaling/signaling_sctp_connection.cpp +++ b/ntgcalls/signaling/signaling_sctp_connection.cpp @@ -4,10 +4,13 @@ #include "signaling_sctp_connection.hpp" +#include + namespace signaling { SignalingSctpConnection::SignalingSctpConnection( rtc::Thread* networkThread, rtc::Thread* signalingThread, + const webrtc::Environment& env, const EncryptionKey &key, const DataEmitter& onEmitData, const DataReceiver& onSignalData, @@ -16,7 +19,7 @@ namespace signaling { networkThread->BlockingCall([&] { packetTransport = std::make_unique(onEmitData); sctpTransportFactory = std::make_unique(networkThread); - sctpTransport = sctpTransportFactory->CreateSctpTransport(packetTransport.get()); + sctpTransport = sctpTransportFactory->CreateSctpTransport(env, packetTransport.get()); sctpTransport->OpenStream(0); sctpTransport->SetDataChannelSink(this); sctpTransport->Start(5000, 5000, 262144); diff --git a/ntgcalls/signaling/signaling_sctp_connection.hpp b/ntgcalls/signaling/signaling_sctp_connection.hpp index 18406c0b..0e45309e 100644 --- a/ntgcalls/signaling/signaling_sctp_connection.hpp +++ b/ntgcalls/signaling/signaling_sctp_connection.hpp @@ -21,6 +21,7 @@ namespace signaling { SignalingSctpConnection( rtc::Thread* networkThread, rtc::Thread* signalingThread, + const webrtc::Environment& env, const EncryptionKey &key, const DataEmitter& onEmitData, const DataReceiver& onSignalData, @@ -42,6 +43,7 @@ namespace signaling { // Unused void OnChannelClosing(int channel_id) override{} void OnChannelClosed(int channel_id) override{} + void OnBufferedAmountLow(int channel_id) override{} protected: [[nodiscard]] bool supportsCompression() const override; diff --git a/wrtc/interfaces/network_interface.cpp b/wrtc/interfaces/network_interface.cpp index 4d791133..c082c9b7 100644 --- a/wrtc/interfaces/network_interface.cpp +++ b/wrtc/interfaces/network_interface.cpp @@ -33,6 +33,10 @@ namespace wrtc { return factory->workerThread(); } + const webrtc::Environment& NetworkInterface::environment() const { + return factory->environment(); + } + void NetworkInterface::onDataChannelOpened(const std::function& callback) { dataChannelOpenedCallback = callback; } diff --git a/wrtc/interfaces/network_interface.hpp b/wrtc/interfaces/network_interface.hpp index 3b909530..b5602c1d 100644 --- a/wrtc/interfaces/network_interface.hpp +++ b/wrtc/interfaces/network_interface.hpp @@ -32,6 +32,8 @@ namespace wrtc { [[nodiscard]] rtc::Thread *workerThread() const; + const webrtc::Environment& environment() const; + void onDataChannelOpened(const std::function &callback); void onIceCandidate(const std::function& callback); From 4d7be474467eed4653ec10dd226e145ab7314f2e Mon Sep 17 00:00:00 2001 From: Laky64 Date: Sun, 18 Aug 2024 16:55:56 +0200 Subject: [PATCH 04/48] Fixed build_tools wrong commit --- cmake/FindLibCXX.cmake | 4 ++-- cmake/FindWebRTC.cmake | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/FindLibCXX.cmake b/cmake/FindLibCXX.cmake index 60f82348..ca2c8a0d 100644 --- a/cmake/FindLibCXX.cmake +++ b/cmake/FindLibCXX.cmake @@ -8,11 +8,11 @@ if(LINUX) DIRECTORY ${LIBCXX_INCLUDE} ) GitFile( - URL https://chromium.googlesource.com/chromium/src/buildtools.git/+/refs/heads/main/third_party/libc++/__config_site + URL https://chromium.googlesource.com/chromium/src/buildtools.git/+/${BUILDTOOLS_COMMIT}/third_party/libc++/__config_site DIRECTORY ${LIBCXX_INCLUDE}/include/__config_site ) GitFile( - URL https://chromium.googlesource.com/chromium/src/buildtools.git/+/refs/heads/main/third_party/libc++/__assertion_handler + URL https://chromium.googlesource.com/chromium/src/buildtools.git/+/${BUILDTOOLS_COMMIT}/third_party/libc++/__assertion_handler DIRECTORY ${LIBCXX_INCLUDE}/include/__assertion_handler ) GitClone( diff --git a/cmake/FindWebRTC.cmake b/cmake/FindWebRTC.cmake index 383c9b52..6ada3ee0 100644 --- a/cmake/FindWebRTC.cmake +++ b/cmake/FindWebRTC.cmake @@ -75,4 +75,6 @@ if(NOT TARGET WebRTC::webrtc) set(LIBCXX_COMMIT ${CMAKE_MATCH_1}) string(REGEX MATCH "WEBRTC_SRC_THIRD_PARTY_LIBCXXABI_SRC_COMMIT=([^ \n]+)" matched "${WEBRTC_DATA}") set(LIBCXX_ABI_COMMIT ${CMAKE_MATCH_1}) + string(REGEX MATCH "WEBRTC_SRC_BUILDTOOLS_COMMIT=([^ \n]+)" matched "${WEBRTC_DATA}") + set(BUILDTOOLS_COMMIT ${CMAKE_MATCH_1}) endif () \ No newline at end of file From fb4a39b2f2a10a081bf33022a148b72b09057e19 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Sun, 18 Aug 2024 17:01:36 +0200 Subject: [PATCH 05/48] Fixed deprecated warning --- wrtc/interfaces/peer_connection/peer_connection_factory.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrtc/interfaces/peer_connection/peer_connection_factory.hpp b/wrtc/interfaces/peer_connection/peer_connection_factory.hpp index 16c0151c..d831b8b8 100644 --- a/wrtc/interfaces/peer_connection/peer_connection_factory.hpp +++ b/wrtc/interfaces/peer_connection/peer_connection_factory.hpp @@ -11,7 +11,7 @@ namespace wrtc { - class PeerConnectionFactory: public rtc::RefCountInterface { + class PeerConnectionFactory: public webrtc::RefCountInterface { public: PeerConnectionFactory(); From 6a1422fb36cddfdeed0cd3cc00c5460cfedd36f2 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Sun, 18 Aug 2024 17:02:36 +0200 Subject: [PATCH 06/48] Updated Dependencies --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45c6b22b..8212e4b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ endif () include(ExternalProject) # https://chromiumdash.appspot.com/branches -set(WEBRTC_REVISION m125.6422.2.3) -set(BOOST_REVISION 1.85.0) +set(WEBRTC_REVISION m127.6533.1.1) +set(BOOST_REVISION 1.86.0) set(BOOST_LIBS filesystem) option(STATIC_BUILD "Build static libraries" ON) From 578234e795bbe73120620f7f6aeebe36a7df5013 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Sun, 18 Aug 2024 17:03:04 +0200 Subject: [PATCH 07/48] Switch to CLang 19 and CMake 3.29.6 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index fba129be..a643d8ce 100644 --- a/setup.py +++ b/setup.py @@ -15,8 +15,8 @@ from setuptools.command.build_ext import build_ext base_path = os.path.abspath(os.path.dirname(__file__)) -CLANG_VERSION = '18' -CMAKE_VERSION = '3.28.1' +CLANG_VERSION = '19' +CMAKE_VERSION = '3.29.6' TOOLS_PATH = Path(Path.cwd(), 'build_tools') From ae4a1c0dd785f90ffe053e7d822b2127defa8f82 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Sun, 18 Aug 2024 17:03:54 +0200 Subject: [PATCH 08/48] Switch to rtc::MakeArrayView --- ntgcalls/signaling/crypto/signaling_encryption.cpp | 2 +- ntgcalls/signaling/messages/message.cpp | 2 +- ntgcalls/signaling/signaling_packet_transport.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ntgcalls/signaling/crypto/signaling_encryption.cpp b/ntgcalls/signaling/crypto/signaling_encryption.cpp index 7ab0e28a..9730e499 100644 --- a/ntgcalls/signaling/crypto/signaling_encryption.cpp +++ b/ntgcalls/signaling/crypto/signaling_encryption.cpp @@ -139,7 +139,7 @@ namespace signaling { auto currentSeq = packetSeq; auto currentCounter = CounterFromSeq(currentSeq); - rtc::ByteBufferReader reader(rtc::ArrayView(fullBuffer.data() + 4, fullBuffer.size() - 4)); + rtc::ByteBufferReader reader(rtc::MakeArrayView(fullBuffer.data() + 4, fullBuffer.size() - 4)); auto messages = std::vector(); while (true) { const auto type = static_cast(*reader.Data()); diff --git a/ntgcalls/signaling/messages/message.cpp b/ntgcalls/signaling/messages/message.cpp index 5e90da01..2e822d41 100644 --- a/ntgcalls/signaling/messages/message.cpp +++ b/ntgcalls/signaling/messages/message.cpp @@ -35,7 +35,7 @@ namespace signaling { } rtc::CopyOnWriteBuffer result; result.SetSize(length); - if (!reader.ReadBytes(rtc::ArrayView(result.MutableData(), result.size()))) { + if (!reader.ReadBytes(rtc::MakeArrayView(result.MutableData(), result.size()))) { return std::nullopt; } return result; diff --git a/ntgcalls/signaling/signaling_packet_transport.cpp b/ntgcalls/signaling/signaling_packet_transport.cpp index e17412ae..45ea1ccc 100644 --- a/ntgcalls/signaling/signaling_packet_transport.cpp +++ b/ntgcalls/signaling/signaling_packet_transport.cpp @@ -8,7 +8,7 @@ namespace signaling { void SignalingPacketTransport::receiveData(const bytes::binary& data) { NotifyPacketReceived( rtc::ReceivedPacket( - rtc::ArrayView(data.data(), data.size()), + rtc::MakeArrayView(data.data(), data.size()), rtc::SocketAddress() ) ); From 649703424904fda0a228963f64dcb2322ecfd37e Mon Sep 17 00:00:00 2001 From: Laky64 Date: Sun, 18 Aug 2024 17:05:24 +0200 Subject: [PATCH 09/48] Code cleanup --- wrtc/interfaces/media/rtc_audio_source.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/wrtc/interfaces/media/rtc_audio_source.hpp b/wrtc/interfaces/media/rtc_audio_source.hpp index b063fce4..704dc22b 100644 --- a/wrtc/interfaces/media/rtc_audio_source.hpp +++ b/wrtc/interfaces/media/rtc_audio_source.hpp @@ -4,8 +4,6 @@ #pragma once -#include - #include "tracks/audio_track_source.hpp" #include "../peer_connection/peer_connection_factory.hpp" #include "../../models/rtc_on_data_event.hpp" From 8a591bb8082d6bac3047159dae21972ada1fc072 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Mon, 19 Aug 2024 13:30:54 +0200 Subject: [PATCH 10/48] Fixed wrong cryptation --- ntgcalls/signaling/crypto/signaling_encryption.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ntgcalls/signaling/crypto/signaling_encryption.cpp b/ntgcalls/signaling/crypto/signaling_encryption.cpp index 9730e499..979cbb7a 100644 --- a/ntgcalls/signaling/crypto/signaling_encryption.cpp +++ b/ntgcalls/signaling/crypto/signaling_encryption.cpp @@ -34,7 +34,7 @@ namespace signaling { auto aesKeyIv = openssl::Aes::PrepareKeyIv(key, msgKey, x); openssl::Aes::ProcessCtr( bytes::memory_span(buffer.data(), buffer.size()), - msgKey + 16, + encrypted.data() + 16, aesKeyIv ); return encrypted; @@ -263,7 +263,7 @@ namespace signaling { for (auto &[data, lastSent] : myNotYetAckedMessages) { const auto sent = lastSent; const auto when = sent ? sent + minDelayBeforeMessageResend : 0; - assert(resending.data.size() >= 5); + assert(data.size() >= 5); const auto counter = CounterFromSeq(ReadSeq(data.data())); const auto type = static_cast(data.data()[4]); if (when > now) { From a9ecc80d9ab53a65c98a99bfa6854ea0f5c7ed30 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Mon, 19 Aug 2024 13:31:07 +0200 Subject: [PATCH 11/48] Fixed possible SIGSEGV --- ntgcalls/instances/call_interface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ntgcalls/instances/call_interface.cpp b/ntgcalls/instances/call_interface.cpp index 2ba47bc3..40902469 100644 --- a/ntgcalls/instances/call_interface.cpp +++ b/ntgcalls/instances/call_interface.cpp @@ -18,10 +18,9 @@ namespace ntgcalls { stream = nullptr; if (connection) { connection->onConnectionChange(nullptr); - connection->close(); + connection = nullptr; RTC_LOG(LS_VERBOSE) << "Connection closed"; } - connection = nullptr; updateThread = nullptr; cancelNetworkListener(); RTC_LOG(LS_VERBOSE) << "CallInterface destroyed"; From b82f35d3f360fa1dd97c49cf394b5e7d8cea7fd6 Mon Sep 17 00:00:00 2001 From: Eugeny Dementev Date: Wed, 28 Aug 2024 17:01:37 +0800 Subject: [PATCH 12/48] doc: add compile guide for ./examples/go (#25) --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index ced335e7..151c88c9 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,20 @@ NativeTgCalls offers Py Bindings, enabling seamless integration with Python. Fol ```shell python3 setup.py install ``` +### Go Bindings +NativeTgCalls offers Go Bindings, enabling seamless integration with Go. Follow these steps to compile NativeTgCalls with Go Bindings: +1. There is an example project for Go in `./examples/go/` directory, ensure you are in that directory +2. Prerequisites for building are the same as for building library itself and can be found [here](https://pytgcalls.github.io/NTgCalls/Build%20Guide#Installing=Prerequisites) +3. Download **shared** release of the library from https://github.com/pytgcalls/ntgcalls/releases + 1. Static won't work +4. Copy `ntgcalls.h` file into `./examples/go/ntgcalls/` directory +5. The rest of the files should be copied to `./examples/go/` directory + 1. `ntgcalls.dll` and `ntgcalls.lib` files in case of Windows amd64 shared release +6. Then in `./examples/go/` directory run `go build` or `go run .` with CGO_ENABLED=1 env variable set + 1. `$env:CGO_ENABLED=1; go run .` for Windows PowerShell + 2. `CGO_ENABLED=1 go run .` for UNIX + + ### C Bindings For developers looking to use NativeTgCalls with C and C++, we provide C Bindings. Follow these steps to compile NativeTgCalls with C Bindings: 1. Ensure you are in the root directory of the NativeTgCalls project. From f6b422d0de31a59510caee973c9c73741f017156 Mon Sep 17 00:00:00 2001 From: Santiago Ramirez Date: Wed, 28 Aug 2024 05:03:21 -0400 Subject: [PATCH 13/48] Update shell_reader.py (#26) --- examples/python/shell_reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python/shell_reader.py b/examples/python/shell_reader.py index f5db5e0b..4ba5abf9 100644 --- a/examples/python/shell_reader.py +++ b/examples/python/shell_reader.py @@ -17,7 +17,7 @@ async def main(): link = 'https://docs.evostream.com/sample_content/assets/sintel1m720p.mp4' async with client: - call_params = wrtc.create_call( + call_params = await wrtc.create_call( chat_id, MediaDescription( audio=AudioDescription( From 29fcf43121865a1031332881988c81047912cb7d Mon Sep 17 00:00:00 2001 From: Santiago Ramirez Date: Wed, 28 Aug 2024 09:01:59 -0400 Subject: [PATCH 14/48] refactor: update in the master branch of the python examples (#27) * Update shell_reader.py * Update youtube_reader.py --- examples/python/shell_reader.py | 2 +- examples/python/youtube_reader.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/python/shell_reader.py b/examples/python/shell_reader.py index f5db5e0b..4ba5abf9 100644 --- a/examples/python/shell_reader.py +++ b/examples/python/shell_reader.py @@ -17,7 +17,7 @@ async def main(): link = 'https://docs.evostream.com/sample_content/assets/sintel1m720p.mp4' async with client: - call_params = wrtc.create_call( + call_params = await wrtc.create_call( chat_id, MediaDescription( audio=AudioDescription( diff --git a/examples/python/youtube_reader.py b/examples/python/youtube_reader.py index e1f955a7..c82738d3 100644 --- a/examples/python/youtube_reader.py +++ b/examples/python/youtube_reader.py @@ -16,7 +16,7 @@ async def main(): audio, video = await get_youtube_stream("https://www.youtube.com/watch?v=u__gKd2mCVA") async with client: - call_params = wrtc.create_call( + call_params = await wrtc.create_call( chat_id, MediaDescription( audio=AudioDescription( From 7543808b352a63f13c0588327080ad2fbaf2c150 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Wed, 4 Sep 2024 17:07:47 +0200 Subject: [PATCH 15/48] WebRTC Version Bump --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8212e4b0..917c8df8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ endif () include(ExternalProject) # https://chromiumdash.appspot.com/branches -set(WEBRTC_REVISION m127.6533.1.1) +set(WEBRTC_REVISION m128.6613.2.0) set(BOOST_REVISION 1.86.0) set(BOOST_LIBS filesystem) From 82690a1375f0690934e6479b8a0d5f75bb8251c0 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Wed, 4 Sep 2024 17:10:04 +0200 Subject: [PATCH 16/48] Switch to "crypto_random.h" --- wrtc/interfaces/media/rtc_audio_source.cpp | 2 +- wrtc/interfaces/media/rtc_video_source.cpp | 1 + wrtc/sdp_builder.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/wrtc/interfaces/media/rtc_audio_source.cpp b/wrtc/interfaces/media/rtc_audio_source.cpp index 1f4897b1..696fac94 100644 --- a/wrtc/interfaces/media/rtc_audio_source.cpp +++ b/wrtc/interfaces/media/rtc_audio_source.cpp @@ -3,7 +3,7 @@ // #include "rtc_audio_source.hpp" - +#include namespace wrtc { RTCAudioSource::RTCAudioSource() { diff --git a/wrtc/interfaces/media/rtc_video_source.cpp b/wrtc/interfaces/media/rtc_video_source.cpp index 35dae31d..abaf66e3 100644 --- a/wrtc/interfaces/media/rtc_video_source.cpp +++ b/wrtc/interfaces/media/rtc_video_source.cpp @@ -3,6 +3,7 @@ // #include "rtc_video_source.hpp" +#include namespace wrtc { RTCVideoSource::RTCVideoSource() { diff --git a/wrtc/sdp_builder.cpp b/wrtc/sdp_builder.cpp index db486514..0d773b4b 100644 --- a/wrtc/sdp_builder.cpp +++ b/wrtc/sdp_builder.cpp @@ -5,7 +5,7 @@ #include "sdp_builder.hpp" #include -#include +#include namespace wrtc { std::string SdpBuilder::join() const From d12bf16c24f3bb5cbf62e1ece773cfa531b2d2bc Mon Sep 17 00:00:00 2001 From: Laky64 Date: Wed, 4 Sep 2024 17:10:25 +0200 Subject: [PATCH 17/48] Typo fix --- ntgcalls/io/file_reader.cpp | 2 +- ntgcalls/io/file_reader.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ntgcalls/io/file_reader.cpp b/ntgcalls/io/file_reader.cpp index a7cb9283..d3a6dbee 100644 --- a/ntgcalls/io/file_reader.cpp +++ b/ntgcalls/io/file_reader.cpp @@ -5,7 +5,7 @@ #include "file_reader.hpp" namespace ntgcalls { - FileReader::FileReader(const std::string& path, const int64_t bufferSize, const bool noLatecy): BaseReader(bufferSize, noLatecy) { + FileReader::FileReader(const std::string& path, const int64_t bufferSize, const bool noLatency): BaseReader(bufferSize, noLatency) { source = std::ifstream(path, std::ios::binary); if (!source) { RTC_LOG(LS_ERROR) << "Unable to open the file located at \"" << path << "\""; diff --git a/ntgcalls/io/file_reader.hpp b/ntgcalls/io/file_reader.hpp index d640d6db..4d932546 100644 --- a/ntgcalls/io/file_reader.hpp +++ b/ntgcalls/io/file_reader.hpp @@ -17,7 +17,7 @@ namespace ntgcalls { bytes::unique_binary readInternal(int64_t size) override; public: - explicit FileReader(const std::string& path, int64_t bufferSize, bool noLatecy); + explicit FileReader(const std::string& path, int64_t bufferSize, bool noLatency); ~FileReader() override; From 52f8eca46de3c1ac3b626353f7b5071c5b936b1a Mon Sep 17 00:00:00 2001 From: Laky64 Date: Wed, 4 Sep 2024 17:11:59 +0200 Subject: [PATCH 18/48] Switch to Protocol V8 Addressed any segfault or memory leak, --- ntgcalls/instances/p2p_call.cpp | 162 ++++- ntgcalls/instances/p2p_call.hpp | 4 + .../external_signaling_connection.cpp | 29 + .../external_signaling_connection.hpp | 28 + .../signaling/messages/candidates_message.cpp | 30 + .../signaling/messages/candidates_message.hpp | 25 + .../messages/initial_setup_message.cpp | 42 ++ .../messages/initial_setup_message.hpp | 31 + ntgcalls/signaling/messages/message.cpp | 9 + ntgcalls/signaling/messages/message.hpp | 3 + .../messages/negotiate_channels_message.cpp | 235 +++++++ .../messages/negotiate_channels_message.hpp | 34 + ntgcalls/signaling/signaling.cpp | 10 + ntgcalls/signaling/signaling.hpp | 4 - wrtc/interfaces/instance_networking.cpp | 21 + wrtc/interfaces/instance_networking.hpp | 88 +++ wrtc/interfaces/media/channel_manager.cpp | 141 ++++ wrtc/interfaces/media/channel_manager.hpp | 48 ++ .../media/channels/outgoing_audio_channel.cpp | 103 +++ .../media/channels/outgoing_audio_channel.hpp | 35 + .../media/channels/outgoing_video_channel.cpp | 123 ++++ .../media/channels/outgoing_video_channel.hpp | 36 + wrtc/interfaces/media/local_video_adapter.cpp | 23 + wrtc/interfaces/media/local_video_adapter.hpp | 20 + wrtc/interfaces/native_connection.cpp | 441 ++++++++++++ wrtc/interfaces/native_connection.hpp | 108 +++ wrtc/interfaces/reflector_port.cpp | 631 +++++++++++++++++ wrtc/interfaces/reflector_port.hpp | 126 ++++ .../reflector_relay_port_factory.cpp | 66 ++ .../reflector_relay_port_factory.hpp | 25 + ...p_data_channel_provider_interface_impl.cpp | 130 ++++ ...p_data_channel_provider_interface_impl.hpp | 85 +++ wrtc/models/content_negotiation_context.cpp | 642 ++++++++++++++++++ wrtc/models/content_negotiation_context.hpp | 96 +++ wrtc/models/media_content.hpp | 117 ++++ 35 files changed, 3730 insertions(+), 21 deletions(-) create mode 100644 ntgcalls/signaling/external_signaling_connection.cpp create mode 100644 ntgcalls/signaling/external_signaling_connection.hpp create mode 100644 ntgcalls/signaling/messages/candidates_message.cpp create mode 100644 ntgcalls/signaling/messages/candidates_message.hpp create mode 100644 ntgcalls/signaling/messages/initial_setup_message.cpp create mode 100644 ntgcalls/signaling/messages/initial_setup_message.hpp create mode 100644 ntgcalls/signaling/messages/negotiate_channels_message.cpp create mode 100644 ntgcalls/signaling/messages/negotiate_channels_message.hpp create mode 100644 wrtc/interfaces/instance_networking.cpp create mode 100644 wrtc/interfaces/instance_networking.hpp create mode 100644 wrtc/interfaces/media/channel_manager.cpp create mode 100644 wrtc/interfaces/media/channel_manager.hpp create mode 100644 wrtc/interfaces/media/channels/outgoing_audio_channel.cpp create mode 100644 wrtc/interfaces/media/channels/outgoing_audio_channel.hpp create mode 100644 wrtc/interfaces/media/channels/outgoing_video_channel.cpp create mode 100644 wrtc/interfaces/media/channels/outgoing_video_channel.hpp create mode 100644 wrtc/interfaces/media/local_video_adapter.cpp create mode 100644 wrtc/interfaces/media/local_video_adapter.hpp create mode 100644 wrtc/interfaces/native_connection.cpp create mode 100644 wrtc/interfaces/native_connection.hpp create mode 100644 wrtc/interfaces/reflector_port.cpp create mode 100644 wrtc/interfaces/reflector_port.hpp create mode 100644 wrtc/interfaces/reflector_relay_port_factory.cpp create mode 100644 wrtc/interfaces/reflector_relay_port_factory.hpp create mode 100644 wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp create mode 100644 wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp create mode 100644 wrtc/models/content_negotiation_context.cpp create mode 100644 wrtc/models/content_negotiation_context.hpp create mode 100644 wrtc/models/media_content.hpp diff --git a/ntgcalls/instances/p2p_call.cpp b/ntgcalls/instances/p2p_call.cpp index c65b9fa6..418af22a 100644 --- a/ntgcalls/instances/p2p_call.cpp +++ b/ntgcalls/instances/p2p_call.cpp @@ -6,10 +6,14 @@ #include "ntgcalls/exceptions.hpp" #include "ntgcalls/signaling/crypto/mod_exp_first.hpp" +#include "ntgcalls/signaling/messages/candidates_message.hpp" #include "ntgcalls/signaling/messages/candidate_message.hpp" +#include "ntgcalls/signaling/messages/initial_setup_message.hpp" #include "ntgcalls/signaling/messages/media_state_message.hpp" #include "ntgcalls/signaling/messages/message.hpp" +#include "ntgcalls/signaling/messages/negotiate_channels_message.hpp" #include "ntgcalls/signaling/messages/rtc_description_message.hpp" +#include "wrtc/interfaces/native_connection.hpp" #include "wrtc/utils/encryption.hpp" namespace ntgcalls { @@ -99,17 +103,25 @@ namespace ntgcalls { auto encryptionKey = std::make_shared>(); memcpy(encryptionKey->data(), key.value().data(), signaling::EncryptionKey::kSize); protocolVersion = signaling::Signaling::matchVersion(versions); - connection = std::make_unique( + if (protocolVersion & signaling::Signaling::Version::V2Full) { + connection = std::make_unique( RTCServer::toIceServers(servers), true, p2pAllowed ); - Safe(connection)->onRenegotiationNeeded([this] { - if (makingNegotation) { - RTC_LOG(LS_INFO) << "Renegotiation needed"; - sendLocalDescription(); - } - }); + Safe(connection)->onRenegotiationNeeded([this] { + if (makingNegotation) { + RTC_LOG(LS_INFO) << "Renegotiation needed"; + sendLocalDescription(); + } + }); + } else { + connection = std::make_unique( + RTCServer::toRtcServers(servers), + p2pAllowed, + type() == Type::Outgoing + ); + } signaling = signaling::Signaling::Create( protocolVersion, connection->networkThread(), @@ -126,12 +138,20 @@ namespace ntgcalls { } ); connection->onIceCandidate([this](const wrtc::IceCandidate& candidate) { - signaling::CandidateMessage candMess; - candMess.sdp = candidate.sdp; - candMess.mid = candidate.mid; - candMess.mLine = candidate.mLine; - RTC_LOG(LS_INFO) << "Sending candidate: " << bytes::to_string(candMess.serialize()); - signaling->send(candMess.serialize()); + bytes::binary message; + if (protocolVersion & signaling::Signaling::Version::V2Full) { + signaling::CandidateMessage candMess; + candMess.sdp = candidate.sdp; + candMess.mid = candidate.mid; + candMess.mLine = candidate.mLine; + message = candMess.serialize(); + } else { + signaling::CandidatesMessage candMess; + candMess.iceCandidates.push_back({candidate.sdp}); + message = candMess.serialize(); + } + RTC_LOG(LS_INFO) << "Sending candidate: " << bytes::to_string(message); + signaling->send(message); }); connection->onDataChannelOpened([this] { sendMediaState(stream->getState()); @@ -142,10 +162,15 @@ namespace ntgcalls { sendMediaState(mediaState); }); if (type() == Type::Outgoing) { - RTC_LOG(LS_INFO) << "Creating data channel"; - Safe(connection)->createDataChannel("data"); - makingNegotation = true; - sendLocalDescription(); + if (protocolVersion & signaling::Signaling::Version::V2Full) { + RTC_LOG(LS_INFO) << "Creating data channel"; + Safe(connection)->createDataChannel("data"); + makingNegotation = true; + sendLocalDescription(); + } else { + sendInitialSetup(); + sendMediaState(stream->getState()); + } } setConnectionObserver(); } @@ -154,6 +179,67 @@ namespace ntgcalls { RTC_LOG(LS_INFO) << "processSignalingData: " << std::string(buffer.begin(), buffer.end()); try { switch (signaling::Message::type(buffer)) { + case signaling::Message::Type::InitialSetup: { + const auto message = signaling::InitialSetupMessage::deserialize(buffer); + wrtc::PeerIceParameters remoteIceParameters; + remoteIceParameters.ufrag = message->ufrag; + remoteIceParameters.pwd = message->pwd; + remoteIceParameters.supportsRenomination = message->supportsRenomination; + + std::unique_ptr fingerprint; + std::string sslSetup; + if (!message->fingerprints.empty()) { + fingerprint = rtc::SSLFingerprint::CreateUniqueFromRfc4572(message->fingerprints[0].hash, message->fingerprints[0].fingerprint); + sslSetup = message->fingerprints[0].setup; + } + Safe(connection)->setRemoteParams(remoteIceParameters, std::move(fingerprint), sslSetup); + handshakeCompleted = true; + if (type() == Type::Outgoing) { + sendOfferIfNeeded(); + } else { + sendInitialSetup(); + } + applyPendingIceCandidates(); + break; + } + case signaling::Message::Type::Candidates: { + for (const auto message = signaling::CandidatesMessage::deserialize(buffer); const auto&[sdpString] : message->iceCandidates) { + webrtc::JsepIceCandidate parseCandidate{ std::string(), 0 }; + if (!parseCandidate.Initialize(sdpString, nullptr)) { + RTC_LOG(LS_ERROR) << "Could not parse candidate: " << sdpString; + continue; + } + std::string sdp; + parseCandidate.ToString(&sdp); + pendingIceCandidates.emplace_back( + parseCandidate.sdp_mid(), + parseCandidate.sdp_mline_index(), + sdp + ); + } + if (handshakeCompleted) { + applyPendingIceCandidates(); + } + break; + } + case signaling::Message::Type::NegotiateChannels: { + const auto message = signaling::NegotiateChannelsMessage::deserialize(buffer); + auto negotiationContents = std::make_unique(); + negotiationContents->exchangeId = message->exchangeId; + negotiationContents->contents = message->contents; + auto negotiation = message->serialize(); + if (const auto response = Safe(connection)->setPendingAnswer(std::move(negotiationContents))) { + signaling::NegotiateChannelsMessage channelMessage; + channelMessage.exchangeId = response->exchangeId; + channelMessage.contents = response->contents; + RTC_LOG(LS_INFO) << "Sending negotiate channels: " << bytes::to_string(channelMessage.serialize()); + signaling->send(channelMessage.serialize()); + } + sendOfferIfNeeded(); + Safe(connection)->createChannels(); + sendMediaState(stream->getState()); + break; + } case signaling::Message::Type::RtcDescription: { const auto message = signaling::RtcDescriptionMessage::deserialize(buffer); if ( @@ -261,6 +347,48 @@ namespace ntgcalls { connection->sendDataChannelMessage(message.serialize()); } + void P2PCall::sendOfferIfNeeded() const { + if (const auto offer = Safe(connection)->getPendingOffer()) { + signaling::NegotiateChannelsMessage data; + data.exchangeId = offer->exchangeId; + data.contents = offer->contents; + RTC_LOG(LS_INFO) << "Sending offer: " << bytes::to_string(data.serialize()); + signaling->send(data.serialize()); + } + } + + void P2PCall::sendInitialSetup() const { + connection->networkThread()->PostTask([this] { + const auto localFingerprint = Safe(connection)->localFingerprint(); + std::string hash; + std::string fingerprint; + if (localFingerprint) { + hash = localFingerprint->algorithm; + fingerprint = localFingerprint->GetRfc4572Fingerprint(); + } + std::string setup; + if (type() == Type::Outgoing) { + setup = "actpass"; + } else { + setup = "passive"; + } + const auto localIceParams = Safe(connection)->localIceParameters(); + connection->signalingThread()->PostTask([this, localIceParams, hash, fingerprint, setup] { + signaling::InitialSetupMessage message; + message.ufrag = localIceParams.ufrag; + message.pwd = localIceParams.pwd; + message.supportsRenomination = localIceParams.supportsRenomination; + signaling::InitialSetupMessage::DtlsFingerprint dtlsFingerprint; + dtlsFingerprint.hash = hash; + dtlsFingerprint.fingerprint = fingerprint; + dtlsFingerprint.setup = setup; + message.fingerprints.push_back(std::move(dtlsFingerprint)); + RTC_LOG(LS_INFO) << "Sending initial setup: " << bytes::to_string(message.serialize()); + signaling->send(message.serialize()); + }); + }); + } + void P2PCall::onSignalingData(const std::function& callback) { onEmitData = callback; } diff --git a/ntgcalls/instances/p2p_call.hpp b/ntgcalls/instances/p2p_call.hpp index b2f19171..ff987c2d 100644 --- a/ntgcalls/instances/p2p_call.hpp +++ b/ntgcalls/instances/p2p_call.hpp @@ -35,6 +35,10 @@ namespace ntgcalls { void sendMediaState(MediaState mediaState) const; + void sendOfferIfNeeded() const; + + void sendInitialSetup() const; + public: explicit P2PCall(rtc::Thread* updateThread): CallInterface(updateThread) {} diff --git a/ntgcalls/signaling/external_signaling_connection.cpp b/ntgcalls/signaling/external_signaling_connection.cpp new file mode 100644 index 00000000..93d3ee80 --- /dev/null +++ b/ntgcalls/signaling/external_signaling_connection.cpp @@ -0,0 +1,29 @@ +// +// Created by Laky64 on 18/08/2024. +// + +#include "external_signaling_connection.hpp" + +namespace signaling { + ExternalSignalingConnection::ExternalSignalingConnection( + rtc::Thread* networkThread, + rtc::Thread* signalingThread, + const EncryptionKey &key, + const DataEmitter& onEmitData, + const DataReceiver& onSignalData + ): SignalingInterface(networkThread, signalingThread, key, onEmitData, onSignalData) {} + + void ExternalSignalingConnection::send(const bytes::binary& data) { + onEmitData(preSendData(data, true)); + } + + void ExternalSignalingConnection::receive(const bytes::binary& data) const { + signalingThread->PostTask([this, data] { + onSignalData(preReadData(data, true)); + }); + } + + bool ExternalSignalingConnection::supportsCompression() const { + return false; + } +} // signaling \ No newline at end of file diff --git a/ntgcalls/signaling/external_signaling_connection.hpp b/ntgcalls/signaling/external_signaling_connection.hpp new file mode 100644 index 00000000..375e94a0 --- /dev/null +++ b/ntgcalls/signaling/external_signaling_connection.hpp @@ -0,0 +1,28 @@ +// +// Created by Laky64 on 18/08/2024. +// + +#pragma once +#include + +#include "signaling_interface.hpp" + +namespace signaling { + class ExternalSignalingConnection final : public sigslot::has_slots<>, public SignalingInterface { + public: + ExternalSignalingConnection( + rtc::Thread* networkThread, + rtc::Thread* signalingThread, + const EncryptionKey &key, + const DataEmitter& onEmitData, + const DataReceiver& onSignalData + ); + + void send(const bytes::binary& data) override; + + void receive(const bytes::binary& data) const override; + + protected: + [[nodiscard]] bool supportsCompression() const override; + }; +} // signaling diff --git a/ntgcalls/signaling/messages/candidates_message.cpp b/ntgcalls/signaling/messages/candidates_message.cpp new file mode 100644 index 00000000..98b3a554 --- /dev/null +++ b/ntgcalls/signaling/messages/candidates_message.cpp @@ -0,0 +1,30 @@ +// +// Created by Laky64 on 30/03/2024. +// + +#include "candidates_message.hpp" + +namespace signaling { + bytes::binary CandidatesMessage::serialize() const { + json res = { + {"@type", "Candidates"}, + }; + auto iceCandidatesJson = json::array(); + for (const auto& [sdpString] : iceCandidates) { + iceCandidatesJson.push_back(json{ + {"sdpString", sdpString}, + }); + } + res["candidates"] = iceCandidatesJson; + return bytes::make_binary(to_string(res)); + } + + std::unique_ptr CandidatesMessage::deserialize(const bytes::binary &data) { + json j = json::parse(data.begin(), data.end()); + auto message = std::make_unique(); + for (const auto& iceCandidate : j["candidates"]) { + message->iceCandidates.emplace_back(iceCandidate["sdpString"]); + } + return std::move(message); + } +} // signaling \ No newline at end of file diff --git a/ntgcalls/signaling/messages/candidates_message.hpp b/ntgcalls/signaling/messages/candidates_message.hpp new file mode 100644 index 00000000..2c1d304c --- /dev/null +++ b/ntgcalls/signaling/messages/candidates_message.hpp @@ -0,0 +1,25 @@ +// +// Created by Laky64 on 30/03/2024. +// + +#pragma once +#include +#include + +#include "message.hpp" + +namespace signaling { + class CandidatesMessage final : public Message { + public: + struct IceCandidate { + std::string sdpString; + }; + + std::vector iceCandidates; + + [[nodiscard]] bytes::binary serialize() const override; + + static std::unique_ptr deserialize(const bytes::binary& data); + }; + +} // signaling \ No newline at end of file diff --git a/ntgcalls/signaling/messages/initial_setup_message.cpp b/ntgcalls/signaling/messages/initial_setup_message.cpp new file mode 100644 index 00000000..513046ba --- /dev/null +++ b/ntgcalls/signaling/messages/initial_setup_message.cpp @@ -0,0 +1,42 @@ +// +// Created by Laky64 on 28/03/2024. +// + +#include "initial_setup_message.hpp" + +namespace signaling { + bytes::binary InitialSetupMessage::serialize() const { + json res = { + {"@type", "InitialSetup"}, + {"ufrag", ufrag}, + {"pwd", pwd}, + {"renomination", supportsRenomination}, + }; + json fingerprintsJson = json::array(); + for (const auto& [hash, setup, fingerprint] : fingerprints) { + fingerprintsJson.push_back(json{ + {"hash", hash}, + {"setup", setup}, + {"fingerprint", fingerprint}, + }); + } + res["fingerprints"] = fingerprintsJson; + return bytes::make_binary(to_string(res)); + } + + std::unique_ptr InitialSetupMessage::deserialize(const bytes::binary& data) { + json j = json::parse(data.begin(), data.end()); + auto message = std::make_unique(); + message->ufrag = j["ufrag"]; + message->pwd = j["pwd"]; + message->supportsRenomination = j["renomination"]; + for (const auto& fingerprint : j["fingerprints"]) { + message->fingerprints.push_back({ + fingerprint["hash"], + fingerprint["setup"], + fingerprint["fingerprint"], + }); + } + return std::move(message); + } +} // signaling \ No newline at end of file diff --git a/ntgcalls/signaling/messages/initial_setup_message.hpp b/ntgcalls/signaling/messages/initial_setup_message.hpp new file mode 100644 index 00000000..9498bc1e --- /dev/null +++ b/ntgcalls/signaling/messages/initial_setup_message.hpp @@ -0,0 +1,31 @@ +// +// Created by Laky64 on 28/03/2024. +// + +#pragma once +#include +#include + +#include "message.hpp" + +namespace signaling { + + class InitialSetupMessage final: public Message{ + public: + struct DtlsFingerprint { + std::string hash; + std::string setup; + std::string fingerprint; + }; + + std::string ufrag; + std::string pwd; + bool supportsRenomination = false; + std::vector fingerprints; + + [[nodiscard]] bytes::binary serialize() const override; + + static std::unique_ptr deserialize(const bytes::binary& data); + }; + +} // signaling \ No newline at end of file diff --git a/ntgcalls/signaling/messages/message.cpp b/ntgcalls/signaling/messages/message.cpp index 2e822d41..70943788 100644 --- a/ntgcalls/signaling/messages/message.cpp +++ b/ntgcalls/signaling/messages/message.cpp @@ -18,6 +18,15 @@ namespace signaling { if (type == "offer" || type == "answer") { return Type::RtcDescription; } + if (type == "InitialSetup") { + return Type::InitialSetup; + } + if (type == "Candidates") { + return Type::Candidates; + } + if (type == "NegotiateChannels") { + return Type::NegotiateChannels; + } } return Type::Unknown; } diff --git a/ntgcalls/signaling/messages/message.hpp b/ntgcalls/signaling/messages/message.hpp index f30ac512..0b76f845 100644 --- a/ntgcalls/signaling/messages/message.hpp +++ b/ntgcalls/signaling/messages/message.hpp @@ -17,6 +17,9 @@ namespace signaling { enum class Type { Candidate, RtcDescription, + InitialSetup, + Candidates, + NegotiateChannels, Unknown }; diff --git a/ntgcalls/signaling/messages/negotiate_channels_message.cpp b/ntgcalls/signaling/messages/negotiate_channels_message.cpp new file mode 100644 index 00000000..ee5bc9f8 --- /dev/null +++ b/ntgcalls/signaling/messages/negotiate_channels_message.cpp @@ -0,0 +1,235 @@ +// +// Created by Laky64 on 30/03/2024. +// + +#include "negotiate_channels_message.hpp" + +#include "ntgcalls/exceptions.hpp" + +namespace signaling { + json NegotiateChannelsMessage::serializeSourceGroup(const wrtc::SsrcGroup &ssrcGroup) { + auto ssrcsJson = json::array(); + for (const auto ssrc : ssrcGroup.ssrcs) { + ssrcsJson.push_back(std::to_string(ssrc)); + } + return { + {"semantics", ssrcGroup.semantics}, + {"ssrcs", ssrcsJson}, + }; + } + + json NegotiateChannelsMessage::serializePayloadType(const wrtc::PayloadType &payloadType) { + auto res = json{ + {"id", payloadType.id}, + {"name", payloadType.name}, + {"clockrate", payloadType.clockrate}, + {"channels", payloadType.channels}, + }; + auto feedbackTypesJson = json::array(); + if (!payloadType.feedbackTypes.empty()) { + for (const auto&[type, subtype] : payloadType.feedbackTypes) { + feedbackTypesJson.push_back({ + {"type", type}, + {"subtype", subtype}, + }); + } + } + res["feedbackTypes"] = feedbackTypesJson; + auto parametersJson = json::object(); + for (const auto& [key, value] : payloadType.parameters) { + parametersJson[key] = value; + } + res["parameters"] = parametersJson; + return res; + } + + json NegotiateChannelsMessage::serializeContent(const wrtc::MediaContent &content) { + json contentJson = { + {"type", content.type == wrtc::MediaContent::Type::Audio ? "audio" : "video"}, + {"ssrc", std::to_string(content.ssrc)}, + }; + if (!content.ssrcGroups.empty()) { + auto ssrcGroupsJson = json::array(); + for (const auto& ssrcGroup : content.ssrcGroups) { + ssrcGroupsJson.push_back(serializeSourceGroup(ssrcGroup)); + } + contentJson["ssrcGroups"] = ssrcGroupsJson; + } + if (!content.payloadTypes.empty()) { + auto payloadTypesJson = json::array(); + for (const auto& payloadType : content.payloadTypes) { + payloadTypesJson.push_back(serializePayloadType(payloadType)); + } + contentJson["payloadTypes"] = payloadTypesJson; + } + auto rtpExtensionsJson = json::array(); + for (const auto& rtpExtension : content.rtpExtensions) { + rtpExtensionsJson.push_back(json{ + {"uri", rtpExtension.uri}, + {"id", rtpExtension.id}, + }); + } + contentJson["rtpExtensions"] = rtpExtensionsJson; + return contentJson; + } + + bytes::binary NegotiateChannelsMessage::serialize() const { + auto res = json{ + {"@type", "NegotiateChannels"}, + {"exchangeId", std::to_string(exchangeId)}, + }; + auto contentsJson = json::array(); + for (const auto& content : contents) { + contentsJson.push_back(serializeContent(content)); + } + res["contents"] = contentsJson; + return bytes::make_binary(to_string(res)); + } + + wrtc::SsrcGroup NegotiateChannelsMessage::deserializeSourceGroup(const json &ssrcGroup) { + wrtc::SsrcGroup result; + if (!ssrcGroup.contains("semantics") || !ssrcGroup.contains("ssrcs")) { + throw ntgcalls::InvalidParams("Signaling: ssrcGroup must contain semantics and ssrcs"); + } + result.semantics = ssrcGroup["semantics"]; + for (const auto &ssrc : ssrcGroup["ssrcs"].items()) { + if (ssrc.value().is_string()) { + uint32_t parsedSsrc = stringToUInt32(ssrc.value()); + if (parsedSsrc == 0) { + throw ntgcalls::InvalidParams("Signaling: parsedSsrc must not be 0"); + } + result.ssrcs.push_back(parsedSsrc); + } else if (ssrc.value().is_number()) { + result.ssrcs.push_back(ssrc.value()); + } else { + throw ntgcalls::InvalidParams("Signaling: ssrcs item must be a string or a number"); + } + } + return result; + } + + wrtc::FeedbackType NegotiateChannelsMessage::deserializeFeedbackType(const json &feedbackType) { + wrtc::FeedbackType result; + if (!feedbackType.contains("type") || !feedbackType.contains("subtype")) { + throw ntgcalls::InvalidParams("Signaling: feedbackType must contain type and subtype"); + } + result.type = feedbackType["type"]; + result.subtype = feedbackType["subtype"]; + return result; + } + + wrtc::PayloadType NegotiateChannelsMessage::deserializePayloadType(const json &payloadType) { + wrtc::PayloadType result; + if (!payloadType.contains("id") || !payloadType.contains("name") || !payloadType.contains("clockrate")) { + throw ntgcalls::InvalidParams("Signaling: payloadType must contain id, name and clockrate"); + } + result.id = payloadType["id"]; + result.name = payloadType["name"]; + result.clockrate = payloadType["clockrate"]; + if (payloadType.contains("channels")) { + if (!payloadType["channels"].is_number()) { + throw ntgcalls::InvalidParams("Signaling: channels must be a number"); + } + result.channels = payloadType["channels"]; + } + if (payloadType.contains("feedbackTypes")) { + for (const auto &feedbackType : payloadType["feedbackTypes"].items()) { + if (!feedbackType.value().is_object()) { + throw ntgcalls::InvalidParams("Signaling: feedbackTypes items must be objects"); + } + result.feedbackTypes.push_back(deserializeFeedbackType(feedbackType.value())); + } + } + if (payloadType.contains("parameters")) { + for (const auto ¶meter : payloadType["parameters"].items()) { + if (!parameter.value().is_string()) { + throw ntgcalls::InvalidParams("Signaling: parameters items must be strings"); + } + result.parameters.emplace_back(parameter.key(), parameter.value()); + } + } + return result; + } + + webrtc::RtpExtension NegotiateChannelsMessage::deserializeRtpExtension(const json &rtpExtension) { + webrtc::RtpExtension result; + if (!rtpExtension.contains("id") || !rtpExtension.contains("uri")) { + throw ntgcalls::InvalidParams("Signaling: rtpExtension must contain id and uri"); + } + result.id = rtpExtension["id"]; + result.uri = rtpExtension["uri"]; + return result; + } + + wrtc::MediaContent NegotiateChannelsMessage::deserializeContent(const json &content) { + wrtc::MediaContent result; + if (!content.contains("type") || !content.contains("ssrc")) { + throw ntgcalls::InvalidParams("Signaling: content must contain type and ssrc"); + } + if (const auto& type = content["type"]; type == "audio") { + result.type = wrtc::MediaContent::Type::Audio; + } else if (type == "video") { + result.type = wrtc::MediaContent::Type::Video; + } else { + throw ntgcalls::InvalidParams("Signaling: type must be 'audio' or 'video'"); + } + + if (const auto& ssrc = content["ssrc"]; ssrc.is_string()) { + result.ssrc = stringToUInt32(ssrc); + } else if (ssrc.is_number()) { + result.ssrc = static_cast(ssrc); + } else { + throw ntgcalls::InvalidParams("Signaling: ssrc must be a string or a number"); + } + if (content.contains("ssrcGroups")) { + for (const auto &ssrcGroup : content["ssrcGroups"].items()) { + if (!ssrcGroup.value().is_object()) { + throw ntgcalls::InvalidParams("Signaling: ssrcsGroups items must be objects"); + } + result.ssrcGroups.push_back(deserializeSourceGroup(ssrcGroup.value())); + } + } + if (content.contains("payloadTypes")) { + for (const auto &payloadType : content["payloadTypes"].items()) { + if (!payloadType.value().is_object()) { + throw ntgcalls::InvalidParams("Signaling: payloadTypes items must be objects"); + } + result.payloadTypes.push_back(deserializePayloadType(payloadType.value())); + } + } + if (content.contains("rtpExtensions")) { + for (const auto &rtpExtension : content["rtpExtensions"].items()) { + if (!rtpExtension.value().is_object()) { + throw ntgcalls::InvalidParams("Signaling: rtpExtensions items must be objects"); + } + result.rtpExtensions.push_back(deserializeRtpExtension(rtpExtension.value())); + } + } + return result; + } + + std::unique_ptr NegotiateChannelsMessage::deserialize(const bytes::binary &data) { + json j = json::parse(data.begin(), data.end()); + auto message = std::make_unique(); + if (!j.contains("exchangeId")) { + throw ntgcalls::InvalidParams("Signaling: exchangeId must be present"); + } + if (const auto exchangeId = j["exchangeId"]; exchangeId.is_string()) { + message->exchangeId = stringToUInt32(exchangeId); + } else if (exchangeId.is_number()) { + message->exchangeId = static_cast(exchangeId); + } else { + throw ntgcalls::InvalidParams("Signaling: exchangeId must be a string or a number"); + } + if (!j.contains("contents")) { + throw ntgcalls::InvalidParams("Signaling: contents must be present"); + } + for (const auto &content : j["contents"].items()) { + if (!content.value().is_object()) { + throw ntgcalls::InvalidParams("Signaling: contents items must be objects"); + } + message->contents.push_back(deserializeContent(content.value())); + } + return std::move(message); + } +} // signaling \ No newline at end of file diff --git a/ntgcalls/signaling/messages/negotiate_channels_message.hpp b/ntgcalls/signaling/messages/negotiate_channels_message.hpp new file mode 100644 index 00000000..f2854fc1 --- /dev/null +++ b/ntgcalls/signaling/messages/negotiate_channels_message.hpp @@ -0,0 +1,34 @@ +// +// Created by Laky64 on 30/03/2024. +// + +#pragma once +#include "message.hpp" +#include "wrtc/models/media_content.hpp" + +namespace signaling { + class NegotiateChannelsMessage final: public Message { + [[nodiscard]] static json serializeContent(const wrtc::MediaContent &content); + + [[nodiscard]] static json serializeSourceGroup(const wrtc::SsrcGroup& ssrcGroup); + + [[nodiscard]] static json serializePayloadType(const wrtc::PayloadType& payloadType); + + static wrtc::MediaContent deserializeContent(const json& content); + + static webrtc::RtpExtension deserializeRtpExtension(const json& rtpExtension); + + static wrtc::FeedbackType deserializeFeedbackType(const json& feedbackType); + + static wrtc::SsrcGroup deserializeSourceGroup(const json& ssrcGroup); + + static wrtc::PayloadType deserializePayloadType(const json& payloadType); + public: + uint32_t exchangeId = 0; + std::vector contents; + + [[nodiscard]] bytes::binary serialize() const override; + + static std::unique_ptr deserialize(const bytes::binary& data); + }; +} // signaling \ No newline at end of file diff --git a/ntgcalls/signaling/signaling.cpp b/ntgcalls/signaling/signaling.cpp index 91fe84f0..72ac4146 100644 --- a/ntgcalls/signaling/signaling.cpp +++ b/ntgcalls/signaling/signaling.cpp @@ -4,6 +4,7 @@ #include "signaling.hpp" +#include "external_signaling_connection.hpp" #include "signaling_sctp_connection.hpp" @@ -21,6 +22,10 @@ namespace signaling { RTC_LOG(LS_ERROR) << "Signaling V1 is not supported"; throw ntgcalls::SignalingUnsupported("Signaling V1 is not supported"); } + if (version & Version::V2) { + RTC_LOG(LS_WARNING) << "Using signaling V2 Legacy"; + return std::make_unique(networkThread, signalingThread, key, onEmitData, onSignalData); + } if (version & Version::V2Full) { RTC_LOG(LS_INFO) << "Using signaling V2 Full"; return std::make_unique(networkThread, signalingThread, env, key, onEmitData, onSignalData, true); @@ -30,6 +35,8 @@ namespace signaling { std::vector Signaling::SupportedVersions() { return { + "8.0.0", + "9.0.0", "11.0.0", }; } @@ -37,6 +44,9 @@ namespace signaling { Signaling::Version Signaling::matchVersion(const std::vector &versions) { const auto version = bestMatch(versions); RTC_LOG(LS_INFO) << "Selected version: " << version; + if (version == "8.0.0" || version == "9.0.0") { + return Version::V2; + } if (version == "10.0.0") { return Version::V1; } diff --git a/ntgcalls/signaling/signaling.hpp b/ntgcalls/signaling/signaling.hpp index 90af1a4e..5f89efb4 100644 --- a/ntgcalls/signaling/signaling.hpp +++ b/ntgcalls/signaling/signaling.hpp @@ -33,11 +33,7 @@ namespace signaling { static Version matchVersion(const std::vector &versions); private: -#ifdef LEGACY_SUPPORT static constexpr char defaultVersion[] = "8.0.0"; -#else - static constexpr char defaultVersion[] = "11.0.0"; -#endif static std::string bestMatch(std::vector versions); diff --git a/wrtc/interfaces/instance_networking.cpp b/wrtc/interfaces/instance_networking.cpp new file mode 100644 index 00000000..dca6f7f2 --- /dev/null +++ b/wrtc/interfaces/instance_networking.cpp @@ -0,0 +1,21 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#include "instance_networking.hpp" + +namespace wrtc { + InstanceNetworking::RouteDescription::RouteDescription(std::string localDescription_, + std::string remoteDescription_): + localDescription(std::move(localDescription_)), + remoteDescription(std::move(remoteDescription_)) + {} + + InstanceNetworking::ConnectionDescription::CandidateDescription InstanceNetworking::connectionDescriptionFromCandidate(const cricket::Candidate &candidate) { + ConnectionDescription::CandidateDescription result; + result.type = candidate.type_name(); + result.protocol = candidate.protocol(); + result.address = candidate.address().ToString(); + return result; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/instance_networking.hpp b/wrtc/interfaces/instance_networking.hpp new file mode 100644 index 00000000..95b500d5 --- /dev/null +++ b/wrtc/interfaces/instance_networking.hpp @@ -0,0 +1,88 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#pragma once +#include +#include + +namespace wrtc { + + class InstanceNetworking { + public: + struct ConnectionDescription { + struct CandidateDescription { + std::string protocol; + std::string type; + std::string address; + + bool operator==(CandidateDescription const &rhs) const { + if (protocol != rhs.protocol) { + return false; + } + if (type != rhs.type) { + return false; + } + if (address != rhs.address) { + return false; + } + + return true; + } + + bool operator!=(const CandidateDescription& rhs) const { + return !(*this == rhs); + } + }; + + CandidateDescription local; + CandidateDescription remote; + + bool operator==(ConnectionDescription const &rhs) const { + if (local != rhs.local) { + return false; + } + if (remote != rhs.remote) { + return false; + } + + return true; + } + + bool operator!=(const ConnectionDescription& rhs) const { + return !(*this == rhs); + } + }; + + struct RouteDescription { + explicit RouteDescription(std::string localDescription_, std::string remoteDescription_); + + std::string localDescription; + std::string remoteDescription; + + bool operator==(RouteDescription const &rhs) const { + if (localDescription != rhs.localDescription) { + return false; + } + if (remoteDescription != rhs.remoteDescription) { + return false; + } + + return true; + } + + bool operator!=(const RouteDescription& rhs) const { + return !(*this == rhs); + } + }; + + struct State { + bool isReadyToSendData = false; + bool isFailed = false; + absl::optional route; + absl::optional connection; + }; + + static ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(const cricket::Candidate &candidate); + }; +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/channel_manager.cpp b/wrtc/interfaces/media/channel_manager.cpp new file mode 100644 index 00000000..216506fc --- /dev/null +++ b/wrtc/interfaces/media/channel_manager.cpp @@ -0,0 +1,141 @@ +// +// Created by Laky64 on 01/04/2024. +// + +#include "channel_manager.hpp" + +#include + +namespace wrtc { + ChannelManager::ChannelManager( + cricket::MediaEngineInterface *mediaEngine, + rtc::Thread* workerThread, + rtc::Thread* networkThread, + rtc::Thread* signalingThread + ): mediaEngine(mediaEngine), signalingThread(signalingThread), workerThread(workerThread), networkThread(networkThread) { + RTC_DCHECK_RUN_ON(signalingThread); + RTC_DCHECK(workerThread); + RTC_DCHECK(networkThread); + } + + std::unique_ptr ChannelManager::CreateVoiceChannel( + webrtc::Call *call, + const cricket::MediaConfig &mediaConfig, + const std::string &mid, + const bool srtpRequired, + const webrtc::CryptoOptions &cryptoOptions, + const cricket::AudioOptions &options + ) { + RTC_DCHECK(call); + RTC_DCHECK(mediaEngine); + if (!workerThread->IsCurrent()) { + std::unique_ptr temp; + workerThread->BlockingCall([&] { + temp = CreateVoiceChannel(call, mediaConfig, mid, srtpRequired, cryptoOptions, options); + }); + return std::move(temp); + } + RTC_DCHECK_RUN_ON(workerThread); + auto sendMediaChannel = mediaEngine->voice().CreateSendChannel( + call, + mediaConfig, + options, + cryptoOptions, + webrtc::AudioCodecPairId::Create() + ); + if (!sendMediaChannel) { + return nullptr; + } + auto receiveMediaChannel = mediaEngine->voice().CreateReceiveChannel( + call, + mediaConfig, + options, + cryptoOptions, + webrtc::AudioCodecPairId::Create() + ); + if (!receiveMediaChannel) { + return nullptr; + } + return std::make_unique( + workerThread, + networkThread, + signalingThread, + std::move(sendMediaChannel), + std::move(receiveMediaChannel), + mid, + srtpRequired, + cryptoOptions, + &ssrcGenerator + ); + } + + std::unique_ptr ChannelManager::CreateVideoChannel( + webrtc::Call *call, + const cricket::MediaConfig &mediaConfig, + const std::string &mid, + const bool srtpRequired, + const webrtc::CryptoOptions &cryptoOptions, + const cricket::VideoOptions &options, + webrtc::VideoBitrateAllocatorFactory *bitrateAllocatorFactory + ) { + RTC_DCHECK(call); + RTC_DCHECK(mediaEngine); + if (!workerThread->IsCurrent()) { + std::unique_ptr temp = nullptr; + workerThread->BlockingCall([&] { + temp = CreateVideoChannel( + call, + mediaConfig, + mid, + srtpRequired, + cryptoOptions, + options, + bitrateAllocatorFactory + ); + }); + return temp; + } + RTC_DCHECK_RUN_ON(workerThread); + std::unique_ptr sendMediaChannel = mediaEngine->video().CreateSendChannel( + call, + mediaConfig, + options, + cryptoOptions, + bitrateAllocatorFactory + ); + if (!sendMediaChannel) { + return nullptr; + } + std::unique_ptr receiveMediaChannel = mediaEngine->video().CreateReceiveChannel( + call, + mediaConfig, + options, + cryptoOptions + ); + return std::make_unique( + workerThread, + networkThread, + signalingThread, + std::move(sendMediaChannel), + std::move(receiveMediaChannel), + mid, + srtpRequired, + cryptoOptions, + &ssrcGenerator + ); + } + + void ChannelManager::DestroyChannel(cricket::ChannelInterface* channel) { + RTC_DCHECK(channel); + if (!workerThread->IsCurrent()) { + workerThread->BlockingCall([&] { DestroyChannel(channel); }); + return; + } + if (channel->media_type() == cricket::MEDIA_TYPE_AUDIO) { + //DestroyVoiceChannel(static_cast(channel)); + } else { + RTC_DCHECK_EQ(channel->media_type(), cricket::MEDIA_TYPE_VIDEO); + //DestroyVideoChannel(dynamic_cast(channel)); + } + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/channel_manager.hpp b/wrtc/interfaces/media/channel_manager.hpp new file mode 100644 index 00000000..c2d0f230 --- /dev/null +++ b/wrtc/interfaces/media/channel_manager.hpp @@ -0,0 +1,48 @@ +// +// Created by Laky64 on 01/04/2024. +// + +#pragma once +#include +#include +#include +#include "api/video/video_bitrate_allocator_factory.h" + +namespace wrtc { + class ChannelManager { + cricket::MediaEngineInterface* mediaEngine; + rtc::Thread* signalingThread; + rtc::Thread* workerThread; + rtc::Thread* networkThread; + rtc::UniqueRandomIdGenerator ssrcGenerator; + + public: + ChannelManager( + cricket::MediaEngineInterface* mediaEngine, + rtc::Thread* workerThread, + rtc::Thread* networkThread, + rtc::Thread* signalingThread + ); + + std::unique_ptr CreateVoiceChannel( + webrtc::Call* call, + const cricket::MediaConfig& mediaConfig, + const std::string& mid, + bool srtpRequired, + const webrtc::CryptoOptions& cryptoOptions, + const cricket::AudioOptions& options + ); + + std::unique_ptr CreateVideoChannel( + webrtc::Call* call, + const cricket::MediaConfig& mediaConfig, + const std::string& mid, + bool srtpRequired, + const webrtc::CryptoOptions& cryptoOptions, + const cricket::VideoOptions& options, + webrtc::VideoBitrateAllocatorFactory* bitrateAllocatorFactory + ); + + void DestroyChannel(cricket::ChannelInterface* channel); + }; +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/channels/outgoing_audio_channel.cpp b/wrtc/interfaces/media/channels/outgoing_audio_channel.cpp new file mode 100644 index 00000000..f76e24f9 --- /dev/null +++ b/wrtc/interfaces/media/channels/outgoing_audio_channel.cpp @@ -0,0 +1,103 @@ +// +// Created by Laky64 on 31/03/2024. +// + +#include "outgoing_audio_channel.hpp" + +#include "wrtc/interfaces/native_connection.hpp" + +namespace wrtc { + OutgoingAudioChannel::OutgoingAudioChannel( + webrtc::Call *call, + ChannelManager *channelManager, + webrtc::RtpTransport* rtpTransport, + const MediaContent& mediaContent, + rtc::Thread *workerThread, + rtc::Thread* networkThread, + webrtc::LocalAudioSinkAdapter* sink + ): _ssrc(mediaContent.ssrc), workerThread(workerThread), networkThread(networkThread) { + cricket::AudioOptions audioOptions; + audioOptions.echo_cancellation = false; + audioOptions.noise_suppression = false; + audioOptions.auto_gain_control = false; + audioOptions.highpass_filter = false; + + const auto contentId = std::to_string(_ssrc); + channel = channelManager->CreateVoiceChannel( + call, + cricket::MediaConfig(), + contentId, + false, + NativeConnection::getDefaultCryptoOptions(), + audioOptions + ); + networkThread->BlockingCall([&] { + channel->SetRtpTransport(rtpTransport); + }); + std::vector codecs; + for (const auto &[id, name, clockrate, channels, feedbackTypes, parameters] : mediaContent.payloadTypes) { + if (name == "opus") { + cricket::Codec codec = cricket::CreateAudioCodec(static_cast(id), name, static_cast(clockrate), channels); + codec.SetParam(cricket::kCodecParamUseInbandFec, 1); + codec.SetParam(cricket::kCodecParamPTime, 60); + for (const auto &[type, subtype] : feedbackTypes) { + codec.AddFeedbackParam(cricket::FeedbackParam(type, subtype)); + } + codecs.push_back(std::move(codec)); + break; + } + } + const auto outgoingDescription = std::make_unique(); + for (const auto &rtpExtension : mediaContent.rtpExtensions) { + outgoingDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id)); + } + outgoingDescription->set_rtcp_mux(true); + outgoingDescription->set_rtcp_reduced_size(true); + outgoingDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly); + outgoingDescription->set_codecs(codecs); + outgoingDescription->set_bandwidth(-1); + outgoingDescription->AddStream(cricket::StreamParams::CreateLegacy(_ssrc)); + const auto incomingDescription = std::make_unique(); + for (const auto &rtpExtension : mediaContent.rtpExtensions) { + incomingDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id)); + } + incomingDescription->set_rtcp_mux(true); + incomingDescription->set_rtcp_reduced_size(true); + incomingDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly); + incomingDescription->set_codecs(codecs); + incomingDescription->set_bandwidth(-1); + workerThread->BlockingCall([&] { + channel->SetPayloadTypeDemuxingEnabled(false); + std::string errorDesc; + channel->SetLocalContent(outgoingDescription.get(), webrtc::SdpType::kOffer, errorDesc); + channel->SetRemoteContent(incomingDescription.get(), webrtc::SdpType::kAnswer, errorDesc); + }); + channel->Enable(true); + workerThread->BlockingCall([&] { + channel->send_channel()->SetAudioSend(_ssrc, true, nullptr, sink); + webrtc::RtpParameters initialParameters = channel->send_channel()->GetRtpSendParameters(_ssrc); + webrtc::RtpParameters updatedParameters = initialParameters; + if (updatedParameters.encodings.empty()) { + updatedParameters.encodings.emplace_back(); + } + updatedParameters.encodings[0].max_bitrate_bps = 32 * 1024; + if (initialParameters != updatedParameters) { + channel->send_channel()->SetRtpSendParameters(_ssrc, updatedParameters); + } + }); + } + + OutgoingAudioChannel::~OutgoingAudioChannel() { + channel->Enable(false); + networkThread->BlockingCall([&] { + channel->SetRtpTransport(nullptr); + }); + workerThread->BlockingCall([&] { + channel = nullptr; + }); + } + + uint32_t OutgoingAudioChannel::ssrc() const { + return _ssrc; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/channels/outgoing_audio_channel.hpp b/wrtc/interfaces/media/channels/outgoing_audio_channel.hpp new file mode 100644 index 00000000..a75e9111 --- /dev/null +++ b/wrtc/interfaces/media/channels/outgoing_audio_channel.hpp @@ -0,0 +1,35 @@ +// +// Created by Laky64 on 31/03/2024. +// + +#pragma once +#include +#include +#include + +#include "../../../models/media_content.hpp" +#include "../channel_manager.hpp" + +namespace wrtc { + class OutgoingAudioChannel : public sigslot::has_slots<> { + uint32_t _ssrc = 0; + std::unique_ptr channel; + rtc::Thread* workerThread; + rtc::Thread* networkThread; + + public: + OutgoingAudioChannel( + webrtc::Call* call, + ChannelManager* channelManager, + webrtc::RtpTransport* rtpTransport, + const MediaContent& mediaContent, + rtc::Thread* workerThread, + rtc::Thread* networkThread, + webrtc::LocalAudioSinkAdapter* sink + ); + + ~OutgoingAudioChannel() override; + + [[nodiscard]] uint32_t ssrc() const; + }; +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/channels/outgoing_video_channel.cpp b/wrtc/interfaces/media/channels/outgoing_video_channel.cpp new file mode 100644 index 00000000..6fa39858 --- /dev/null +++ b/wrtc/interfaces/media/channels/outgoing_video_channel.cpp @@ -0,0 +1,123 @@ +// +// Created by Laky64 on 02/04/2024. +// + +#include "outgoing_video_channel.hpp" + +#include "wrtc/interfaces/native_connection.hpp" +#include "api/video/builtin_video_bitrate_allocator_factory.h" + +namespace wrtc { + OutgoingVideoChannel::OutgoingVideoChannel( + webrtc::Call *call, + ChannelManager *channelManager, + webrtc::RtpTransport *rtpTransport, + const MediaContent &mediaContent, + rtc::Thread *workerThread, + rtc::Thread *networkThread, + LocalVideoAdapter* videoSink + ): _ssrc(mediaContent.ssrc), workerThread(workerThread), networkThread(networkThread) { + cricket::VideoOptions videoOptions; + videoOptions.is_screencast = false; + bitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory(); + channel = channelManager->CreateVideoChannel( + call, + cricket::MediaConfig(), + std::to_string(mediaContent.ssrc), + false, + NativeConnection::getDefaultCryptoOptions(), + videoOptions, + bitrateAllocatorFactory.get() + ); + networkThread->BlockingCall([&] { + channel->SetRtpTransport(rtpTransport); + }); + std::vector unsortedCodecs; + for (const auto &[id, name, clockrate, channels, feedbackTypes, parameters] : mediaContent.payloadTypes) { + cricket::Codec codec = cricket::CreateVideoCodec(static_cast(id), name); + for (const auto &[fst, snd] : parameters) { + codec.SetParam(fst, snd); + } + for (const auto &[type, subtype] : feedbackTypes) { + codec.AddFeedbackParam(cricket::FeedbackParam(type, subtype)); + } + unsortedCodecs.push_back(std::move(codec)); + } + const std::vector codecPreferences = { + cricket::kH264CodecName + }; + std::vector codecs; + for (const auto &name : codecPreferences) { + for (const auto &codec : unsortedCodecs) { + if (codec.name == name) { + codecs.push_back(codec); + } + } + } + for (const auto &codec : unsortedCodecs) { + if (std::ranges::find(codecs, codec) == codecs.end()) { + codecs.push_back(codec); + } + } + + auto outgoingVideoDescription = std::make_unique(); + for (const auto &rtpExtension : mediaContent.rtpExtensions) { + outgoingVideoDescription->AddRtpHeaderExtension(rtpExtension); + } + outgoingVideoDescription->set_rtcp_mux(true); + outgoingVideoDescription->set_rtcp_reduced_size(true); + outgoingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly); + outgoingVideoDescription->set_codecs(codecs); + outgoingVideoDescription->set_bandwidth(-1); + cricket::StreamParams videoSendStreamParams; + for (const auto &[ssrcs, semantics] : mediaContent.ssrcGroups) { + for (auto ssrc : ssrcs) { + if (!videoSendStreamParams.has_ssrc(ssrc)) { + videoSendStreamParams.ssrcs.push_back(ssrc); + } + } + cricket::SsrcGroup mappedGroup(semantics, ssrcs); + videoSendStreamParams.ssrc_groups.push_back(std::move(mappedGroup)); + } + videoSendStreamParams.cname = "cname"; + outgoingVideoDescription->AddStream(videoSendStreamParams); + + auto incomingVideoDescription = std::make_unique(); + for (const auto &rtpExtension : mediaContent.rtpExtensions) { + incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id)); + } + incomingVideoDescription->set_rtcp_mux(true); + incomingVideoDescription->set_rtcp_reduced_size(true); + incomingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly); + incomingVideoDescription->set_codecs(codecs); + incomingVideoDescription->set_bandwidth(-1); + workerThread->BlockingCall([&] { + channel->SetPayloadTypeDemuxingEnabled(false); + std::string errorDesc; + channel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, errorDesc); + channel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, errorDesc); + webrtc::RtpParameters rtpParameters = channel->send_channel()->GetRtpSendParameters(mediaContent.ssrc); + rtpParameters.degradation_preference = webrtc::DegradationPreference::MAINTAIN_RESOLUTION; + channel->send_channel()->SetRtpSendParameters(mediaContent.ssrc, rtpParameters); + }); + channel->Enable(false); + workerThread->BlockingCall([&] { + channel->send_channel()->SetVideoSend(mediaContent.ssrc, nullptr, videoSink); + }); + } + + OutgoingVideoChannel::~OutgoingVideoChannel() { + channel->Enable(false); + networkThread->BlockingCall([&] { + channel->SetRtpTransport(nullptr); + }); + workerThread->BlockingCall([&] { + channel = nullptr; + bitrateAllocatorFactory = nullptr; + }); + } + + uint32_t OutgoingVideoChannel::ssrc() const { + return _ssrc; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/channels/outgoing_video_channel.hpp b/wrtc/interfaces/media/channels/outgoing_video_channel.hpp new file mode 100644 index 00000000..016f369a --- /dev/null +++ b/wrtc/interfaces/media/channels/outgoing_video_channel.hpp @@ -0,0 +1,36 @@ +// +// Created by Laky64 on 02/04/2024. +// + +#pragma once +#include +#include + +#include "../../../models/media_content.hpp" +#include "../channel_manager.hpp" +#include "wrtc/interfaces/media/local_video_adapter.hpp" + +namespace wrtc { + class OutgoingVideoChannel final : public sigslot::has_slots<> { + uint32_t _ssrc = 0; + std::unique_ptr channel; + rtc::Thread* workerThread; + rtc::Thread* networkThread; + std::unique_ptr bitrateAllocatorFactory; + + public: + OutgoingVideoChannel( + webrtc::Call* call, + ChannelManager* channelManager, + webrtc::RtpTransport* rtpTransport, + const MediaContent& mediaContent, + rtc::Thread* workerThread, + rtc::Thread* networkThread, + LocalVideoAdapter* videoSink + ); + + ~OutgoingVideoChannel() override; + + [[nodiscard]] uint32_t ssrc() const; + }; +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/local_video_adapter.cpp b/wrtc/interfaces/media/local_video_adapter.cpp new file mode 100644 index 00000000..87ab9e21 --- /dev/null +++ b/wrtc/interfaces/media/local_video_adapter.cpp @@ -0,0 +1,23 @@ +// +// Created by Laky64 on 04/09/2024. +// + +#include "local_video_adapter.hpp" +#include "ntgcalls/media/video_streamer.hpp" + +namespace wrtc { + LocalVideoAdapter::LocalVideoAdapter(): _sink(std::nullopt){} + + void LocalVideoAdapter::OnFrame(const webrtc::VideoFrame& frame) { + webrtc::MutexLock lock(&lock_); + if(_sink.has_value()){ + _sink.value().sink->OnFrame(frame); + } + } + + void LocalVideoAdapter::AddOrUpdateSink(VideoSinkInterface* sink, const rtc::VideoSinkWants& wants){ + webrtc::MutexLock lock(&lock_); + RTC_DCHECK(!sink || !_sink.has_value()); + _sink = SinkPair(sink, wants); + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/local_video_adapter.hpp b/wrtc/interfaces/media/local_video_adapter.hpp new file mode 100644 index 00000000..9accc854 --- /dev/null +++ b/wrtc/interfaces/media/local_video_adapter.hpp @@ -0,0 +1,20 @@ +// +// Created by Laky64 on 04/09/2024. +// + +#pragma once +#include + +namespace wrtc { + class LocalVideoAdapter final : public rtc::VideoSinkInterface, public rtc::VideoSourceBaseGuarded { + std::optional _sink; + webrtc::Mutex lock_; + + public: + LocalVideoAdapter(); + + void OnFrame(const webrtc::VideoFrame& frame) override; + + void AddOrUpdateSink(VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) override; + }; +} // wrtc diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp new file mode 100644 index 00000000..3b9a5c9f --- /dev/null +++ b/wrtc/interfaces/native_connection.cpp @@ -0,0 +1,441 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#include "native_connection.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "reflector_relay_port_factory.hpp" +#include "media/rtc_audio_source.hpp" + + +namespace wrtc { + NativeConnection::NativeConnection( + std::vector rtcServers, + const bool enableP2P, + const bool isOutgoing): isOutgoing(isOutgoing), enableP2P(enableP2P), rtcServers(std::move(rtcServers)) { + networkThread()->PostTask([this] { + localParameters = PeerIceParameters( + rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), + rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), + true + ); + localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate( + rtc::KeyParams(rtc::KT_ECDSA), + absl::nullopt + ); + asyncResolverFactory = std::make_unique(); + dtlsSrtpTransport = std::make_unique(true, factory->fieldTrials()); + dtlsSrtpTransport->SetDtlsTransports(nullptr, nullptr); + dtlsSrtpTransport->SetActiveResetSrtpParams(false); + dtlsSrtpTransport->SubscribeReadyToSend(this, [this](const bool readyToSend) { + DtlsReadyToSend(readyToSend); + }); + dtlsSrtpTransport->SubscribeRtcpPacketReceived(this, [this](const rtc::CopyOnWriteBuffer* packet, int64_t) { + workerThread()->PostTask([this, packet = *packet] { + call->Receiver()->DeliverRtcpPacket(packet); + }); + }); + resetDtlsSrtpTransport(); + }); + channelManager = std::make_unique( + factory->mediaEngine(), + workerThread(), + networkThread(), + signalingThread() + ); + workerThread()->BlockingCall([&] { + webrtc::CallConfig callConfig(factory->environment(), networkThread()); + callConfig.audio_state = factory->mediaEngine()->voice().GetAudioState(); + call = factory->mediaFactory()->CreateCall(callConfig); + }); + contentNegotiationContext = std::make_unique(factory->fieldTrials(), isOutgoing, factory->mediaEngine(), factory->ssrcGenerator()); + contentNegotiationContext->copyCodecsFromChannelManager(factory->mediaEngine(), false); + networkThread()->PostTask([this] { + start(); + }); + } + + void NativeConnection::resetDtlsSrtpTransport() { + relayPortFactory = std::make_unique(rtcServers); + portAllocator = std::make_unique( + factory->networkManager(), + factory->socketFactory(), + nullptr, + relayPortFactory.get() + ); + uint32_t flags = portAllocator->flags(); + flags |= cricket::PORTALLOCATOR_ENABLE_IPV6; + flags |= cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI; + flags |= cricket::PORTALLOCATOR_DISABLE_TCP; + if (!enableP2P) { + flags |= cricket::PORTALLOCATOR_DISABLE_UDP; + flags |= cricket::PORTALLOCATOR_DISABLE_STUN; + uint32_t candidateFilter = portAllocator->candidate_filter(); + candidateFilter &= ~cricket::CF_REFLEXIVE; + portAllocator->SetCandidateFilter(candidateFilter); + } + portAllocator->set_step_delay(cricket::kMinimumStepDelay); + portAllocator->set_flags(flags); + portAllocator->Initialize(); + cricket::ServerAddresses stunServers; + std::vector turnServers; + for (auto &[id, host, port, login, password, isTurn, isTcp] : rtcServers) { + if (isTcp) { + continue; + } + if (isTurn) { + turnServers.emplace_back( + rtc::SocketAddress(host, port), + login, + password, + cricket::PROTO_UDP + ); + } else { + auto stunAddress = rtc::SocketAddress(host, port); + stunServers.insert(stunAddress); + } + } + portAllocator->SetConfiguration(stunServers, turnServers, 0, webrtc::NO_PRUNE); + webrtc::IceTransportInit iceTransportInit; + iceTransportInit.set_port_allocator(portAllocator.get()); + iceTransportInit.set_async_dns_resolver_factory(asyncResolverFactory.get()); + transportChannel = cricket::P2PTransportChannel::Create("transport", 0, std::move(iceTransportInit)); + cricket::IceConfig iceConfig; + iceConfig.continual_gathering_policy = cricket::GATHER_CONTINUALLY; + iceConfig.prioritize_most_likely_candidate_pairs = true; + iceConfig.regather_on_failed_networks_interval = 8000; + transportChannel->SetIceConfig(iceConfig); + const cricket::IceParameters localIceParameters( + localParameters.ufrag, + localParameters.pwd, + localParameters.supportsRenomination + ); + transportChannel->SetIceParameters(localIceParameters); + transportChannel->SetIceRole(isOutgoing ? cricket::ICEROLE_CONTROLLING : cricket::ICEROLE_CONTROLLED); + transportChannel->SetRemoteIceMode(cricket::ICEMODE_FULL); + transportChannel->SignalCandidateGathered.connect(this, &NativeConnection::candidateGathered); + transportChannel->SignalIceTransportStateChanged.connect(this, &NativeConnection::transportStateChanged); + transportChannel->SetCandidatePairChangeCallback([this](cricket::CandidatePairChangeEvent const &event) { + candidatePairChanged(event); + }); + transportChannel->SignalNetworkRouteChanged.connect(this, &NativeConnection::transportRouteChanged); + dtlsTransport = std::make_unique(transportChannel.get(), getDefaultCryptoOptions(), nullptr); + dtlsTransport->SignalWritableState.connect(this, &NativeConnection::OnTransportWritableState_n); + dtlsTransport->SignalReceivingState.connect(this, &NativeConnection::OnTransportReceivingState_n); + dtlsTransport->SetLocalCertificate(localCertificate); + dtlsSrtpTransport->SetDtlsTransports(dtlsTransport.get(), nullptr); + } + + void NativeConnection::createChannels() { + const auto coordinatedState = contentNegotiationContext->coordinatedState(); + if (!coordinatedState) { + return; + } + if (audioChannelId) { + if (const auto audioSsrc = contentNegotiationContext->outgoingChannelSsrc(*audioChannelId)) { + if (audioChannel && audioChannel->ssrc() != audioSsrc.value()) { + audioChannel = nullptr; + } + std::optional audioContent; + for (const auto &content : coordinatedState->outgoingContents) { + if (content.type == MediaContent::Type::Audio && content.ssrc == audioSsrc.value()) { + audioContent = content; + break; + } + } + if (audioContent) { + if (!audioChannel) { + audioChannel = std::make_unique( + call.get(), + channelManager.get(), + dtlsSrtpTransport.get(), + *audioContent, + workerThread(), + networkThread(), + &audioSink + ); + } + } + } + } + if (videoChannelId) { + if (const auto videoSsrc = contentNegotiationContext->outgoingChannelSsrc(*videoChannelId)) { + if (videoChannel && videoChannel->ssrc() != videoSsrc.value()) { + videoChannel = nullptr; + } + std::optional videoContent; + for (const auto &content : coordinatedState->outgoingContents) { + if (content.type == MediaContent::Type::Video && content.ssrc == videoSsrc.value()) { + videoContent = content; + break; + } + } + if (videoContent) { + if (!videoChannel) { + videoChannel = std::make_unique( + call.get(), + channelManager.get(), + dtlsSrtpTransport.get(), + *videoContent, + workerThread(), + networkThread(), + &videoSink + ); + } + } + } + } + } + + NativeConnection::~NativeConnection() { + close(); + isExiting = true; + dtlsSrtpTransport = nullptr; + asyncResolverFactory = nullptr; + } + + void NativeConnection::notifyStateUpdated() const { + ConnectionState newValue; + if (failed) { + newValue = ConnectionState::Failed; + } else if (connected) { + newValue = ConnectionState::Connected; + } else { + newValue = ConnectionState::Connecting; + } + signalingThread()->PostTask([this, newValue] { + (void) connectionChangeCallback(newValue); + }); + } + + void NativeConnection::DtlsReadyToSend(const bool isReadyToSend) { + UpdateAggregateStates_n(); + + if (isReadyToSend) { + networkThread()->PostTask([this] { + UpdateAggregateStates_n(); + }); + } + } + + // ReSharper disable once CppMemberFunctionMayBeConst + void NativeConnection::candidateGathered(cricket::IceTransportInternal*, const cricket::Candidate& candidate) { + assert(networkThread()->IsCurrent()); + signalingThread()->PostTask([this, candidate] { + cricket::Candidate patchedCandidate = candidate; + patchedCandidate.set_component(1); + webrtc::JsepIceCandidate iceCandidate{std::string(),0}; + iceCandidate.SetCandidate(patchedCandidate); + (void) iceCandidateCallback(IceCandidate(&iceCandidate)); + }); + } + + void NativeConnection::transportStateChanged(cricket::IceTransportInternal*) { + UpdateAggregateStates_n(); + } + + // ReSharper disable once CppPassValueParameterByConstReference + void NativeConnection::transportRouteChanged(absl::optional route) { + assert(networkThread()->IsCurrent()); + if (route.has_value()) { + RTC_LOG(LS_INFO) << "NativeNetworkingImpl route changed: " << route->DebugString(); + const bool localIsWifi = route->local.adapter_type() == rtc::AdapterType::ADAPTER_TYPE_WIFI; + const bool remoteIsWifi = route->remote.adapter_type() == rtc::AdapterType::ADAPTER_TYPE_WIFI; + RTC_LOG(LS_INFO) << "NativeNetworkingImpl is wifi: local=" << localIsWifi << ", remote=" << remoteIsWifi; + const std::string localDescription = route->local.uses_turn() ? "turn" : "p2p"; + const std::string remoteDescription = route->remote.uses_turn() ? "turn" : "p2p"; + if (RouteDescription routeDescription(localDescription, remoteDescription); !currentRouteDescription || routeDescription != currentRouteDescription.value()) { + currentRouteDescription = std::move(routeDescription); + notifyStateUpdated(); + } + } + } + + void NativeConnection::OnTransportWritableState_n(rtc::PacketTransportInternal*) { + assert(networkThread()->IsCurrent()); + UpdateAggregateStates_n(); + } + + void NativeConnection::OnTransportReceivingState_n(rtc::PacketTransportInternal*){ + assert(networkThread()->IsCurrent()); + UpdateAggregateStates_n(); + } + + void NativeConnection::candidatePairChanged(cricket::CandidatePairChangeEvent const& event) { + ConnectionDescription connectionDescription; + + connectionDescription.local = connectionDescriptionFromCandidate(event.selected_candidate_pair.local); + connectionDescription.remote = connectionDescriptionFromCandidate(event.selected_candidate_pair.remote); + + if (!currentConnectionDescription || currentConnectionDescription.value() != connectionDescription) { + currentConnectionDescription = std::move(connectionDescription); + notifyStateUpdated(); + } + } + + void NativeConnection::UpdateAggregateStates_n() { + assert(networkThread()->IsCurrent()); + const auto state = transportChannel->GetIceTransportState(); + bool isConnected = false; + switch (state) { + case webrtc::IceTransportState::kConnected: + case webrtc::IceTransportState::kCompleted: + isConnected = true; + break; + default: + break; + } + if (!dtlsSrtpTransport->IsWritable(false)) { + isConnected = false; + } + if (connected != isConnected) { + connected = isConnected; + if (!isConnected) { + lastDisconnectedTimestamp = rtc::TimeMillis(); + } + notifyStateUpdated(); + if (dataChannelInterface) { + dataChannelInterface->updateIsConnected(isConnected); + } + } + } + + webrtc::CryptoOptions NativeConnection::getDefaultCryptoOptions() { + auto options = webrtc::CryptoOptions(); + options.srtp.enable_aes128_sha1_80_crypto_cipher = true; + options.srtp.enable_gcm_crypto_suites = true; + return options; + } + + void NativeConnection::start() { + transportChannel->MaybeStartGathering(); + dataChannelInterface = std::make_unique( + environment(), + transportChannel.get(), + isOutgoing, + networkThread(), + signalingThread() + ); + dataChannelInterface->onStateChanged([this](const bool isOpen) { + if (!dataChannelOpen && isOpen) { + dataChannelOpen = true; + (void) dataChannelOpenedCallback(); + } else { + dataChannelOpen = false; + } + }); + lastDisconnectedTimestamp = rtc::TimeMillis(); + checkConnectionTimeout(); + } + + void NativeConnection::close() { + if (factory) { + audioChannel = nullptr; + videoChannel = nullptr; + networkThread()->BlockingCall([this] { + dtlsSrtpTransport->UnsubscribeReadyToSend(this); + transportChannel->SignalCandidateGathered.disconnect(this); + transportChannel->SignalIceTransportStateChanged.disconnect(this); + transportChannel->SignalNetworkRouteChanged.disconnect(this); + dtlsTransport->SignalWritableState.disconnect(this); + dtlsTransport->SignalReceivingState.disconnect(this); + dtlsSrtpTransport->SetDtlsTransports(nullptr, nullptr); + dataChannelInterface = nullptr; + dtlsTransport = nullptr; + transportChannel = nullptr; + portAllocator = nullptr; + }); + NetworkInterface::close(); + } + } + + void NativeConnection::sendDataChannelMessage(const bytes::binary& data) const { + networkThread()->PostTask([this, data] { + if (dataChannelInterface) { + dataChannelInterface->sendDataChannelMessage(data); + } + }); + } + + void NativeConnection::addIceCandidate(const IceCandidate& rawCandidate) const { + networkThread()->PostTask([this, rawCandidate] { + transportChannel->AddRemoteCandidate(parseIceCandidate(rawCandidate)->candidate()); + }); + } + + void NativeConnection::setRemoteParams(PeerIceParameters remoteIceParameters, std::unique_ptr fingerprint, const std::string& sslSetup) { + networkThread()->PostTask([this, remoteIceParameters = std::move(remoteIceParameters), fingerprint = std::move(fingerprint), sslSetup] { + remoteParameters = remoteIceParameters; + const cricket::IceParameters parameters( + remoteIceParameters.ufrag, + remoteIceParameters.pwd, + remoteIceParameters.supportsRenomination + ); + transportChannel->SetRemoteIceParameters(parameters); + if (sslSetup == "active") { + dtlsTransport->SetDtlsRole(rtc::SSLRole::SSL_SERVER); + } else if (sslSetup == "passive") { + dtlsTransport->SetDtlsRole(rtc::SSLRole::SSL_CLIENT); + } else { + dtlsTransport->SetDtlsRole(isOutgoing ? rtc::SSLRole::SSL_CLIENT : rtc::SSLRole::SSL_SERVER); + } + if (fingerprint) { + dtlsTransport->SetRemoteFingerprint(fingerprint->algorithm, fingerprint->digest.data(), fingerprint->digest.size()); + } + }); + } + + std::unique_ptr NativeConnection::getPendingOffer() const { + return contentNegotiationContext->getPendingOffer(); + } + + std::unique_ptr NativeConnection::setPendingAnswer(std::unique_ptr answer) const { + return contentNegotiationContext->setPendingAnwer(std::move(answer)); + } + + void NativeConnection::addTrack(const rtc::scoped_refptr& track) { + if (const auto audioTrack = dynamic_cast(track.get())) { + audioChannelId = contentNegotiationContext->addOutgoingChannel(audioTrack); + audioTrack->AddSink(&audioSink); + } else if (const auto videoTrack = dynamic_cast(track.get())) { + videoChannelId = contentNegotiationContext->addOutgoingChannel(videoTrack); + videoTrack->AddOrUpdateSink(&videoSink, rtc::VideoSinkWants()); + } + } + + std::unique_ptr NativeConnection::localFingerprint() const { + const auto certificate = localCertificate; + if (!certificate) { + return nullptr; + } + return rtc::SSLFingerprint::CreateFromCertificate(*certificate); + } + + PeerIceParameters NativeConnection::localIceParameters() { + return localParameters; + } + + void NativeConnection::checkConnectionTimeout() { + networkThread()->PostDelayedTask([this] { + if (isExiting) return; + const int64_t currentTimestamp = rtc::TimeMillis(); + if (constexpr int64_t maxTimeout = 20000; !connected && lastDisconnectedTimestamp + maxTimeout < currentTimestamp) { + RTC_LOG(LS_INFO) << "NativeNetworkingImpl timeout " << currentTimestamp - lastDisconnectedTimestamp << " ms"; + failed = true; + notifyStateUpdated(); + return; + } + checkConnectionTimeout(); + }, webrtc::TimeDelta::Millis(1000)); + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/native_connection.hpp b/wrtc/interfaces/native_connection.hpp new file mode 100644 index 00000000..d5ac6dd9 --- /dev/null +++ b/wrtc/interfaces/native_connection.hpp @@ -0,0 +1,108 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#include "instance_networking.hpp" +#include "network_interface.hpp" +#include "sctp_data_channel_provider_interface_impl.hpp" +#include "media/channel_manager.hpp" +#include "media/local_video_adapter.hpp" +#include "media/channels/outgoing_audio_channel.hpp" +#include "media/channels/outgoing_video_channel.hpp" +#include "wrtc/models/content_negotiation_context.hpp" +#include "wrtc/models/peer_ice_parameters.hpp" +#include "wrtc/models/rtc_server.hpp" + +namespace wrtc { + class NativeConnection final : public sigslot::has_slots<>, public InstanceNetworking, public NetworkInterface { + bool connected = false, failed = false; + std::atomic isExiting; + bool isOutgoing, enableP2P; + int64_t lastDisconnectedTimestamp = 0; + std::vector rtcServers; + PeerIceParameters localParameters, remoteParameters; + rtc::scoped_refptr localCertificate; + std::unique_ptr dtlsTransport; + std::unique_ptr dtlsSrtpTransport; + std::unique_ptr relayPortFactory; + std::unique_ptr portAllocator; + std::unique_ptr asyncResolverFactory; + std::unique_ptr transportChannel; + std::unique_ptr dataChannelInterface; + absl::optional currentRouteDescription; + absl::optional currentConnectionDescription; + std::unique_ptr eventLog; + std::unique_ptr taskQueueFactory; + std::unique_ptr contentNegotiationContext; + std::optional audioChannelId, videoChannelId; + std::unique_ptr call; + std::unique_ptr channelManager; + std::unique_ptr audioChannel; + std::unique_ptr videoChannel; + webrtc::LocalAudioSinkAdapter audioSink; + LocalVideoAdapter videoSink; + + void notifyStateUpdated() const; + + void DtlsReadyToSend(bool isReadyToSend); + + void candidateGathered(cricket::IceTransportInternal *transport, const cricket::Candidate &candidate); + + void transportStateChanged(cricket::IceTransportInternal *transport); + + void transportRouteChanged(absl::optional route); + + void UpdateAggregateStates_n(); + + void OnTransportWritableState_n(rtc::PacketTransportInternal *transport); + + void OnTransportReceivingState_n(rtc::PacketTransportInternal *transport); + + void candidatePairChanged(cricket::CandidatePairChangeEvent const &event); + + void checkConnectionTimeout(); + + void start(); + + void resetDtlsSrtpTransport(); + + + public: + explicit NativeConnection(std::vector rtcServers, bool enableP2P, bool isOutgoing); + + ~NativeConnection() override; + + static webrtc::CryptoOptions getDefaultCryptoOptions(); + + void close() override; + + void createChannels(); + + void sendDataChannelMessage(const bytes::binary& data) const override; + + void addIceCandidate(const IceCandidate& rawCandidate) const override; + + void setRemoteParams(PeerIceParameters remoteIceParameters, std::unique_ptr fingerprint, const std::string& sslSetup); + + std::unique_ptr getPendingOffer() const; + + std::unique_ptr setPendingAnswer(std::unique_ptr answer) const; + + void addTrack(const rtc::scoped_refptr& track) override; + + std::unique_ptr localFingerprint() const; + + PeerIceParameters localIceParameters(); + }; + +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/reflector_port.cpp b/wrtc/interfaces/reflector_port.cpp new file mode 100644 index 00000000..27c7d1fc --- /dev/null +++ b/wrtc/interfaces/reflector_port.cpp @@ -0,0 +1,631 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#include "reflector_port.hpp" + +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/strings/match.h" +#include "absl/types/optional.h" +#include "api/transport/stun.h" +#include "p2p/base/connection.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/byte_order.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helpers.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/strings/string_builder.h" +#include "system_wrappers/include/field_trial.h" + +namespace wrtc { + ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* socket, const uint8_t serverId, const int serverPriority): Port( + PortParametersRef( + args.network_thread, + args.socket_factory, + args.network, + args.username, + args.password + ), + webrtc::IceCandidateType::kRelay + ), + serverAddress(*args.server_address), + serverId(serverId), + socket(socket), + error(0), + state(STATE_CONNECTING), + stunDscpValue(rtc::DSCP_NO_CHANGE), + credentials(args.config->credentials), + serverPriority(serverPriority) + { + const auto rawPeerTag = parseHex(args.config->credentials.password); + auto generator = std::mt19937(std::random_device()()); + do { + std::uniform_int_distribution distribution; + randomTag = distribution(generator); + } while (!randomTag); + peerTag.AppendData(rawPeerTag.data(), rawPeerTag.size() - 4); + peerTag.AppendData(reinterpret_cast(&randomTag), 4); + } + + ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args, const uint16_t min_port, const uint16_t max_port, const uint8_t serverId, const int serverPriority): Port( + PortParametersRef( + args.network_thread, + args.socket_factory, + args.network, + args.username, + args.password + ), + webrtc::IceCandidateType::kRelay, + min_port, + max_port + ), + serverAddress(*args.server_address), + serverId(serverId), + socket(nullptr), + error(0), + state(STATE_CONNECTING), + stunDscpValue(rtc::DSCP_NO_CHANGE), + credentials(args.config->credentials), + serverPriority(serverPriority) + { + const auto rawPeerTag = parseHex(args.config->credentials.password); + auto generator = std::mt19937(std::random_device()()); + do { + std::uniform_int_distribution distribution; + randomTag = distribution(generator); + } while (!randomTag); + peerTag.AppendData(rawPeerTag.data(), rawPeerTag.size() - 4); + peerTag.AppendData(reinterpret_cast(&randomTag), 4); + } + + bool ReflectorPort::ready() const { + return state == STATE_READY; + } + + bool ReflectorPort::connected() const { + return state == STATE_READY || state == STATE_CONNECTED; + } + + ReflectorPort::~ReflectorPort() { + if (ready()) { + Release(); + } + delete socket; + } + + std::unique_ptr ReflectorPort::Create(const cricket::CreateRelayPortArgs &args, rtc::AsyncPacketSocket *socket, const uint8_t serverId, const int serverPriority) { + if (args.config->credentials.username.size() > 32) { + RTC_LOG(LS_ERROR) << "Attempt to use REFLECTOR with a too long username of length " << args.config->credentials.username.size(); + return nullptr; + } + return absl::WrapUnique(new ReflectorPort(args, socket, serverId, serverPriority)); + } + + std::unique_ptr ReflectorPort::Create(const cricket::CreateRelayPortArgs &args, const uint16_t minPort, const uint16_t maxPort, const uint8_t serverId, const int serverPriority) { + if (args.config->credentials.username.size() > 32) { + RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username of length " << args.config->credentials.username.size(); + return nullptr; + } + return absl::WrapUnique(new ReflectorPort(args, minPort, maxPort, serverId, serverPriority)); + } + + rtc::SocketAddress ReflectorPort::GetLocalAddress() const { + return socket ? socket->GetLocalAddress() : rtc::SocketAddress(); + } + + cricket::ProtocolType ReflectorPort::GetProtocol() const { + return serverAddress.proto; + } + + void ReflectorPort::PrepareAddress() { + if (peerTag.size() != 16) { + RTC_LOG(LS_ERROR) << "Allocation can't be started without setting the peer tag."; + OnAllocateError(cricket::STUN_ERROR_UNAUTHORIZED, "Missing REFLECTOR server credentials."); + return; + } + if (serverId == 0) { + RTC_LOG(LS_ERROR) << "Allocation can't be started without setting the server id."; + OnAllocateError(cricket::STUN_ERROR_UNAUTHORIZED, "Missing REFLECTOR server id."); + return; + } + if (!serverAddress.address.port()) { + serverAddress.address.SetPort(599); + } + if (serverAddress.address.IsUnresolvedIP()) { + ResolveTurnAddress(serverAddress.address); + } else { + if (!IsCompatibleAddress(serverAddress.address)) { + RTC_LOG(LS_ERROR) << "IP address family does not match. server: " << serverAddress.address.family() << " local: " << Network()->GetBestIP().family(); + OnAllocateError(cricket::STUN_ERROR_GLOBAL_FAILURE, "IP address family does not match."); + return; + } + attemptedServerAddresses.insert(serverAddress.address); + RTC_LOG(LS_INFO) << ToString() << ": Trying to connect to REFLECTOR server via " << ProtoToString(serverAddress.proto) << " @ " << serverAddress.address.ToSensitiveString(); + if (!CreateReflectorClientSocket()) { + RTC_LOG(LS_ERROR) << "Failed to create REFLECTOR client socket"; + OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR, + "Failed to create REFLECTOR client socket."); + return; + } + if (serverAddress.proto == cricket::PROTO_UDP) { + SendReflectorHello(); + } + } + } + + void ReflectorPort::SendReflectorHello() { + if (!(state == STATE_CONNECTED || state == STATE_READY)) { + return; + } + RTC_LOG(LS_WARNING) << ToString() << ": REFLECTOR sending ping to " << serverAddress.address.ToString(); + rtc::ByteBufferWriter bufferWriter; + bufferWriter.WriteBytes(peerTag.data(), peerTag.size()); + for (int i = 0; i < 12; i++) { + bufferWriter.WriteUInt8(0xffu); + } + bufferWriter.WriteUInt8(0xfeu); + for (int i = 0; i < 3; i++) { + bufferWriter.WriteUInt8(0xffu); + } + bufferWriter.WriteUInt64(123); + while (bufferWriter.Length() % 4 != 0) { + bufferWriter.WriteUInt8(0); + } + const rtc::PacketOptions options; + (void) Send(bufferWriter.Data(), bufferWriter.Length(), options); + if (!isRunningPingTask) { + isRunningPingTask = true; + int timeoutMs = 10000; + if (state == STATE_CONNECTED) { + timeoutMs = 500; + } + thread()->PostDelayedTask(SafeTask(taskSafety.flag(), [this] { + isRunningPingTask = false; + SendReflectorHello(); + }), webrtc::TimeDelta::Millis(timeoutMs)); + } + } + + bool ReflectorPort::CreateReflectorClientSocket() { + RTC_DCHECK(!socket || SharedSocket()); + if (serverAddress.proto == cricket::PROTO_UDP && !SharedSocket()) { + socket = socket_factory()->CreateUdpSocket(rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port()); + } else if (serverAddress.proto == cricket::PROTO_TCP) { + RTC_DCHECK(!SharedSocket()); + constexpr int opts = rtc::PacketSocketFactory::OPT_STUN; + rtc::PacketSocketTcpOptions tcp_options; + tcp_options.opts = opts; + socket = socket_factory()->CreateClientTcpSocket( + rtc::SocketAddress(Network()->GetBestIP(), 0), + serverAddress.address, + get_proxy(), + get_user_agent(), + tcp_options + ); + } + if (!socket) { + error = SOCKET_ERROR; + return false; + } + for (auto &[fst, snd] : socketOptions) { + socket->SetOption(fst, snd); + } + if (!SharedSocket()) { + socket->RegisterReceivedPacketCallback([this](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) { + OnReadPacket(socket, packet); + }); + } + socket->SignalReadyToSend.connect(this, &ReflectorPort::OnReadyToSend); + socket->SignalSentPacket.connect(this, &ReflectorPort::OnSentPacket); + if (serverAddress.proto == cricket::PROTO_TCP || serverAddress.proto == cricket::PROTO_TLS) { + socket->SignalConnect.connect(this, &ReflectorPort::OnSocketConnect); + socket->SubscribeCloseEvent(this, [this](rtc::AsyncPacketSocket* socket, const int error) { + OnSocketClose(socket, error); + }); + } else { + state = STATE_CONNECTED; + } + return true; + } + + // ReSharper disable once CppParameterMayBeConstPtrOrRef + void ReflectorPort::OnSocketConnect(rtc::AsyncPacketSocket* socket) { + RTC_DCHECK(serverAddress.proto == cricket::PROTO_TCP || serverAddress.proto == cricket::PROTO_TLS); + if (const rtc::SocketAddress& socket_address = socket->GetLocalAddress(); absl::c_none_of(Network()->GetIPs(), [socket_address](const rtc::InterfaceAddress& addr) { + return socket_address.ipaddr() == addr; + })) { + if (socket->GetLocalAddress().IsLoopbackIP()) { + RTC_LOG(LS_WARNING) << "Socket is bound to the address:" << socket_address.ipaddr().ToSensitiveString() + << ", rather than an address associated with network:" + << Network()->ToString() + << ". Still allowing it since it's localhost."; + } else if (IPIsAny(Network()->GetBestIP())) { + RTC_LOG(LS_WARNING) + << "Socket is bound to the address:" + << socket_address.ipaddr().ToSensitiveString() + << ", rather than an address associated with network:" + << Network()->ToString() + << ". Still allowing it since it's the 'any' address" + ", possibly caused by multiple_routes being disabled."; + } else { + RTC_LOG(LS_WARNING) << "Socket is bound to the address:" + << socket_address.ipaddr().ToSensitiveString() + << ", rather than an address associated with network:" + << Network()->ToString() << ". Discarding REFLECTOR port."; + OnAllocateError( + cricket::STUN_ERROR_GLOBAL_FAILURE, + "Address not associated with the desired network interface." + ); + return; + } + } + state = STATE_CONNECTED; + if (serverAddress.address.IsUnresolvedIP()) { + serverAddress.address = socket->GetRemoteAddress(); + } + RTC_LOG(LS_INFO) << "ReflectorPort connected to " << socket->GetRemoteAddress().ToSensitiveString() << " using tcp."; + } + + void ReflectorPort::OnSocketClose(rtc::AsyncPacketSocket* socket, const int error) { + RTC_LOG(LS_WARNING) << ToString() << ": Connection with server failed with error: " << error; + RTC_DCHECK(socket == this->socket); + Close(); + } + + cricket::Connection* ReflectorPort::CreateConnection(const cricket::Candidate& remote_candidate, CandidateOrigin origin) { + if (!SupportsProtocol(remote_candidate.protocol())) { + return nullptr; + } + const auto remoteHostname = remote_candidate.address().hostname(); + if (remoteHostname.empty()) { + return nullptr; + } + const auto ipFormat = "reflector-" + std::to_string(static_cast(serverId)) + "-"; + if (!absl::StartsWith(remoteHostname, ipFormat) || !absl::EndsWith(remoteHostname, ".reflector")) { + return nullptr; + } + if (remote_candidate.address().port() != serverAddress.address.port()) { + return nullptr; + } + if (state == STATE_DISCONNECTED || state == STATE_RECEIVEONLY) { + return nullptr; + } + auto* conn = new cricket::ProxyConnection(NewWeakPtr(), 0, remote_candidate); + AddOrReplaceConnection(conn); + return conn; + } + + bool ReflectorPort::FailAndPruneConnection(const rtc::SocketAddress& address) { + if (cricket::Connection* conn = GetConnection(address); conn != nullptr) { + conn->FailAndPrune(); + return true; + } + return false; + } + + int ReflectorPort::SetOption(const rtc::Socket::Option opt, int value) { + if (opt == rtc::Socket::OPT_DSCP) { + stunDscpValue = static_cast(value); + } + if (!socket) { + socketOptions[opt] = value; + return 0; + } + return socket->SetOption(opt, value); + } + + int ReflectorPort::GetOption(const rtc::Socket::Option opt, int* value) { + if (!socket) { + const auto it = socketOptions.find(opt); + if (it == socketOptions.end()) { + return -1; + } + *value = it->second; + return 0; + } + return socket->GetOption(opt, value); + } + + int ReflectorPort::GetError() { + return error; + } + + int ReflectorPort::SendTo(const void* data, size_t size, const rtc::SocketAddress& addr, const rtc::PacketOptions& options, bool payload) { + rtc::CopyOnWriteBuffer targetPeerTag; + auto syntheticHostname = addr.hostname(); + uint32_t resolvedPeerTag = 0; + if (auto resolvedPeerTagIt = resolvedPeerTagsByHostname.find(syntheticHostname); resolvedPeerTagIt != resolvedPeerTagsByHostname.end()) { + resolvedPeerTag = resolvedPeerTagIt->second; + } else { + const auto prefixFormat = "reflector-" + std::to_string(static_cast(serverId)) + "-"; + std::string suffixFormat = ".reflector"; + if (!absl::StartsWith(syntheticHostname, prefixFormat) || !absl::EndsWith(syntheticHostname, suffixFormat)) { + RTC_LOG(LS_ERROR) << ToString() << ": Discarding SendTo request with destination " << addr.ToString(); + return -1; + } + auto startPosition = prefixFormat.size(); + auto tagString = syntheticHostname.substr(startPosition, syntheticHostname.size() - suffixFormat.size() - startPosition); + std::stringstream tagStringStream(tagString); + tagStringStream >> resolvedPeerTag; + if (resolvedPeerTag == 0) { + RTC_LOG(LS_ERROR) << ToString() << ": Discarding SendTo request with destination " << addr.ToString() << " (could not parse peer tag)"; + return -1; + } + resolvedPeerTagsByHostname.insert(std::make_pair(syntheticHostname, resolvedPeerTag)); + } + targetPeerTag.AppendData(peerTag.data(), peerTag.size() - 4); + targetPeerTag.AppendData(reinterpret_cast(&resolvedPeerTag), 4); + + rtc::ByteBufferWriter bufferWriter; + bufferWriter.WriteBytes(targetPeerTag.data(), targetPeerTag.size()); + bufferWriter.WriteBytes(reinterpret_cast(&randomTag), 4); + + bufferWriter.WriteUInt32(static_cast(size)); + bufferWriter.WriteBytes(static_cast(data), size); + while (bufferWriter.Length() % 4 != 0) { + bufferWriter.WriteUInt8(0); + } + rtc::PacketOptions modified_options(options); + CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent); + modified_options.info_signaled_after_sent.turn_overhead_bytes = bufferWriter.Length() - size; + (void) Send(bufferWriter.Data(), bufferWriter.Length(), modified_options); + return static_cast(size); + } + + bool ReflectorPort::CanHandleIncomingPacketsFrom(const rtc::SocketAddress& addr) const { + return serverAddress.address == addr; + } + + bool ReflectorPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket, rtc::ReceivedPacket const &packet) { + if (socket != this->socket) { + return false; + } + uint8_t const *data = packet.payload().begin(); + size_t size = packet.payload().size(); + rtc::SocketAddress const &remote_addr = packet.source_address(); + auto packet_time_us = packet.arrival_time(); + + if (remote_addr != serverAddress.address) { + RTC_LOG(LS_WARNING) << ToString() + << ": Discarding REFLECTOR message from unknown address: " + << remote_addr.ToSensitiveString() + << " server_address_: " + << serverAddress.address.ToSensitiveString(); + return false; + } + if (size < 16) { + RTC_LOG(LS_WARNING) << ToString() + << ": Received REFLECTOR message that was too short (" << size << ")"; + return false; + } + if (state == STATE_DISCONNECTED) { + RTC_LOG(LS_WARNING) + << ToString() + << ": Received REFLECTOR message while the REFLECTOR port is disconnected"; + return false; + } + + uint8_t receivedPeerTag[16]; + memcpy(receivedPeerTag, data, 16); + + if (memcmp(receivedPeerTag, peerTag.data(), 16 - 4) != 0) { + RTC_LOG(LS_WARNING) + << ToString() + << ": Received REFLECTOR message with incorrect peer_tag"; + return false; + } + if (state != STATE_READY) { + state = STATE_READY; + + RTC_LOG(LS_INFO) << ToString() << ": REFLECTOR " << serverAddress.address.ToString() << " is now ready"; + + const auto ipFormat = "reflector-" + std::to_string(static_cast(serverId)) + "-" + std::to_string(randomTag) + ".reflector"; + rtc::SocketAddress candidateAddress(ipFormat, serverAddress.address.port()); + candidateAddress.SetResolvedIP(serverAddress.address.ipaddr()); + AddAddress( + candidateAddress, + serverAddress.address, + rtc::SocketAddress(), + cricket::UDP_PROTOCOL_NAME, + ProtoToString(serverAddress.proto), + "", + webrtc::IceCandidateType::kRelay, + GetRelayPreference(serverAddress.proto), + serverPriority, + ReconstructedServerUrl(false), + true + ); + } + + if (size > 16 + 4 + 4) { + bool isSpecialPacket = false; + if (size >= 16 + 12) { + uint8_t specialTag[12]; + memcpy(specialTag, data + 16, 12); + + uint8_t expectedSpecialTag[12]; + memset(expectedSpecialTag, 0xff, 12); + + if (memcmp(specialTag, expectedSpecialTag, 12) == 0) { + isSpecialPacket = true; + } + } + + if (!isSpecialPacket) { + uint32_t senderTag = 0; + memcpy(&senderTag, data + 16, 4); + + uint32_t dataSize = 0; + memcpy(&dataSize, data + 16 + 4, 4); + dataSize = be32toh(dataSize); + if (dataSize > size - 16 - 4 - 4) { + RTC_LOG(LS_WARNING) + << ToString() + << ": Received data packet with invalid size tag"; + } else { + const auto ipFormat = "reflector-" + std::to_string(static_cast(serverId)) + "-" + std::to_string(senderTag) + ".reflector"; + rtc::SocketAddress candidateAddress(ipFormat, serverAddress.address.port()); + candidateAddress.SetResolvedIP(serverAddress.address.ipaddr()); + int64_t packet_timestamp = -1; + if (packet_time_us.has_value()) { + packet_timestamp = packet_time_us->us_or(-1); + } + DispatchPacket(rtc::ReceivedPacket::CreateFromLegacy(data + 16 + 4 + 4, dataSize, packet_timestamp, candidateAddress)); + } + } + } + return true; + } + + void ReflectorPort::OnReadPacket(rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) { + HandleIncomingPacket(socket, packet); + } + + void ReflectorPort::OnSentPacket(rtc::AsyncPacketSocket* socket, const rtc::SentPacket& sent_packet) { + SignalSentPacket(sent_packet); + } + + void ReflectorPort::OnReadyToSend(rtc::AsyncPacketSocket*) { + if (ready()) { + Port::OnReadyToSend(); + } + } + + bool ReflectorPort::SupportsProtocol(const absl::string_view protocol) const { + return protocol == cricket::UDP_PROTOCOL_NAME; + } + + void ReflectorPort::ResolveTurnAddress(const rtc::SocketAddress& address) { + if (resolver) + return; + RTC_LOG(LS_INFO) << ToString() << ": Starting TURN host lookup for " << address.ToSensitiveString(); + resolver = socket_factory()->CreateAsyncDnsResolver(); + resolver->Start(address, [this] { + auto& result = resolver->result(); + if (result.GetError() != 0 && (serverAddress.proto == cricket::PROTO_TCP || serverAddress.proto == cricket::PROTO_TLS)) { + if (!CreateReflectorClientSocket()) { + OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR, "TURN host lookup received error."); + } + return; + } + rtc::SocketAddress resolved_address = serverAddress.address; + if (result.GetError() != 0 || !result.GetResolvedAddress(Network()->GetBestIP().family(), &resolved_address)) { + RTC_LOG(LS_WARNING) << ToString() << ": TURN host lookup received error " << result.GetError(); + error = result.GetError(); + OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR, "TURN host lookup received error."); + return; + } + SignalResolvedServerAddress(this, serverAddress.address, resolved_address); + serverAddress.address = resolved_address; + PrepareAddress(); + }); + } + + void ReflectorPort::OnSendStunPacket(const void* data, const size_t size, cricket::StunRequest* _) { + RTC_DCHECK(connected()); + rtc::PacketOptions options(StunDscpValue()); + options.info_signaled_after_sent.packet_type = rtc::PacketType::kTurnMessage; + CopyPortInformationToPacketInfo(&options.info_signaled_after_sent); + if (Send(data, size, options) < 0) { + RTC_LOG(LS_ERROR) << ToString() << ": Failed to send TURN message, error: " + << socket->GetError(); + } + } + + void ReflectorPort::OnAllocateError(const int error_code, const std::string& reason) { + thread()->PostTask(SafeTask(taskSafety.flag(), [this] { + SignalPortError(this); + })); + std::string address = GetLocalAddress().HostAsSensitiveURIString(); + int port = GetLocalAddress().port(); + if (serverAddress.proto == cricket::PROTO_TCP && serverAddress.address.IsPrivateIP()) { + address.clear(); + port = 0; + } + SignalCandidateError(this, cricket::IceCandidateErrorEvent(address, port, ReconstructedServerUrl(true), error_code, reason)); + } + + void ReflectorPort::Release() { + state = STATE_RECEIVEONLY; + } + + void ReflectorPort::Close() { + if (!ready()) { + OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR, ""); + } + state = STATE_DISCONNECTED; + for (auto [fst, snd] : connections()) { + snd->Destroy(); + } + SignalReflectorPortClosed(this); + } + + int ReflectorPort::GetRelayPreference(const cricket::ProtocolType proto) { + switch (proto) { + case cricket::PROTO_TCP: + return cricket::ICE_TYPE_PREFERENCE_RELAY_TCP; + case cricket::PROTO_TLS: + return cricket::ICE_TYPE_PREFERENCE_RELAY_TLS; + default: + RTC_DCHECK(proto == cricket::PROTO_UDP); + return cricket::ICE_TYPE_PREFERENCE_RELAY_UDP; + } + } + + rtc::DiffServCodePoint ReflectorPort::StunDscpValue() const { + return stunDscpValue; + } + + void ReflectorPort::DispatchPacket(const rtc::ReceivedPacket& packet) { + if (cricket::Connection* conn = GetConnection(packet.source_address())) { + conn->OnReadPacket(packet); + } else { + Port::OnReadPacket(packet, cricket::ProtocolType::PROTO_UDP); + } + } + + rtc::CopyOnWriteBuffer ReflectorPort::parseHex(std::string const &string) { + rtc::CopyOnWriteBuffer result; + for (size_t i = 0; i < string.length(); i += 2) { + std::string byteString = string.substr(i, 2); + char byte = static_cast(strtol(byteString.c_str(), nullptr, 16)); + result.AppendData(&byte, 1); + } + return result; + } + + int ReflectorPort::Send(const void* data, const size_t size, const rtc::PacketOptions& options) const { + return socket->SendTo(data, size, serverAddress.address, options); + } + + void ReflectorPort::HandleConnectionDestroyed(cricket::Connection* conn) {} + + std::string ReflectorPort::ReconstructedServerUrl(const bool useHostname) const { + std::string scheme = "turn"; + std::string transport = "tcp"; + switch (serverAddress.proto) { + case cricket::PROTO_SSLTCP: + case cricket::PROTO_TLS: + scheme = "turns"; + break; + case cricket::PROTO_UDP: + transport = "udp"; + break; + case cricket::PROTO_TCP: + break; + } + rtc::StringBuilder url; + url << scheme << ":" + << (useHostname ? serverAddress.address.hostname() : serverAddress.address.ipaddr().ToString()) + << ":" << serverAddress.address.port() << "?transport=" << transport; + return url.Release(); + } +} // namespace cricket \ No newline at end of file diff --git a/wrtc/interfaces/reflector_port.hpp b/wrtc/interfaces/reflector_port.hpp new file mode 100644 index 00000000..c6e40223 --- /dev/null +++ b/wrtc/interfaces/reflector_port.hpp @@ -0,0 +1,126 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#pragma once +#include +#include +#include +#include +#include + +namespace wrtc { + + + class ReflectorPort final : public cricket::Port { + public: + enum PortState { + STATE_CONNECTING, + STATE_CONNECTED, + STATE_READY, + STATE_RECEIVEONLY, + STATE_DISCONNECTED, + }; + + bool ready() const; + + bool connected() const; + + ~ReflectorPort() override; + + static std::unique_ptr Create(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* socket, uint8_t serverId, int serverPriority); + + static std::unique_ptr Create(const cricket::CreateRelayPortArgs& args, uint16_t minPort, uint16_t maxPort, uint8_t serverId, int serverPriority); + + rtc::SocketAddress GetLocalAddress() const; + + bool SupportsProtocol(absl::string_view protocol) const override; + + void PrepareAddress() override; + + void OnReadyToSend(rtc::AsyncPacketSocket* socket); + + void OnReadPacket(rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet); + + cricket::Connection* CreateConnection(const cricket::Candidate& remote_candidate, CandidateOrigin origin) override; + + bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) override; + + int SetOption(rtc::Socket::Option opt, int value) override; + + int GetOption(rtc::Socket::Option opt, int* value) override; + + int GetError() override; + + cricket::ProtocolType GetProtocol() const override; + + int SendTo(const void* data, size_t size, const rtc::SocketAddress& addr, const rtc::PacketOptions& options, bool payload) override; + + void OnSentPacket(rtc::AsyncPacketSocket* socket, const rtc::SentPacket& sent_packet) override; + + bool CanHandleIncomingPacketsFrom(const rtc::SocketAddress& addr) const override; + + void Close(); + + static int GetRelayPreference(cricket::ProtocolType proto); + + void HandleConnectionDestroyed(cricket::Connection* conn) override; + + protected: + ReflectorPort(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* socket, uint8_t serverId, int serverPriority); + + ReflectorPort(const cricket::CreateRelayPortArgs& args, uint16_t min_port, uint16_t max_port, uint8_t serverId, int serverPriority); + + rtc::DiffServCodePoint StunDscpValue() const override; + + private: + typedef std::map SocketOptionsMap; + typedef std::set AttemptedServerSet; + + rtc::CopyOnWriteBuffer peerTag; + uint32_t randomTag = 0; + cricket::ProtocolAddress serverAddress; + uint8_t serverId = 0; + webrtc::ScopedTaskSafety taskSafety; + rtc::AsyncPacketSocket* socket; + SocketOptionsMap socketOptions; + std::unique_ptr resolver; + int error; + sigslot::signal1 SignalReflectorPortClosed; + sigslot::signal3 SignalResolvedServerAddress; + PortState state; + AttemptedServerSet attemptedServerAddresses; + bool isRunningPingTask = false; + rtc::DiffServCodePoint stunDscpValue; + std::map resolvedPeerTagsByHostname; + cricket::RelayCredentials credentials; + int serverPriority; + + void OnAllocateError(int error_code, const std::string& reason); + + std::string ReconstructedServerUrl(bool useHostname) const; + + void ResolveTurnAddress(const rtc::SocketAddress& address); + + void OnSendStunPacket(const void *data, size_t size, cricket::StunRequest *_); + + int Send(const void* data, size_t size, const rtc::PacketOptions& options) const; + + bool CreateReflectorClientSocket(); + + void OnSocketConnect(rtc::AsyncPacketSocket* socket); + + void OnSocketClose(rtc::AsyncPacketSocket* socket, int error); + + void Release(); + + void SendReflectorHello(); + + void DispatchPacket(const rtc::ReceivedPacket& packet); + + static rtc::CopyOnWriteBuffer parseHex(std::string const &string); + + bool FailAndPruneConnection(const rtc::SocketAddress& address); + }; + +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/reflector_relay_port_factory.cpp b/wrtc/interfaces/reflector_relay_port_factory.cpp new file mode 100644 index 00000000..8679f17a --- /dev/null +++ b/wrtc/interfaces/reflector_relay_port_factory.cpp @@ -0,0 +1,66 @@ +// +// Created by Laky64 on 29/03/2024. +// +#include + +#include "reflector_relay_port_factory.hpp" + +#include "reflector_port.hpp" + +namespace wrtc { + ReflectorRelayPortFactory::ReflectorRelayPortFactory(const std::vector& servers): servers(servers) {} + + std::unique_ptr ReflectorRelayPortFactory::Create(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* udp_socket) { + if (args.config->credentials.username == "reflector") { + uint8_t foundId = 0; + for (const auto & [id, host, port, login, password, isTurn, isTcp] : servers) { + if (rtc::SocketAddress serverAddress(host, port); args.server_address->address == serverAddress) { + foundId = id; + break; + } + } + if (foundId == 0) { + return nullptr; + } + auto port = ReflectorPort::Create(args, udp_socket, foundId, args.relative_priority); + if (!port) { + return nullptr; + } + return port; + } + auto port = cricket::TurnPort::Create(args, udp_socket); + if (!port) { + return nullptr; + } + port->SetTlsCertPolicy(args.config->tls_cert_policy); + port->SetTurnLoggingId(args.config->turn_logging_id); + return port; + } + + std::unique_ptr ReflectorRelayPortFactory::Create(const cricket::CreateRelayPortArgs& args, const int min_port, const int max_port) { + if (args.config->credentials.username == "reflector") { + uint8_t foundId = 0; + for (const auto & [id, host, port, login, password, isTurn, isTcp] : servers) { + if (rtc::SocketAddress serverAddress(host, port); args.server_address->address == serverAddress) { + foundId = id; + break; + } + } + if (foundId == 0) { + return nullptr; + } + auto port = ReflectorPort::Create(args, min_port, max_port, foundId, args.relative_priority); + if (!port) { + return nullptr; + } + return port; + } + auto port = cricket::TurnPort::Create(args, min_port, max_port); + if (!port) { + return nullptr; + } + port->SetTlsCertPolicy(args.config->tls_cert_policy); + port->SetTurnLoggingId(args.config->turn_logging_id); + return port; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/reflector_relay_port_factory.hpp b/wrtc/interfaces/reflector_relay_port_factory.hpp new file mode 100644 index 00000000..f6dd28f5 --- /dev/null +++ b/wrtc/interfaces/reflector_relay_port_factory.hpp @@ -0,0 +1,25 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#pragma once + +#include + +#include "../models/rtc_server.hpp" + +namespace wrtc { + + class ReflectorRelayPortFactory final : public cricket::RelayPortFactoryInterface { + std::vector servers; + public: + explicit ReflectorRelayPortFactory(const std::vector& servers); + + ~ReflectorRelayPortFactory() override = default; + + std::unique_ptr Create(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* udp_socket) override; + + std::unique_ptr Create(const cricket::CreateRelayPortArgs& args, int min_port, int max_port) override; + }; + +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp new file mode 100644 index 00000000..850bca34 --- /dev/null +++ b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp @@ -0,0 +1,130 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#include "sctp_data_channel_provider_interface_impl.hpp" + +#include +#include +#include + +namespace wrtc { + SctpDataChannelProviderInterfaceImpl::SctpDataChannelProviderInterfaceImpl( + const webrtc::Environment& env, + rtc::PacketTransportInternal* transportChannel, + const bool isOutgoing, + rtc::Thread* networkThread, + rtc::Thread* signalingThread + ): weakFactory(this), networkThread(networkThread) { + assert(networkThread->IsCurrent()); + sctpTransportFactory = std::make_unique(networkThread); + sctpTransport = sctpTransportFactory->CreateSctpTransport(env, transportChannel); + sctpTransport->SetDataChannelSink(this); + + webrtc::InternalDataChannelInit dataChannelInit; + dataChannelInit.id = 0; + dataChannelInit.open_handshake_role = isOutgoing ? webrtc::InternalDataChannelInit::kOpener : webrtc::InternalDataChannelInit::kAcker; + dataChannel = webrtc::SctpDataChannel::Create( + weakFactory.GetWeakPtr(), + "data", + true, + dataChannelInit, + signalingThread, + networkThread + ); + if (dataChannel == nullptr) { + return; + } + dataChannel->RegisterObserver(this); + AddSctpDataStream(webrtc::StreamId(0)); + } + + SctpDataChannelProviderInterfaceImpl::~SctpDataChannelProviderInterfaceImpl() { + assert(networkThread->IsCurrent()); + weakFactory.InvalidateWeakPtrs(); + dataChannel->UnregisterObserver(); + dataChannel->Close(); + dataChannel = nullptr; + sctpTransport = nullptr; + sctpTransportFactory = nullptr; + } + + bool SctpDataChannelProviderInterfaceImpl::IsOkToCallOnTheNetworkThread() { + return true; + } + + void SctpDataChannelProviderInterfaceImpl::OnDataReceived(int channel_id, const webrtc::DataMessageType type, const rtc::CopyOnWriteBuffer& buffer) { + std::cout << "OnDataReceived" << std::endl; + assert(networkThread->IsCurrent()); + dataChannel->OnDataReceived(type, buffer); + } + + void SctpDataChannelProviderInterfaceImpl::OnReadyToSend() { + std::cout << "OnReadyToSend" << std::endl; + assert(networkThread->IsCurrent()); + dataChannel->OnTransportReady(); + } + + void SctpDataChannelProviderInterfaceImpl::OnStateChange() { + std::cout << "OnStateChange" << std::endl; + assert(networkThread->IsCurrent()); + const auto state = dataChannel->state(); + if (const bool isDataChannelOpen = state == webrtc::DataChannelInterface::DataState::kOpen; isOpen != isDataChannelOpen) { + isOpen = isDataChannelOpen; + (void) onStateChangedCallback(isDataChannelOpen); + } + } + + void SctpDataChannelProviderInterfaceImpl::OnMessage(const webrtc::DataBuffer& buffer) { + std::cout << "OnMessage" << std::endl; + assert(networkThread->IsCurrent()); + } + + webrtc::RTCError SctpDataChannelProviderInterfaceImpl::SendData(const webrtc::StreamId sid, const webrtc::SendDataParams& params, const rtc::CopyOnWriteBuffer& payload) { + std::cout << "SendData" << std::endl; + assert(networkThread->IsCurrent()); + return sctpTransport->SendData(sid.stream_id_int(), params, payload); + } + + void SctpDataChannelProviderInterfaceImpl::AddSctpDataStream(const webrtc::StreamId sid) { + std::cout << "AddSctpDataStream" << std::endl; + assert(networkThread->IsCurrent()); + sctpTransport->OpenStream(sid.stream_id_int()); + } + + void SctpDataChannelProviderInterfaceImpl::RemoveSctpDataStream(webrtc::StreamId sid) { + std::cout << "RemoveSctpDataStream" << std::endl; + assert(networkThread->IsCurrent()); + networkThread->BlockingCall([this, sid] { + sctpTransport->ResetStream(sid.stream_id_int()); + }); + } + + void SctpDataChannelProviderInterfaceImpl::updateIsConnected(const bool isConnected) { + std::cout << "updateIsConnected" << std::endl; + assert(networkThread->IsCurrent()); + if (isConnected) { + if (!isSctpTransportStarted) { + isSctpTransportStarted = true; + std::cout << "isSctpTransportStarted" << std::endl; + sctpTransport->Start(5000, 5000, 262144); + } + } + } + + void SctpDataChannelProviderInterfaceImpl::sendDataChannelMessage(const bytes::binary& data) const { + std::cout << "sendDataChannelMessage" << std::endl; + if (isOpen) { + const std::string message = bytes::to_string(data); + RTC_LOG(LS_INFO) << "Outgoing DataChannel message: " << message; + const webrtc::DataBuffer buffer(message); + dataChannel->Send(buffer); + } else { + RTC_LOG(LS_INFO) << "Could not send an outgoing DataChannel message: the channel is not open"; + } + } + + void SctpDataChannelProviderInterfaceImpl::onStateChanged(const std::function& callback) { + onStateChangedCallback = callback; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp new file mode 100644 index 00000000..019d3e2a --- /dev/null +++ b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp @@ -0,0 +1,85 @@ +// +// Created by Laky64 on 29/03/2024. +// + +#pragma once +#include +#include +#include +#include +#include + +#include "wrtc/utils/binary.hpp" +#include "wrtc/utils/syncronized_callback.hpp" + +namespace wrtc { + + class SctpDataChannelProviderInterfaceImpl final : public sigslot::has_slots<>, public webrtc::SctpDataChannelControllerInterface, public webrtc::DataChannelObserver, public webrtc::DataChannelSink { + rtc::WeakPtrFactory weakFactory; + std::unique_ptr sctpTransportFactory; + std::unique_ptr sctpTransport; + rtc::scoped_refptr dataChannel; + rtc::Thread* networkThread; + bool isOpen = false; + bool isSctpTransportStarted = false; + + synchronized_callback onStateChangedCallback; + + public: + SctpDataChannelProviderInterfaceImpl( + const webrtc::Environment& env, + rtc::PacketTransportInternal* transportChannel, + bool isOutgoing, + rtc::Thread* networkThread, + rtc::Thread* signalingThread + ); + + ~SctpDataChannelProviderInterfaceImpl() override; + + bool IsOkToCallOnTheNetworkThread() override; + + void OnDataReceived(int channel_id, webrtc::DataMessageType type, const rtc::CopyOnWriteBuffer& buffer) override; + + void OnReadyToSend() override; + + void OnStateChange() override; + + void OnMessage(const webrtc::DataBuffer& buffer) override; + + webrtc::RTCError SendData(webrtc::StreamId sid, const webrtc::SendDataParams& params, const rtc::CopyOnWriteBuffer& payload) override; + + void AddSctpDataStream(webrtc::StreamId sid) override; + + void RemoveSctpDataStream(webrtc::StreamId sid) override; + + void updateIsConnected(bool isConnected); + + void sendDataChannelMessage(const bytes::binary& data) const; + + // Unused + void OnChannelClosing(int channel_id) override + { + std::cout << "OnChannelClosing" << std::endl; + } + void OnChannelClosed(int channel_id) override + { + std::cout << "OnChannelClosed" << std::endl; + } + void OnChannelStateChanged(webrtc::SctpDataChannel* data_channel, webrtc::DataChannelInterface::DataState state) override + { + std::cout << "OnChannelStateChanged" << std::endl; + } + void OnTransportClosed(webrtc::RTCError error) override + { + std::cout << "OnTransportClosed" << std::endl; + }; + + void onStateChanged(const std::function& callback); + + void OnBufferedAmountLow(int channel_id) override {} + size_t buffered_amount(webrtc::StreamId sid) const override { return 0; } + size_t buffered_amount_low_threshold(webrtc::StreamId sid) const override { return 0;} + void SetBufferedAmountLowThreshold(webrtc::StreamId sid, size_t bytes) override {} + }; + +} // wrtc \ No newline at end of file diff --git a/wrtc/models/content_negotiation_context.cpp b/wrtc/models/content_negotiation_context.cpp new file mode 100644 index 00000000..d5710602 --- /dev/null +++ b/wrtc/models/content_negotiation_context.cpp @@ -0,0 +1,642 @@ +// +// Created by Laky64 on 30/03/2024. +// + +#include "content_negotiation_context.hpp" + +#include +#include +#include +#include + +#include "wrtc/exceptions.hpp" + +namespace wrtc { + ContentNegotiationContext::ContentNegotiationContext( + const webrtc::FieldTrialsView& fieldTrials, + const bool isOutgoing, + cricket::MediaEngineInterface *mediaEngine, + rtc::UniqueRandomIdGenerator *uniqueRandomIdGenerator + ) :isOutgoing(isOutgoing), uniqueRandomIdGenerator(uniqueRandomIdGenerator) { + transportDescriptionFactory = std::make_unique(fieldTrials); + const auto tempCertificate = rtc::RTCCertificateGenerator::GenerateCertificate( + rtc::KeyParams(rtc::KT_ECDSA), + absl::nullopt + ); + transportDescriptionFactory->set_certificate(tempCertificate); + sessionDescriptionFactory = std::make_unique( + mediaEngine, + true, + uniqueRandomIdGenerator, + transportDescriptionFactory.get() + ); + needNegotiation = true; + } + + void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngineInterface *mediaEngine, const bool randomize) { + std::vector audioSendCodecs = mediaEngine->voice().send_codecs(); + std::vector audioRecvCodecs = mediaEngine->voice().recv_codecs(); + std::vector videoSendCodecs = mediaEngine->video().send_codecs(); + std::vector videoRecvCodecs = mediaEngine->video().recv_codecs(); + for (const auto &codec : audioSendCodecs) { + if (codec.name == "opus") { + audioSendCodecs = { codec }; + audioRecvCodecs = { codec }; + break; + } + } + if (randomize) { + for (auto &codec : audioSendCodecs) { + codec.id += 3; + } + for (auto &codec : videoSendCodecs) { + codec.id += 3; + } + for (auto &codec : audioRecvCodecs) { + codec.id += 3; + } + for (auto &codec : videoRecvCodecs) { + codec.id += 3; + } + } + sessionDescriptionFactory->set_audio_codecs(audioSendCodecs, audioRecvCodecs); + sessionDescriptionFactory->set_video_codecs(videoSendCodecs, videoRecvCodecs); + int absSendTimeUriId = 2; + int transportSequenceNumberUriId = 3; + int videoRotationUri = 13; + + if (randomize) { + absSendTimeUriId = 3; + transportSequenceNumberUriId = 2; + videoRotationUri = 4; + } + rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId); + rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId); + rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId); + rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId); + rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kVideoRotationUri, videoRotationUri); + } + + std::string ContentNegotiationContext::addOutgoingChannel(const webrtc::MediaStreamTrackInterface* track) { + std::string channelId = track->id(); + cricket::MediaType mappedMediaType; + std::vector rtpExtensions; + if (track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { + mappedMediaType = cricket::MediaType::MEDIA_TYPE_AUDIO; + rtpExtensions = rtpAudioExtensions; + } else { + mappedMediaType = cricket::MediaType::MEDIA_TYPE_VIDEO; + rtpExtensions = rtpVideoExtensions; + } + cricket::MediaDescriptionOptions offerDescription(mappedMediaType, channelId, webrtc::RtpTransceiverDirection::kSendOnly, false); + offerDescription.header_extensions = rtpExtensions; + if (track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { + offerDescription.AddAudioSender(channelId, {channelId}); + } else { + const cricket::SimulcastLayerList simulcastLayers; + offerDescription.AddVideoSender(channelId, {channelId}, {}, simulcastLayers, 1); + } + outgoingChannelDescriptions.emplace_back(std::move(offerDescription)); + needNegotiation = true; + return channelId; + } + + std::unique_ptr ContentNegotiationContext::getPendingOffer() { + if (!needNegotiation) { + return nullptr; + } + if (pendingOutgoingOffer) { + return nullptr; + } + needNegotiation = false; + pendingOutgoingOffer = std::make_unique(); + pendingOutgoingOffer->exchangeId = uniqueRandomIdGenerator->GenerateId(); + auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState(); + cricket::MediaSessionOptions offerOptions; + offerOptions.offer_extmap_allow_mixed = true; + offerOptions.bundle_enabled = true; + for (const auto &id : channelIdOrder) { + bool found = false; + for (const auto &channel : outgoingChannelDescriptions) { + if (channel.description.mid == id) { + found = true; + offerOptions.media_description_options.push_back(channel.description); + + break; + } + } + for (const auto &content : incomingChannels) { + if (std::to_string(content.ssrc) == id) { + found = true; + offerOptions.media_description_options.push_back(getIncomingContentDescription(content)); + break; + } + } + if (!found) { + cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false); + offerOptions.media_description_options.push_back(contentDescription); + } + } + for (const auto &channel : outgoingChannelDescriptions) { + if (std::ranges::find(channelIdOrder, channel.description.mid) == channelIdOrder.end()) { + channelIdOrder.push_back(channel.description.mid); + offerOptions.media_description_options.push_back(channel.description); + } + for (const auto &content : incomingChannels) { + if (std::ranges::find(channelIdOrder, std::to_string(content.ssrc)) == channelIdOrder.end()) { + channelIdOrder.push_back(std::to_string(content.ssrc)); + offerOptions.media_description_options.push_back(getIncomingContentDescription(content)); + } + } + } + auto offerOrError = sessionDescriptionFactory->CreateOfferOrError( + offerOptions, currentSessionDescription.get() + ); + if (!offerOrError.ok()) { + RTC_LOG(LS_ERROR) << "Failed to create offer: " << offerOrError.error().message(); + wrapRTCError(offerOrError.error()); + } + auto offer = offerOrError.MoveValue(); + auto mappedOffer = std::make_unique(); + mappedOffer->exchangeId = pendingOutgoingOffer->exchangeId; + for (const auto &content : offer->contents()) { + auto mappedContent = convertContentInfoToSingalingContent(content); + if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kSendOnly) { + mappedOffer->contents.push_back(std::move(mappedContent)); + for (auto &channel : outgoingChannelDescriptions) { + if (channel.description.mid == content.mid()) { + channel.ssrc = mappedContent.ssrc; + channel.ssrcGroups = mappedContent.ssrcGroups; + } + } + } + } + return mappedOffer; + } + + std::unique_ptr ContentNegotiationContext::setPendingAnwer(std::unique_ptr&& answer) { + if (!answer) { + return nullptr; + } + if (pendingOutgoingOffer) { + if (answer->exchangeId == pendingOutgoingOffer->exchangeId) { + setAnswer(std::move(answer)); + return nullptr; + } + if (!isOutgoing) { + pendingOutgoingOffer.reset(); + return getAnswer(std::move(answer)); + } + return nullptr; + } + return getAnswer(std::move(answer)); + } + + std::unique_ptr ContentNegotiationContext::coordinatedState() const { + auto result = std::make_unique(); + result->incomingContents = incomingChannels; + for (const auto &channel : outgoingChannels) { + bool found = false; + for (const auto &channelDescription : outgoingChannelDescriptions) { + if (channelDescription.description.mid == channel.id) { + found = true; + break; + } + } + if (found) { + result->outgoingContents.push_back(channel.content); + } + } + return result; + } + + std::optional ContentNegotiationContext::outgoingChannelSsrc(const std::string &id) const { + for (const auto &channel : outgoingChannels) { + bool found = false; + for (const auto &channelDescription : outgoingChannelDescriptions) { + if (channelDescription.description.mid == channel.id) { + found = true; + break; + } + } + if (found && channel.id == id) { + if (channel.content.ssrc != 0) { + return channel.content.ssrc; + } + } + } + return std::nullopt; + } + + std::unique_ptr ContentNegotiationContext::currentSessionDescriptionFromCoordinatedState() const { + if (channelIdOrder.empty()) { + return nullptr; + } + auto sessionDescription = std::make_unique(); + for (const auto &id : channelIdOrder) { + bool found = false; + for (const auto &channel : incomingChannels) { + if (std::to_string(channel.ssrc) == id) { + found = true; + auto mappedContent = convertSignalingContentToContentInfo(std::to_string(channel.ssrc), channel, webrtc::RtpTransceiverDirection::kRecvOnly); + auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); + std::unique_ptr fingerprint; + if (localCertificate) { + fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get()); + } + std::vector transportOptions; + cricket::TransportDescription transportDescription( + transportOptions, + "ufrag", + "pwd", + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_ACTPASS, + fingerprint.get() + ); + cricket::TransportInfo transportInfo(std::to_string(channel.ssrc), transportDescription); + sessionDescription->AddTransportInfo(transportInfo); + sessionDescription->AddContent(std::move(mappedContent)); + break; + } + } + for (const auto &channel : outgoingChannels) { + if (channel.id == id) { + found = true; + auto mappedContent = convertSignalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kSendOnly); + auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); + std::unique_ptr fingerprint; + if (localCertificate) { + fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get()); + } + std::vector transportOptions; + cricket::TransportDescription transportDescription( + transportOptions, + "ufrag", + "pwd", + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_ACTPASS, + fingerprint.get() + ); + cricket::TransportInfo transportInfo(mappedContent.name, transportDescription); + sessionDescription->AddTransportInfo(transportInfo); + sessionDescription->AddContent(std::move(mappedContent)); + break; + } + } + + if (!found) { + auto mappedContent = createInactiveContentInfo("_" + id); + auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); + std::unique_ptr fingerprint; + if (localCertificate) { + fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get()); + } + std::vector transportOptions; + cricket::TransportDescription transportDescription( + transportOptions, + "ufrag", + "pwd", + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_ACTPASS, + fingerprint.get() + ); + cricket::TransportInfo transportInfo(mappedContent.name, transportDescription); + sessionDescription->AddTransportInfo(transportInfo); + sessionDescription->AddContent(std::move(mappedContent)); + } + } + return sessionDescription; + } + + cricket::ContentInfo ContentNegotiationContext::convertSignalingContentToContentInfo(const std::string &contentId, const MediaContent &content, webrtc::RtpTransceiverDirection direction) { + std::unique_ptr contentDescription; + switch (content.type) { + case MediaContent::Type::Audio: { + auto audioDescription = std::make_unique(); + for (const auto &[id, name, clockrate, channels, feedbackTypes, parameters] : content.payloadTypes) { + auto mappedCodec = cricket::CreateAudioCodec( + static_cast(id), + name, + static_cast(clockrate), + channels + ); + for (const auto ¶meter : parameters) { + mappedCodec.params.insert(parameter); + } + for (const auto &[type, subtype] : feedbackTypes) { + mappedCodec.AddFeedbackParam(cricket::FeedbackParam(type, subtype)); + } + audioDescription->AddCodec(mappedCodec); + } + contentDescription = std::move(audioDescription); + break; + } + case MediaContent::Type::Video: { + auto videoDescription = std::make_unique(); + for (const auto &[id, name, clockrate, channels, feedbackTypes, parameters] : content.payloadTypes) { + webrtc::SdpVideoFormat videoFormat(name); + for (const auto ¶meter : parameters) { + videoFormat.parameters.insert(parameter); + } + cricket::Codec mappedCodec = cricket::CreateVideoCodec(videoFormat); + mappedCodec.id = static_cast(id); + for (const auto ¶meter : parameters) { + mappedCodec.params.insert(parameter); + } + for (const auto &[type, subtype] : feedbackTypes) { + mappedCodec.AddFeedbackParam(cricket::FeedbackParam(type, subtype)); + } + videoDescription->AddCodec(mappedCodec); + } + contentDescription = std::move(videoDescription); + break; + } + default: { + throw RTCException("Unknown media type"); + } + } + cricket::StreamParams streamParams; + streamParams.id = contentId; + streamParams.set_stream_ids({ contentId }); + streamParams.add_ssrc(content.ssrc); + for (const auto &[ssrcs, semantics] : content.ssrcGroups) { + streamParams.ssrc_groups.emplace_back(semantics, ssrcs); + for (const auto &ssrc : ssrcs) { + if (!streamParams.has_ssrc(ssrc)) { + streamParams.add_ssrc(ssrc); + } + } + } + contentDescription->AddStream(streamParams); + for (const auto &extension : content.rtpExtensions) { + contentDescription->AddRtpHeaderExtension(extension); + } + contentDescription->set_direction(direction); + contentDescription->set_rtcp_mux(true); + cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp); + mappedContentInfo.name = contentId; + mappedContentInfo.rejected = false; + mappedContentInfo.bundle_only = false; + mappedContentInfo.set_media_description(std::move(contentDescription)); + return mappedContentInfo; + } + + MediaContent ContentNegotiationContext::convertContentInfoToSingalingContent(const cricket::ContentInfo &content) { + MediaContent mappedContent; + switch (content.media_description()->type()) { + case cricket::MediaType::MEDIA_TYPE_AUDIO: + mappedContent.type = MediaContent::Type::Audio; + for (const auto &codec : content.media_description()->as_audio()->codecs()) { + PayloadType mappedPayloadType; + mappedPayloadType.id = codec.id; + mappedPayloadType.name = codec.name; + mappedPayloadType.clockrate = codec.clockrate; + mappedPayloadType.channels = static_cast(codec.channels); + for (const auto &feedbackType : codec.feedback_params.params()) { + FeedbackType mappedFeedbackType; + mappedFeedbackType.type = feedbackType.id(); + mappedFeedbackType.subtype = feedbackType.param(); + mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType)); + } + for (const auto &[fst, snd] : codec.params) { + mappedPayloadType.parameters.emplace_back(fst, snd); + } + std::ranges::sort(mappedPayloadType.parameters, [](std::pair const &lhs, std::pair const &rhs) -> bool { + return lhs.first < rhs.first; + }); + mappedContent.payloadTypes.push_back(std::move(mappedPayloadType)); + } + break; + case cricket::MediaType::MEDIA_TYPE_VIDEO: + mappedContent.type = MediaContent::Type::Video; + for (const auto &codec : content.media_description()->as_video()->codecs()) { + PayloadType mappedPayloadType; + mappedPayloadType.id = codec.id; + mappedPayloadType.name = codec.name; + mappedPayloadType.clockrate = codec.clockrate; + mappedPayloadType.channels = 0; + for (const auto &feedbackType : codec.feedback_params.params()) { + FeedbackType mappedFeedbackType; + mappedFeedbackType.type = feedbackType.id(); + mappedFeedbackType.subtype = feedbackType.param(); + mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType)); + } + for (const auto &[fst, snd] : codec.params) { + mappedPayloadType.parameters.emplace_back(fst, snd); + } + std::ranges::sort(mappedPayloadType.parameters, [](std::pair const &lhs, std::pair const &rhs) -> bool { + return lhs.first < rhs.first; + }); + mappedContent.payloadTypes.push_back(std::move(mappedPayloadType)); + } + break; + default: + throw RTCException("Unknown media type"); + } + if (!content.media_description()->streams().empty()) { + mappedContent.ssrc = content.media_description()->streams()[0].first_ssrc(); + for (const auto &ssrcGroup : content.media_description()->streams()[0].ssrc_groups) { + SsrcGroup mappedSsrcGroup; + mappedSsrcGroup.semantics = ssrcGroup.semantics; + mappedSsrcGroup.ssrcs = ssrcGroup.ssrcs; + mappedContent.ssrcGroups.push_back(std::move(mappedSsrcGroup)); + } + } + for (const auto &extension : content.media_description()->rtp_header_extensions()) { + mappedContent.rtpExtensions.push_back(extension); + } + return mappedContent; + } + + cricket::ContentInfo ContentNegotiationContext::createInactiveContentInfo(std::string const &contentId) { + auto audioDescription = std::make_unique(); + auto contentDescription = std::move(audioDescription); + contentDescription->set_direction(webrtc::RtpTransceiverDirection::kInactive); + contentDescription->set_rtcp_mux(true); + cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp); + mappedContentInfo.name = contentId; + mappedContentInfo.rejected = false; + mappedContentInfo.bundle_only = false; + mappedContentInfo.set_media_description(std::move(contentDescription)); + return mappedContentInfo; + } + + cricket::MediaDescriptionOptions ContentNegotiationContext::getIncomingContentDescription(const MediaContent &content) { + auto mappedContent = convertSignalingContentToContentInfo(std::to_string(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly); + cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false); + for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) { + contentDescription.header_extensions.emplace_back(extension.uri, extension.id); + } + return contentDescription; + } + + void ContentNegotiationContext::setAnswer(std::unique_ptr &&answer) { + if (!pendingOutgoingOffer) { + return; + } + if (pendingOutgoingOffer->exchangeId != answer->exchangeId) { + return; + } + + pendingOutgoingOffer.reset(); + + outgoingChannels.clear(); + + for (const auto &content : answer->contents) { + for (const auto &pendingChannel : outgoingChannelDescriptions) { + if (pendingChannel.ssrc != 0 && content.ssrc == pendingChannel.ssrc) { + outgoingChannels.emplace_back(pendingChannel.description.mid, content); + break; + } + } + } + } + + std::unique_ptr ContentNegotiationContext::getAnswer(std::unique_ptr &&offer) { + auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState(); + auto mappedOffer = std::make_unique(); + cricket::MediaSessionOptions answerOptions; + answerOptions.offer_extmap_allow_mixed = true; + answerOptions.bundle_enabled = true; + for (const auto &id : channelIdOrder) { + bool found = false; + + for (const auto &channel : outgoingChannels) { + if (channel.id == id) { + found = true; + auto mappedContent = convertSignalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kRecvOnly); + cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kSendOnly, false); + for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) { + contentDescription.header_extensions.emplace_back(extension.uri, extension.id); + } + answerOptions.media_description_options.push_back(contentDescription); + auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); + std::unique_ptr fingerprint; + if (localCertificate) { + fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get()); + } + std::vector transportOptions; + cricket::TransportDescription transportDescription( + transportOptions, + "ufrag", + "pwd", + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_ACTPASS, + fingerprint.get() + ); + cricket::TransportInfo transportInfo(channel.id, transportDescription); + mappedOffer->AddTransportInfo(transportInfo); + mappedOffer->AddContent(std::move(mappedContent)); + break; + } + } + for (const auto &content : offer->contents) { + if (std::to_string(content.ssrc) == id) { + found = true; + auto mappedContent = convertSignalingContentToContentInfo(std::to_string(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly); + cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false); + for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) { + contentDescription.header_extensions.emplace_back(extension.uri, extension.id); + } + answerOptions.media_description_options.push_back(contentDescription); + auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); + std::unique_ptr fingerprint; + if (localCertificate) { + fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get()); + } + std::vector transportOptions; + cricket::TransportDescription transportDescription( + transportOptions, + "ufrag", + "pwd", + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_ACTPASS, + fingerprint.get() + ); + cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription); + mappedOffer->AddTransportInfo(transportInfo); + mappedOffer->AddContent(std::move(mappedContent)); + break; + } + } + if (!found) { + auto mappedContent = createInactiveContentInfo("_" + id); + cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false); + answerOptions.media_description_options.push_back(contentDescription); + auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); + std::unique_ptr fingerprint; + if (localCertificate) { + fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get()); + } + std::vector transportOptions; + cricket::TransportDescription transportDescription( + transportOptions, + "ufrag", + "pwd", + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_ACTPASS, + fingerprint.get() + ); + cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription); + mappedOffer->AddTransportInfo(transportInfo); + mappedOffer->AddContent(std::move(mappedContent)); + } + } + for (const auto &content : offer->contents) { + if (std::ranges::find(channelIdOrder, std::to_string(content.ssrc)) == channelIdOrder.end()) { + channelIdOrder.push_back(std::to_string(content.ssrc)); + answerOptions.media_description_options.push_back(getIncomingContentDescription(content)); + auto mappedContent = convertSignalingContentToContentInfo(std::to_string(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly); + auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); + std::unique_ptr fingerprint; + if (localCertificate) { + fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get()); + } + + std::vector transportOptions; + cricket::TransportDescription transportDescription( + transportOptions, + "ufrag", + "pwd", + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_ACTPASS, + fingerprint.get() + ); + cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription); + mappedOffer->AddTransportInfo(transportInfo); + + mappedOffer->AddContent(std::move(mappedContent)); + } + } + + auto answerOrError = sessionDescriptionFactory->CreateAnswerOrError(mappedOffer.get(), answerOptions, currentSessionDescription.get()); + if (!answerOrError.ok()) { + return nullptr; + } + auto answer = answerOrError.MoveValue(); + + auto mappedAnswer = std::make_unique(); + + mappedAnswer->exchangeId = offer->exchangeId; + + std::vector incomingChannels; + + for (const auto &content : answer->contents()) { + auto mappedContent = convertContentInfoToSingalingContent(content); + + if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kRecvOnly) { + for (const auto &[type, ssrc, ssrcGroups, payloadTypes, rtpExtensions] : offer->contents) { + if (std::to_string(ssrc) == content.mid()) { + mappedContent.ssrc = ssrc; + mappedContent.ssrcGroups = ssrcGroups; + break; + } + } + incomingChannels.push_back(mappedContent); + mappedAnswer->contents.push_back(std::move(mappedContent)); + } + } + this->incomingChannels = incomingChannels; + return mappedAnswer; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/models/content_negotiation_context.hpp b/wrtc/models/content_negotiation_context.hpp new file mode 100644 index 00000000..73c75087 --- /dev/null +++ b/wrtc/models/content_negotiation_context.hpp @@ -0,0 +1,96 @@ +// +// Created by Laky64 on 30/03/2024. +// + +#pragma once +#include +#include +#include + +#include + +#include "media_content.hpp" + +namespace wrtc { + + class ContentNegotiationContext { + public: + struct OutgoingChannel { + std::string id; + MediaContent content; + + OutgoingChannel(std::string id, MediaContent content):id(std::move(id)), content(std::move(content)) {} + }; + + struct PendingOutgoingChannel { + cricket::MediaDescriptionOptions description; + uint32_t ssrc = 0; + std::vector ssrcGroups; + explicit PendingOutgoingChannel(cricket::MediaDescriptionOptions &&description):description(std::move(description)) {} + }; + + struct NegotiationContents { + uint32_t exchangeId = 0; + std::vector contents; + }; + + struct PendingOutgoingOffer { + uint32_t exchangeId = 0; + }; + + + struct CoordinatedState { + std::vector outgoingContents; + std::vector incomingContents; + }; + + private: + bool isOutgoing, needNegotiation; + rtc::UniqueRandomIdGenerator* uniqueRandomIdGenerator; + std::unique_ptr transportDescriptionFactory; + std::unique_ptr sessionDescriptionFactory; + std::vector rtpAudioExtensions; + std::vector rtpVideoExtensions; + std::vector outgoingChannelDescriptions; + std::unique_ptr pendingOutgoingOffer; + std::vector channelIdOrder; + std::vector incomingChannels; + std::vector outgoingChannels; + int nextOutgoingChannelId = 0; + + [[nodiscard]] std::unique_ptr currentSessionDescriptionFromCoordinatedState() const; + + static cricket::ContentInfo convertSignalingContentToContentInfo(const std::string &contentId, const MediaContent &content, webrtc::RtpTransceiverDirection direction); + + static MediaContent convertContentInfoToSingalingContent(const cricket::ContentInfo &content); + + static cricket::ContentInfo createInactiveContentInfo(const std::string &contentId); + + static cricket::MediaDescriptionOptions getIncomingContentDescription(const MediaContent &content); + + void setAnswer(std::unique_ptr &&answer); + + std::unique_ptr getAnswer(std::unique_ptr &&offer); + + public: + ContentNegotiationContext( + const webrtc::FieldTrialsView& fieldTrials, + bool isOutgoing, + cricket::MediaEngineInterface *mediaEngine, + rtc::UniqueRandomIdGenerator *uniqueRandomIdGenerator + ); + + void copyCodecsFromChannelManager(cricket::MediaEngineInterface *mediaEngine, bool randomize); + + std::string addOutgoingChannel(const webrtc::MediaStreamTrackInterface* track); + + std::unique_ptr getPendingOffer(); + + std::unique_ptr setPendingAnwer(std::unique_ptr&& answer); + + [[nodiscard]] std::unique_ptr coordinatedState() const; + + [[nodiscard]] std::optional outgoingChannelSsrc(const std::string &id) const; + }; + +} // wrtc \ No newline at end of file diff --git a/wrtc/models/media_content.hpp b/wrtc/models/media_content.hpp new file mode 100644 index 00000000..1b2bc627 --- /dev/null +++ b/wrtc/models/media_content.hpp @@ -0,0 +1,117 @@ +// +// Created by Laky64 on 30/03/2024. +// + +#pragma once +#include +#include +#include +#include + +namespace wrtc { + struct SsrcGroup { + std::vector ssrcs; + std::string semantics; + + bool operator==(SsrcGroup const &rhs) const { + if (ssrcs != rhs.ssrcs) { + return false; + } + if (semantics != rhs.semantics) { + return false; + } + return true; + } + }; + + struct FeedbackType { + std::string type; + std::string subtype; + + bool operator==(FeedbackType const &rhs) const { + if (type != rhs.type) { + return false; + } + if (subtype != rhs.subtype) { + return false; + } + + return true; + } + }; + + struct PayloadType { + uint32_t id = 0; + std::string name; + uint32_t clockrate = 0; + uint32_t channels = 0; + std::vector feedbackTypes; + std::vector> parameters; + + bool operator==(PayloadType const &rhs) const { + if (id != rhs.id) { + return false; + } + if (name != rhs.name) { + return false; + } + if (clockrate != rhs.clockrate) { + return false; + } + if (channels != rhs.channels) { + return false; + } + if (feedbackTypes != rhs.feedbackTypes) { + return false; + } + if (parameters != rhs.parameters) { + return false; + } + + return true; + } + }; + + struct MediaContent { + enum class Type { + Audio = 1 << 0, + Video = 1 << 1 + }; + + Type type = Type::Audio; + uint32_t ssrc = 0; + std::vector ssrcGroups; + std::vector payloadTypes; + std::vector rtpExtensions; + + bool operator==(const MediaContent& rhs) const { + if (type != rhs.type) { + return false; + } + if (ssrc != rhs.ssrc) { + return false; + } + if (ssrcGroups != rhs.ssrcGroups) { + return false; + } + + std::vector sortedPayloadTypes = payloadTypes; + std::ranges::sort(sortedPayloadTypes, [](PayloadType const &lhs, PayloadType const &rhs) { + return lhs.id < rhs.id; + }); + std::vector sortedRhsPayloadTypes = rhs.payloadTypes; + std::ranges::sort(sortedRhsPayloadTypes, [](PayloadType const &lhs, PayloadType const &rhs) { + return lhs.id < rhs.id; + }); + if (sortedPayloadTypes != sortedRhsPayloadTypes) { + return false; + } + + if (rtpExtensions != rhs.rtpExtensions) { + return false; + } + + return true; + } + }; +} // wrtc \ No newline at end of file From df923145e27172e7d49df93f907d10afd3bd87db Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 00:38:57 +0200 Subject: [PATCH 19/48] Fixed missing Trials Field --- wrtc/interfaces/peer_connection/peer_connection_factory.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrtc/interfaces/peer_connection/peer_connection_factory.cpp b/wrtc/interfaces/peer_connection/peer_connection_factory.cpp index 19d27c60..00b3c90b 100644 --- a/wrtc/interfaces/peer_connection/peer_connection_factory.cpp +++ b/wrtc/interfaces/peer_connection/peer_connection_factory.cpp @@ -23,7 +23,9 @@ namespace wrtc { PeerConnectionFactory::PeerConnectionFactory() { webrtc::field_trial::InitFieldTrialsFromString( "WebRTC-DataChannel-Dcsctp/Enabled/" + "WebRTC-Audio-MinimizeResamplingOnMobile/Enabled/" "WebRTC-Audio-iOS-Holding/Enabled/" + "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/" ); network_thread_ = rtc::Thread::CreateWithSocketServer(); network_thread_->SetName("ntg-net", nullptr); From c9e43c140154b06d1a0c09fde5548775c225e462 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 00:45:29 +0200 Subject: [PATCH 20/48] Future support for calls with custom parameters --- ntgcalls/instances/p2p_call.cpp | 5 +- wrtc/CMakeLists.txt | 1 + wrtc/interfaces/native_connection.cpp | 48 +++++- wrtc/interfaces/native_connection.hpp | 6 + wrtc/interfaces/reflector_port.cpp | 152 +++++++++++------- wrtc/interfaces/reflector_port.hpp | 45 +++++- .../reflector_relay_port_factory.cpp | 11 +- .../reflector_relay_port_factory.hpp | 4 +- 8 files changed, 198 insertions(+), 74 deletions(-) diff --git a/ntgcalls/instances/p2p_call.cpp b/ntgcalls/instances/p2p_call.cpp index 418af22a..13061e8d 100644 --- a/ntgcalls/instances/p2p_call.cpp +++ b/ntgcalls/instances/p2p_call.cpp @@ -169,6 +169,7 @@ namespace ntgcalls { sendLocalDescription(); } else { sendInitialSetup(); + sendOfferIfNeeded(); sendMediaState(stream->getState()); } } @@ -194,9 +195,7 @@ namespace ntgcalls { } Safe(connection)->setRemoteParams(remoteIceParameters, std::move(fingerprint), sslSetup); handshakeCompleted = true; - if (type() == Type::Outgoing) { - sendOfferIfNeeded(); - } else { + if (type() == Type::Incoming) { sendInitialSetup(); } applyPendingIceCandidates(); diff --git a/wrtc/CMakeLists.txt b/wrtc/CMakeLists.txt index 5259452f..d3f4c404 100644 --- a/wrtc/CMakeLists.txt +++ b/wrtc/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(wrtc STATIC ${MODULE_SRC}) set_property(TARGET wrtc PROPERTY CXX_STANDARD 20) target_link_libraries(wrtc PUBLIC WebRTC::webrtc) +target_link_libraries(wrtc PRIVATE nlohmann_json::nlohmann_json) target_include_directories(wrtc PUBLIC ${CMAKE_SOURCE_DIR} diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index 3b9a5c9f..fe52f832 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -66,7 +66,16 @@ namespace wrtc { } void NativeConnection::resetDtlsSrtpTransport() { - relayPortFactory = std::make_unique(rtcServers); + bool standaloneReflectorMode = getCustomParameterBool("network_standalone_reflectors"); + uint32_t standaloneReflectorRoleId = 0; + if (standaloneReflectorMode) { + if (isOutgoing) { + standaloneReflectorRoleId = 1; + } else { + standaloneReflectorRoleId = 2; + } + } + relayPortFactory = std::make_unique(rtcServers, standaloneReflectorMode, standaloneReflectorRoleId); portAllocator = std::make_unique( factory->networkManager(), factory->socketFactory(), @@ -74,6 +83,13 @@ namespace wrtc { relayPortFactory.get() ); uint32_t flags = portAllocator->flags(); + if (getCustomParameterBool("network_use_default_route")) { + flags |= cricket::PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION; + } + + if (getCustomParameterBool("network_enable_shared_socket")) { + flags |= cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET; + } flags |= cricket::PORTALLOCATOR_ENABLE_IPV6; flags |= cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI; flags |= cricket::PORTALLOCATOR_DISABLE_TCP; @@ -114,6 +130,9 @@ namespace wrtc { iceConfig.continual_gathering_policy = cricket::GATHER_CONTINUALLY; iceConfig.prioritize_most_likely_candidate_pairs = true; iceConfig.regather_on_failed_networks_interval = 8000; + if (getCustomParameterBool("network_skip_initial_ping")) { + iceConfig.presume_writable_when_fully_relayed = true; + } transportChannel->SetIceConfig(iceConfig); const cricket::IceParameters localIceParameters( localParameters.ufrag, @@ -136,6 +155,13 @@ namespace wrtc { dtlsSrtpTransport->SetDtlsTransports(dtlsTransport.get(), nullptr); } + bool NativeConnection::getCustomParameterBool(const std::string& name) const { + if (customParameters == nullptr) { + return false; + } + return customParameters[name].is_boolean() && customParameters[name]; + } + void NativeConnection::createChannels() { const auto coordinatedState = contentNegotiationContext->coordinatedState(); if (!coordinatedState) { @@ -368,8 +394,15 @@ namespace wrtc { } void NativeConnection::addIceCandidate(const IceCandidate& rawCandidate) const { - networkThread()->PostTask([this, rawCandidate] { - transportChannel->AddRemoteCandidate(parseIceCandidate(rawCandidate)->candidate()); + const bool standaloneReflectorMode = getCustomParameterBool("network_standalone_reflectors"); + const auto candidate = parseIceCandidate(rawCandidate)->candidate(); + if (standaloneReflectorMode) { + if (absl::EndsWith(candidate.address().hostname(), ".reflector")) { + return; + } + } + networkThread()->PostTask([this, candidate] { + transportChannel->AddRemoteCandidate(candidate); }); } @@ -382,15 +415,16 @@ namespace wrtc { remoteIceParameters.supportsRenomination ); transportChannel->SetRemoteIceParameters(parameters); + rtc::SSLRole sslRole; if (sslSetup == "active") { - dtlsTransport->SetDtlsRole(rtc::SSLRole::SSL_SERVER); + sslRole = rtc::SSLRole::SSL_SERVER; } else if (sslSetup == "passive") { - dtlsTransport->SetDtlsRole(rtc::SSLRole::SSL_CLIENT); + sslRole = rtc::SSLRole::SSL_CLIENT; } else { - dtlsTransport->SetDtlsRole(isOutgoing ? rtc::SSLRole::SSL_CLIENT : rtc::SSLRole::SSL_SERVER); + sslRole = isOutgoing ? rtc::SSLRole::SSL_CLIENT : rtc::SSLRole::SSL_SERVER; } if (fingerprint) { - dtlsTransport->SetRemoteFingerprint(fingerprint->algorithm, fingerprint->digest.data(), fingerprint->digest.size()); + dtlsTransport->SetRemoteParameters(fingerprint->algorithm, fingerprint->digest.data(), fingerprint->digest.size(), sslRole); } }); } diff --git a/wrtc/interfaces/native_connection.hpp b/wrtc/interfaces/native_connection.hpp index d5ac6dd9..ad58bccf 100644 --- a/wrtc/interfaces/native_connection.hpp +++ b/wrtc/interfaces/native_connection.hpp @@ -22,9 +22,13 @@ #include "wrtc/models/content_negotiation_context.hpp" #include "wrtc/models/peer_ice_parameters.hpp" #include "wrtc/models/rtc_server.hpp" +#include namespace wrtc { + using nlohmann::json; + class NativeConnection final : public sigslot::has_slots<>, public InstanceNetworking, public NetworkInterface { + json customParameters; bool connected = false, failed = false; std::atomic isExiting; bool isOutgoing, enableP2P; @@ -76,6 +80,8 @@ namespace wrtc { void resetDtlsSrtpTransport(); + bool getCustomParameterBool(const std::string& name) const; + public: explicit NativeConnection(std::vector rtcServers, bool enableP2P, bool isOutgoing); diff --git a/wrtc/interfaces/reflector_port.cpp b/wrtc/interfaces/reflector_port.cpp index 27c7d1fc..d2eeeae5 100644 --- a/wrtc/interfaces/reflector_port.cpp +++ b/wrtc/interfaces/reflector_port.cpp @@ -26,63 +26,86 @@ #include "system_wrappers/include/field_trial.h" namespace wrtc { - ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* socket, const uint8_t serverId, const int serverPriority): Port( - PortParametersRef( - args.network_thread, - args.socket_factory, - args.network, - args.username, - args.password - ), - webrtc::IceCandidateType::kRelay + ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args, + rtc::AsyncPacketSocket* socket, + const uint8_t serverId, + const int serverPriority, + const bool standaloneReflectorMode, + const uint32_t standaloneReflectorRoleId): + Port( + PortParametersRef( + args.network_thread, + args.socket_factory, + args.network, + args.username, + args.password ), - serverAddress(*args.server_address), - serverId(serverId), - socket(socket), - error(0), - state(STATE_CONNECTING), - stunDscpValue(rtc::DSCP_NO_CHANGE), - credentials(args.config->credentials), - serverPriority(serverPriority) - { + webrtc::IceCandidateType::kRelay + ), + serverAddress(*args.server_address), + serverId(serverId), + socket(socket), + error(0), + state(STATE_CONNECTING), + standaloneReflectorMode(standaloneReflectorMode), + standaloneReflectorRoleId(standaloneReflectorRoleId), + stunDscpValue(rtc::DSCP_NO_CHANGE), + credentials(args.config->credentials), + serverPriority(serverPriority) { const auto rawPeerTag = parseHex(args.config->credentials.password); - auto generator = std::mt19937(std::random_device()()); - do { - std::uniform_int_distribution distribution; - randomTag = distribution(generator); - } while (!randomTag); peerTag.AppendData(rawPeerTag.data(), rawPeerTag.size() - 4); + if (standaloneReflectorMode) { + randomTag = standaloneReflectorRoleId; + } else { + auto generator = std::mt19937(std::random_device()()); + do { + std::uniform_int_distribution distribution; + randomTag = distribution(generator); + } while (!randomTag); + } peerTag.AppendData(reinterpret_cast(&randomTag), 4); } - ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args, const uint16_t min_port, const uint16_t max_port, const uint8_t serverId, const int serverPriority): Port( - PortParametersRef( - args.network_thread, - args.socket_factory, - args.network, - args.username, - args.password - ), - webrtc::IceCandidateType::kRelay, - min_port, - max_port + ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args, + const uint16_t min_port, + const uint16_t max_port, + const uint8_t serverId, + const int serverPriority, + const bool standaloneReflectorMode, + const uint32_t standaloneReflectorRoleId): + Port( + PortParametersRef( + args.network_thread, + args.socket_factory, + args.network, + args.username, + args.password ), - serverAddress(*args.server_address), - serverId(serverId), - socket(nullptr), - error(0), - state(STATE_CONNECTING), - stunDscpValue(rtc::DSCP_NO_CHANGE), - credentials(args.config->credentials), - serverPriority(serverPriority) - { + webrtc::IceCandidateType::kRelay, + min_port, + max_port // TODO: Check if "Shared" is needed + ), + serverAddress(*args.server_address), + serverId(serverId), + socket(nullptr), + error(0), + state(STATE_CONNECTING), + standaloneReflectorMode(standaloneReflectorMode), + standaloneReflectorRoleId(standaloneReflectorRoleId), + stunDscpValue(rtc::DSCP_NO_CHANGE), + credentials(args.config->credentials), + serverPriority(serverPriority) { const auto rawPeerTag = parseHex(args.config->credentials.password); - auto generator = std::mt19937(std::random_device()()); - do { - std::uniform_int_distribution distribution; - randomTag = distribution(generator); - } while (!randomTag); peerTag.AppendData(rawPeerTag.data(), rawPeerTag.size() - 4); + if (standaloneReflectorMode) { + randomTag = standaloneReflectorRoleId; + } else { + auto generator = std::mt19937(std::random_device()()); + do { + std::uniform_int_distribution distribution; + randomTag = distribution(generator); + } while (!randomTag); + } peerTag.AppendData(reinterpret_cast(&randomTag), 4); } @@ -101,20 +124,33 @@ namespace wrtc { delete socket; } - std::unique_ptr ReflectorPort::Create(const cricket::CreateRelayPortArgs &args, rtc::AsyncPacketSocket *socket, const uint8_t serverId, const int serverPriority) { + std::unique_ptr ReflectorPort::Create(const cricket::CreateRelayPortArgs &args, + rtc::AsyncPacketSocket *socket, + const uint8_t serverId, + const int serverPriority, + const bool standaloneReflectorMode, + const uint32_t standaloneReflectorRoleId) { if (args.config->credentials.username.size() > 32) { RTC_LOG(LS_ERROR) << "Attempt to use REFLECTOR with a too long username of length " << args.config->credentials.username.size(); return nullptr; } - return absl::WrapUnique(new ReflectorPort(args, socket, serverId, serverPriority)); + return absl::WrapUnique(new ReflectorPort(args, socket, serverId, serverPriority, standaloneReflectorMode, standaloneReflectorRoleId)); } - std::unique_ptr ReflectorPort::Create(const cricket::CreateRelayPortArgs &args, const uint16_t minPort, const uint16_t maxPort, const uint8_t serverId, const int serverPriority) { + std::unique_ptr ReflectorPort::Create( + const cricket::CreateRelayPortArgs &args, + const uint16_t minPort, + const uint16_t maxPort, + const uint8_t serverId, + const int serverPriority, + const bool standaloneReflectorMode, + const uint32_t standaloneReflectorRoleId + ) { if (args.config->credentials.username.size() > 32) { RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username of length " << args.config->credentials.username.size(); return nullptr; } - return absl::WrapUnique(new ReflectorPort(args, minPort, maxPort, serverId, serverPriority)); + return absl::WrapUnique(new ReflectorPort(args, minPort, maxPort, serverId, serverPriority, standaloneReflectorMode, standaloneReflectorRoleId)); } rtc::SocketAddress ReflectorPort::GetLocalAddress() const { @@ -197,7 +233,12 @@ namespace wrtc { bool ReflectorPort::CreateReflectorClientSocket() { RTC_DCHECK(!socket || SharedSocket()); if (serverAddress.proto == cricket::PROTO_UDP && !SharedSocket()) { - socket = socket_factory()->CreateUdpSocket(rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port()); + if (standaloneReflectorMode && Network()->name() == "shared-reflector-network") { + const rtc::IPAddress ipv4_any_address(INADDR_ANY); + socket = socket_factory()->CreateUdpSocket(rtc::SocketAddress(ipv4_any_address, 12345), min_port(), max_port()); + } else { + socket = socket_factory()->CreateUdpSocket(rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port()); + } } else if (serverAddress.proto == cricket::PROTO_TCP) { RTC_DCHECK(!SharedSocket()); constexpr int opts = rtc::PacketSocketFactory::OPT_STUN; @@ -429,7 +470,9 @@ namespace wrtc { const auto ipFormat = "reflector-" + std::to_string(static_cast(serverId)) + "-" + std::to_string(randomTag) + ".reflector"; rtc::SocketAddress candidateAddress(ipFormat, serverAddress.address.port()); - candidateAddress.SetResolvedIP(serverAddress.address.ipaddr()); + if (standaloneReflectorMode) { + candidateAddress.SetResolvedIP(serverAddress.address.ipaddr()); + } AddAddress( candidateAddress, serverAddress.address, @@ -529,6 +572,7 @@ namespace wrtc { }); } + // ReSharper disable once CppMemberFunctionMayBeConst void ReflectorPort::OnSendStunPacket(const void* data, const size_t size, cricket::StunRequest* _) { RTC_DCHECK(connected()); rtc::PacketOptions options(StunDscpValue()); diff --git a/wrtc/interfaces/reflector_port.hpp b/wrtc/interfaces/reflector_port.hpp index c6e40223..ad4e50c8 100644 --- a/wrtc/interfaces/reflector_port.hpp +++ b/wrtc/interfaces/reflector_port.hpp @@ -28,9 +28,24 @@ namespace wrtc { ~ReflectorPort() override; - static std::unique_ptr Create(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* socket, uint8_t serverId, int serverPriority); - - static std::unique_ptr Create(const cricket::CreateRelayPortArgs& args, uint16_t minPort, uint16_t maxPort, uint8_t serverId, int serverPriority); + static std::unique_ptr Create( + const cricket::CreateRelayPortArgs& args, + rtc::AsyncPacketSocket* socket, + uint8_t serverId, + int serverPriority, + bool standaloneReflectorMode, + uint32_t standaloneReflectorRoleId + ); + + static std::unique_ptr Create( + const cricket::CreateRelayPortArgs& args, + uint16_t minPort, + uint16_t maxPort, + uint8_t serverId, + int serverPriority, + bool standaloneReflectorMode, + uint32_t standaloneReflectorRoleId + ); rtc::SocketAddress GetLocalAddress() const; @@ -67,9 +82,24 @@ namespace wrtc { void HandleConnectionDestroyed(cricket::Connection* conn) override; protected: - ReflectorPort(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* socket, uint8_t serverId, int serverPriority); - - ReflectorPort(const cricket::CreateRelayPortArgs& args, uint16_t min_port, uint16_t max_port, uint8_t serverId, int serverPriority); + ReflectorPort( + const cricket::CreateRelayPortArgs& args, + rtc::AsyncPacketSocket* socket, + uint8_t serverId, + int serverPriority, + bool standaloneReflectorMode, + uint32_t standaloneReflectorRoleId + ); + + ReflectorPort( + const cricket::CreateRelayPortArgs& args, + uint16_t min_port, + uint16_t max_port, + uint8_t serverId, + int serverPriority, + bool standaloneReflectorMode, + uint32_t standaloneReflectorRoleId + ); rtc::DiffServCodePoint StunDscpValue() const override; @@ -91,6 +121,9 @@ namespace wrtc { PortState state; AttemptedServerSet attemptedServerAddresses; bool isRunningPingTask = false; + bool standaloneReflectorMode; + uint32_t standaloneReflectorRoleId; + rtc::DiffServCodePoint stunDscpValue; std::map resolvedPeerTagsByHostname; cricket::RelayCredentials credentials; diff --git a/wrtc/interfaces/reflector_relay_port_factory.cpp b/wrtc/interfaces/reflector_relay_port_factory.cpp index 8679f17a..ac918a04 100644 --- a/wrtc/interfaces/reflector_relay_port_factory.cpp +++ b/wrtc/interfaces/reflector_relay_port_factory.cpp @@ -8,7 +8,12 @@ #include "reflector_port.hpp" namespace wrtc { - ReflectorRelayPortFactory::ReflectorRelayPortFactory(const std::vector& servers): servers(servers) {} + ReflectorRelayPortFactory::ReflectorRelayPortFactory(const std::vector& servers, + const bool standaloneReflectorMode, + const uint32_t standaloneReflectorRoleId): + servers(servers), + standaloneReflectorMode(standaloneReflectorMode), + standaloneReflectorRoleId(standaloneReflectorRoleId) {} std::unique_ptr ReflectorRelayPortFactory::Create(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* udp_socket) { if (args.config->credentials.username == "reflector") { @@ -22,7 +27,7 @@ namespace wrtc { if (foundId == 0) { return nullptr; } - auto port = ReflectorPort::Create(args, udp_socket, foundId, args.relative_priority); + auto port = ReflectorPort::Create(args, udp_socket, foundId, args.relative_priority, standaloneReflectorMode, standaloneReflectorRoleId); if (!port) { return nullptr; } @@ -49,7 +54,7 @@ namespace wrtc { if (foundId == 0) { return nullptr; } - auto port = ReflectorPort::Create(args, min_port, max_port, foundId, args.relative_priority); + auto port = ReflectorPort::Create(args, min_port, max_port, foundId, args.relative_priority, standaloneReflectorMode, standaloneReflectorRoleId); if (!port) { return nullptr; } diff --git a/wrtc/interfaces/reflector_relay_port_factory.hpp b/wrtc/interfaces/reflector_relay_port_factory.hpp index f6dd28f5..5ca9b564 100644 --- a/wrtc/interfaces/reflector_relay_port_factory.hpp +++ b/wrtc/interfaces/reflector_relay_port_factory.hpp @@ -12,8 +12,10 @@ namespace wrtc { class ReflectorRelayPortFactory final : public cricket::RelayPortFactoryInterface { std::vector servers; + bool standaloneReflectorMode; + uint32_t standaloneReflectorRoleId; public: - explicit ReflectorRelayPortFactory(const std::vector& servers); + explicit ReflectorRelayPortFactory(const std::vector& servers, bool standaloneReflectorMode, uint32_t standaloneReflectorRoleId); ~ReflectorRelayPortFactory() override = default; From b93c09182aa52416494f89a84b7a6a8617a89616 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 00:47:01 +0200 Subject: [PATCH 21/48] Fixed wrong transportChannel --- wrtc/interfaces/native_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index fe52f832..afeeb103 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -347,7 +347,7 @@ namespace wrtc { transportChannel->MaybeStartGathering(); dataChannelInterface = std::make_unique( environment(), - transportChannel.get(), + dtlsTransport.get(), isOutgoing, networkThread(), signalingThread() From 4b4fc562c3efc320c91d298716d63d5cc3da458e Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 00:50:52 +0200 Subject: [PATCH 22/48] Code cleanup --- ...p_data_channel_provider_interface_impl.cpp | 9 -------- ...p_data_channel_provider_interface_impl.hpp | 21 ++++--------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp index 850bca34..150b9673 100644 --- a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp +++ b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp @@ -54,19 +54,16 @@ namespace wrtc { } void SctpDataChannelProviderInterfaceImpl::OnDataReceived(int channel_id, const webrtc::DataMessageType type, const rtc::CopyOnWriteBuffer& buffer) { - std::cout << "OnDataReceived" << std::endl; assert(networkThread->IsCurrent()); dataChannel->OnDataReceived(type, buffer); } void SctpDataChannelProviderInterfaceImpl::OnReadyToSend() { - std::cout << "OnReadyToSend" << std::endl; assert(networkThread->IsCurrent()); dataChannel->OnTransportReady(); } void SctpDataChannelProviderInterfaceImpl::OnStateChange() { - std::cout << "OnStateChange" << std::endl; assert(networkThread->IsCurrent()); const auto state = dataChannel->state(); if (const bool isDataChannelOpen = state == webrtc::DataChannelInterface::DataState::kOpen; isOpen != isDataChannelOpen) { @@ -76,24 +73,20 @@ namespace wrtc { } void SctpDataChannelProviderInterfaceImpl::OnMessage(const webrtc::DataBuffer& buffer) { - std::cout << "OnMessage" << std::endl; assert(networkThread->IsCurrent()); } webrtc::RTCError SctpDataChannelProviderInterfaceImpl::SendData(const webrtc::StreamId sid, const webrtc::SendDataParams& params, const rtc::CopyOnWriteBuffer& payload) { - std::cout << "SendData" << std::endl; assert(networkThread->IsCurrent()); return sctpTransport->SendData(sid.stream_id_int(), params, payload); } void SctpDataChannelProviderInterfaceImpl::AddSctpDataStream(const webrtc::StreamId sid) { - std::cout << "AddSctpDataStream" << std::endl; assert(networkThread->IsCurrent()); sctpTransport->OpenStream(sid.stream_id_int()); } void SctpDataChannelProviderInterfaceImpl::RemoveSctpDataStream(webrtc::StreamId sid) { - std::cout << "RemoveSctpDataStream" << std::endl; assert(networkThread->IsCurrent()); networkThread->BlockingCall([this, sid] { sctpTransport->ResetStream(sid.stream_id_int()); @@ -101,12 +94,10 @@ namespace wrtc { } void SctpDataChannelProviderInterfaceImpl::updateIsConnected(const bool isConnected) { - std::cout << "updateIsConnected" << std::endl; assert(networkThread->IsCurrent()); if (isConnected) { if (!isSctpTransportStarted) { isSctpTransportStarted = true; - std::cout << "isSctpTransportStarted" << std::endl; sctpTransport->Start(5000, 5000, 262144); } } diff --git a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp index 019d3e2a..6b7fb08c 100644 --- a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp +++ b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.hpp @@ -3,7 +3,6 @@ // #pragma once -#include #include #include #include @@ -57,22 +56,10 @@ namespace wrtc { void sendDataChannelMessage(const bytes::binary& data) const; // Unused - void OnChannelClosing(int channel_id) override - { - std::cout << "OnChannelClosing" << std::endl; - } - void OnChannelClosed(int channel_id) override - { - std::cout << "OnChannelClosed" << std::endl; - } - void OnChannelStateChanged(webrtc::SctpDataChannel* data_channel, webrtc::DataChannelInterface::DataState state) override - { - std::cout << "OnChannelStateChanged" << std::endl; - } - void OnTransportClosed(webrtc::RTCError error) override - { - std::cout << "OnTransportClosed" << std::endl; - }; + void OnChannelClosing(int channel_id) override {} + void OnChannelClosed(int channel_id) override{} + void OnChannelStateChanged(webrtc::SctpDataChannel* data_channel, webrtc::DataChannelInterface::DataState state) override{} + void OnTransportClosed(webrtc::RTCError error) override{} void onStateChanged(const std::function& callback); From 1cd480e48c67a0b0e22a92e82136773744a6bb2c Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 02:08:38 +0200 Subject: [PATCH 23/48] Fixed "sendMediaState" flood --- ntgcalls/instances/p2p_call.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/ntgcalls/instances/p2p_call.cpp b/ntgcalls/instances/p2p_call.cpp index 13061e8d..62fee100 100644 --- a/ntgcalls/instances/p2p_call.cpp +++ b/ntgcalls/instances/p2p_call.cpp @@ -170,7 +170,6 @@ namespace ntgcalls { } else { sendInitialSetup(); sendOfferIfNeeded(); - sendMediaState(stream->getState()); } } setConnectionObserver(); @@ -236,7 +235,6 @@ namespace ntgcalls { } sendOfferIfNeeded(); Safe(connection)->createChannels(); - sendMediaState(stream->getState()); break; } case signaling::Message::Type::RtcDescription: { From c182424b861aa2676a78de501f456a8df22d60ff Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 02:12:06 +0200 Subject: [PATCH 24/48] Code Cleanup --- wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp index 150b9673..73465fd1 100644 --- a/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp +++ b/wrtc/interfaces/sctp_data_channel_provider_interface_impl.cpp @@ -4,7 +4,6 @@ #include "sctp_data_channel_provider_interface_impl.hpp" -#include #include #include @@ -104,7 +103,6 @@ namespace wrtc { } void SctpDataChannelProviderInterfaceImpl::sendDataChannelMessage(const bytes::binary& data) const { - std::cout << "sendDataChannelMessage" << std::endl; if (isOpen) { const std::string message = bytes::to_string(data); RTC_LOG(LS_INFO) << "Outgoing DataChannel message: " << message; From ec93d9231b8538878e62a1caca8c4d99a42a7f15 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 02:12:40 +0200 Subject: [PATCH 25/48] Fixed missing video sending --- .../media/channels/outgoing_video_channel.cpp | 10 +++++----- .../media/channels/outgoing_video_channel.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wrtc/interfaces/media/channels/outgoing_video_channel.cpp b/wrtc/interfaces/media/channels/outgoing_video_channel.cpp index 6fa39858..cc98033c 100644 --- a/wrtc/interfaces/media/channels/outgoing_video_channel.cpp +++ b/wrtc/interfaces/media/channels/outgoing_video_channel.cpp @@ -15,7 +15,7 @@ namespace wrtc { const MediaContent &mediaContent, rtc::Thread *workerThread, rtc::Thread *networkThread, - LocalVideoAdapter* videoSink + LocalVideoAdapter* sink ): _ssrc(mediaContent.ssrc), workerThread(workerThread), networkThread(networkThread) { cricket::VideoOptions videoOptions; videoOptions.is_screencast = false; @@ -96,14 +96,14 @@ namespace wrtc { std::string errorDesc; channel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, errorDesc); channel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, errorDesc); + }); + channel->Enable(true); + workerThread->BlockingCall([&] { + channel->send_channel()->SetVideoSend(mediaContent.ssrc, nullptr, sink); webrtc::RtpParameters rtpParameters = channel->send_channel()->GetRtpSendParameters(mediaContent.ssrc); rtpParameters.degradation_preference = webrtc::DegradationPreference::MAINTAIN_RESOLUTION; channel->send_channel()->SetRtpSendParameters(mediaContent.ssrc, rtpParameters); }); - channel->Enable(false); - workerThread->BlockingCall([&] { - channel->send_channel()->SetVideoSend(mediaContent.ssrc, nullptr, videoSink); - }); } OutgoingVideoChannel::~OutgoingVideoChannel() { diff --git a/wrtc/interfaces/media/channels/outgoing_video_channel.hpp b/wrtc/interfaces/media/channels/outgoing_video_channel.hpp index 016f369a..0c510750 100644 --- a/wrtc/interfaces/media/channels/outgoing_video_channel.hpp +++ b/wrtc/interfaces/media/channels/outgoing_video_channel.hpp @@ -26,7 +26,7 @@ namespace wrtc { const MediaContent& mediaContent, rtc::Thread* workerThread, rtc::Thread* networkThread, - LocalVideoAdapter* videoSink + LocalVideoAdapter* sink ); ~OutgoingVideoChannel() override; From fee2c28ec803afe2069c998660935ea13065877f Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 11:02:00 +0200 Subject: [PATCH 26/48] Adjust 'Legacy advice' message level from Warning to Info. --- ntgcalls/signaling/signaling.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntgcalls/signaling/signaling.cpp b/ntgcalls/signaling/signaling.cpp index 72ac4146..1d1113ea 100644 --- a/ntgcalls/signaling/signaling.cpp +++ b/ntgcalls/signaling/signaling.cpp @@ -23,7 +23,7 @@ namespace signaling { throw ntgcalls::SignalingUnsupported("Signaling V1 is not supported"); } if (version & Version::V2) { - RTC_LOG(LS_WARNING) << "Using signaling V2 Legacy"; + RTC_LOG(LS_INFO) << "Using signaling V2 Legacy"; return std::make_unique(networkThread, signalingThread, key, onEmitData, onSignalData); } if (version & Version::V2Full) { From f78f1f2fc1dae56079ef74e4134b1920b6f887dc Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 11:09:25 +0200 Subject: [PATCH 27/48] Switch to "wrtc::MediaTrackInterface" --- ntgcalls/stream.cpp | 4 ++-- ntgcalls/stream.hpp | 2 +- .../media/channels/outgoing_audio_channel.cpp | 16 +++++++++----- .../media/channels/outgoing_audio_channel.hpp | 3 +++ .../media/channels/outgoing_video_channel.cpp | 18 ++++++++++----- .../media/channels/outgoing_video_channel.hpp | 3 +++ .../media/tracks/media_track_interface.cpp | 20 +++++++++++++++++ .../media/tracks/media_track_interface.hpp | 22 +++++++++++++++++++ wrtc/interfaces/native_connection.cpp | 17 ++++++++++++-- wrtc/interfaces/native_connection.hpp | 2 +- wrtc/interfaces/network_interface.hpp | 3 ++- wrtc/interfaces/peer_connection.cpp | 7 +++++- wrtc/interfaces/peer_connection.hpp | 2 +- 13 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 wrtc/interfaces/media/tracks/media_track_interface.cpp create mode 100644 wrtc/interfaces/media/tracks/media_track_interface.hpp diff --git a/ntgcalls/stream.cpp b/ntgcalls/stream.cpp index 1d0c7d48..5fae8952 100644 --- a/ntgcalls/stream.cpp +++ b/ntgcalls/stream.cpp @@ -32,8 +32,8 @@ namespace ntgcalls { } void Stream::addTracks(const std::unique_ptr& pc) { - pc->addTrack(audioTrack = audio->createTrack()); - pc->addTrack(videoTrack = video->createTrack()); + audioTrack = pc->addTrack(audio->createTrack()); + videoTrack = pc->addTrack(video->createTrack()); } void Stream::checkStream() const { diff --git a/ntgcalls/stream.hpp b/ntgcalls/stream.hpp index 93e173b4..b81b0822 100644 --- a/ntgcalls/stream.hpp +++ b/ntgcalls/stream.hpp @@ -57,7 +57,7 @@ namespace ntgcalls { private: std::unique_ptr audio; std::unique_ptr video; - rtc::scoped_refptr audioTrack, videoTrack; + std::unique_ptr audioTrack, videoTrack; std::unique_ptr reader; bool idling = false; std::atomic_bool hasVideo = false, changing = false, quit = false; diff --git a/wrtc/interfaces/media/channels/outgoing_audio_channel.cpp b/wrtc/interfaces/media/channels/outgoing_audio_channel.cpp index f76e24f9..fdfe5e4b 100644 --- a/wrtc/interfaces/media/channels/outgoing_audio_channel.cpp +++ b/wrtc/interfaces/media/channels/outgoing_audio_channel.cpp @@ -15,18 +15,17 @@ namespace wrtc { rtc::Thread *workerThread, rtc::Thread* networkThread, webrtc::LocalAudioSinkAdapter* sink - ): _ssrc(mediaContent.ssrc), workerThread(workerThread), networkThread(networkThread) { + ): _ssrc(mediaContent.ssrc), workerThread(workerThread), networkThread(networkThread), sink(sink) { cricket::AudioOptions audioOptions; audioOptions.echo_cancellation = false; audioOptions.noise_suppression = false; audioOptions.auto_gain_control = false; audioOptions.highpass_filter = false; - const auto contentId = std::to_string(_ssrc); channel = channelManager->CreateVoiceChannel( call, cricket::MediaConfig(), - contentId, + std::to_string(_ssrc), false, NativeConnection::getDefaultCryptoOptions(), audioOptions @@ -72,9 +71,8 @@ namespace wrtc { channel->SetLocalContent(outgoingDescription.get(), webrtc::SdpType::kOffer, errorDesc); channel->SetRemoteContent(incomingDescription.get(), webrtc::SdpType::kAnswer, errorDesc); }); - channel->Enable(true); + set_enabled(true); workerThread->BlockingCall([&] { - channel->send_channel()->SetAudioSend(_ssrc, true, nullptr, sink); webrtc::RtpParameters initialParameters = channel->send_channel()->GetRtpSendParameters(_ssrc); webrtc::RtpParameters updatedParameters = initialParameters; if (updatedParameters.encodings.empty()) { @@ -87,6 +85,13 @@ namespace wrtc { }); } + void OutgoingAudioChannel::set_enabled(const bool enable) const { + channel->Enable(enable); + workerThread->BlockingCall([&] { + channel->send_channel()->SetAudioSend(_ssrc, enable, nullptr, sink); + }); + } + OutgoingAudioChannel::~OutgoingAudioChannel() { channel->Enable(false); networkThread->BlockingCall([&] { @@ -95,6 +100,7 @@ namespace wrtc { workerThread->BlockingCall([&] { channel = nullptr; }); + sink = nullptr; } uint32_t OutgoingAudioChannel::ssrc() const { diff --git a/wrtc/interfaces/media/channels/outgoing_audio_channel.hpp b/wrtc/interfaces/media/channels/outgoing_audio_channel.hpp index a75e9111..e30a033b 100644 --- a/wrtc/interfaces/media/channels/outgoing_audio_channel.hpp +++ b/wrtc/interfaces/media/channels/outgoing_audio_channel.hpp @@ -16,6 +16,7 @@ namespace wrtc { std::unique_ptr channel; rtc::Thread* workerThread; rtc::Thread* networkThread; + webrtc::LocalAudioSinkAdapter* sink; public: OutgoingAudioChannel( @@ -28,6 +29,8 @@ namespace wrtc { webrtc::LocalAudioSinkAdapter* sink ); + void set_enabled(bool enable) const; + ~OutgoingAudioChannel() override; [[nodiscard]] uint32_t ssrc() const; diff --git a/wrtc/interfaces/media/channels/outgoing_video_channel.cpp b/wrtc/interfaces/media/channels/outgoing_video_channel.cpp index cc98033c..b0bb44fd 100644 --- a/wrtc/interfaces/media/channels/outgoing_video_channel.cpp +++ b/wrtc/interfaces/media/channels/outgoing_video_channel.cpp @@ -16,14 +16,14 @@ namespace wrtc { rtc::Thread *workerThread, rtc::Thread *networkThread, LocalVideoAdapter* sink - ): _ssrc(mediaContent.ssrc), workerThread(workerThread), networkThread(networkThread) { + ): _ssrc(mediaContent.ssrc), workerThread(workerThread), networkThread(networkThread), sink(sink) { cricket::VideoOptions videoOptions; videoOptions.is_screencast = false; bitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory(); channel = channelManager->CreateVideoChannel( call, cricket::MediaConfig(), - std::to_string(mediaContent.ssrc), + std::to_string(_ssrc), false, NativeConnection::getDefaultCryptoOptions(), videoOptions, @@ -98,11 +98,11 @@ namespace wrtc { channel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, errorDesc); }); channel->Enable(true); + set_enabled(true); workerThread->BlockingCall([&] { - channel->send_channel()->SetVideoSend(mediaContent.ssrc, nullptr, sink); - webrtc::RtpParameters rtpParameters = channel->send_channel()->GetRtpSendParameters(mediaContent.ssrc); + webrtc::RtpParameters rtpParameters = channel->send_channel()->GetRtpSendParameters(_ssrc); rtpParameters.degradation_preference = webrtc::DegradationPreference::MAINTAIN_RESOLUTION; - channel->send_channel()->SetRtpSendParameters(mediaContent.ssrc, rtpParameters); + channel->send_channel()->SetRtpSendParameters(_ssrc, rtpParameters); }); } @@ -115,6 +115,14 @@ namespace wrtc { channel = nullptr; bitrateAllocatorFactory = nullptr; }); + sink = nullptr; + } + + void OutgoingVideoChannel::set_enabled(const bool enable) const { + channel->Enable(enable); + workerThread->BlockingCall([&] { + channel->send_channel()->SetVideoSend(_ssrc, nullptr, enable ? sink:nullptr); + }); } uint32_t OutgoingVideoChannel::ssrc() const { diff --git a/wrtc/interfaces/media/channels/outgoing_video_channel.hpp b/wrtc/interfaces/media/channels/outgoing_video_channel.hpp index 0c510750..6df78ecc 100644 --- a/wrtc/interfaces/media/channels/outgoing_video_channel.hpp +++ b/wrtc/interfaces/media/channels/outgoing_video_channel.hpp @@ -17,6 +17,7 @@ namespace wrtc { rtc::Thread* workerThread; rtc::Thread* networkThread; std::unique_ptr bitrateAllocatorFactory; + LocalVideoAdapter* sink; public: OutgoingVideoChannel( @@ -31,6 +32,8 @@ namespace wrtc { ~OutgoingVideoChannel() override; + void set_enabled(bool enable) const; + [[nodiscard]] uint32_t ssrc() const; }; } // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/tracks/media_track_interface.cpp b/wrtc/interfaces/media/tracks/media_track_interface.cpp new file mode 100644 index 00000000..d57c820c --- /dev/null +++ b/wrtc/interfaces/media/tracks/media_track_interface.cpp @@ -0,0 +1,20 @@ +// +// Created by Laky64 on 05/09/2024. +// + +#include "media_track_interface.hpp" + +namespace wrtc { + MediaTrackInterface::MediaTrackInterface(const std::function& enableCallback) { + this->enableCallback = enableCallback; + } + + void MediaTrackInterface::set_enabled(const bool status) { + (void) enableCallback(status); + this->status = status; + } + + bool MediaTrackInterface::enabled() const { + return this->status; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/media/tracks/media_track_interface.hpp b/wrtc/interfaces/media/tracks/media_track_interface.hpp new file mode 100644 index 00000000..a044a53a --- /dev/null +++ b/wrtc/interfaces/media/tracks/media_track_interface.hpp @@ -0,0 +1,22 @@ +// +// Created by Laky64 on 05/09/2024. +// + +#pragma once + +#include "wrtc/utils/syncronized_callback.hpp" +#include + +namespace wrtc { + class MediaTrackInterface { + synchronized_callback enableCallback; + std::atomic status = true; + + public: + explicit MediaTrackInterface(const std::function& enableCallback); + + void set_enabled(bool status); + + bool enabled() const; + }; +} // wrtc diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index afeeb103..799cda48 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -16,6 +16,7 @@ #include "reflector_relay_port_factory.hpp" #include "media/rtc_audio_source.hpp" +#include "wrtc/exceptions.hpp" namespace wrtc { @@ -437,14 +438,26 @@ namespace wrtc { return contentNegotiationContext->setPendingAnwer(std::move(answer)); } - void NativeConnection::addTrack(const rtc::scoped_refptr& track) { + std::unique_ptr NativeConnection::addTrack(const rtc::scoped_refptr& track) { if (const auto audioTrack = dynamic_cast(track.get())) { audioChannelId = contentNegotiationContext->addOutgoingChannel(audioTrack); audioTrack->AddSink(&audioSink); - } else if (const auto videoTrack = dynamic_cast(track.get())) { + return std::make_unique([this](const bool enable) { + if (audioChannel != nullptr) { + audioChannel->set_enabled(enable); + } + }); + } + if (const auto videoTrack = dynamic_cast(track.get())) { videoChannelId = contentNegotiationContext->addOutgoingChannel(videoTrack); videoTrack->AddOrUpdateSink(&videoSink, rtc::VideoSinkWants()); + return std::make_unique([this](const bool enable) { + if (videoChannel != nullptr) { + videoChannel->set_enabled(enable); + } + }); } + throw RTCException("Unsupported track type"); } std::unique_ptr NativeConnection::localFingerprint() const { diff --git a/wrtc/interfaces/native_connection.hpp b/wrtc/interfaces/native_connection.hpp index ad58bccf..d35b78a6 100644 --- a/wrtc/interfaces/native_connection.hpp +++ b/wrtc/interfaces/native_connection.hpp @@ -104,7 +104,7 @@ namespace wrtc { std::unique_ptr setPendingAnswer(std::unique_ptr answer) const; - void addTrack(const rtc::scoped_refptr& track) override; + std::unique_ptr addTrack(const rtc::scoped_refptr& track) override; std::unique_ptr localFingerprint() const; diff --git a/wrtc/interfaces/network_interface.hpp b/wrtc/interfaces/network_interface.hpp index b5602c1d..104ab3bd 100644 --- a/wrtc/interfaces/network_interface.hpp +++ b/wrtc/interfaces/network_interface.hpp @@ -3,6 +3,7 @@ // #pragma once +#include "media/tracks/media_track_interface.hpp" #include "peer_connection/peer_connection_factory.hpp" #include "wrtc/enums.hpp" #include "wrtc/models/ice_candidate.hpp" @@ -46,7 +47,7 @@ namespace wrtc { virtual void addIceCandidate(const IceCandidate& rawCandidate) const = 0; - virtual void addTrack(const rtc::scoped_refptr& track) = 0; + virtual std::unique_ptr addTrack(const rtc::scoped_refptr& track) = 0; bool isDataChannelOpen() const; }; diff --git a/wrtc/interfaces/peer_connection.cpp b/wrtc/interfaces/peer_connection.cpp index fc0a6aa9..99377a51 100644 --- a/wrtc/interfaces/peer_connection.cpp +++ b/wrtc/interfaces/peer_connection.cpp @@ -118,13 +118,18 @@ namespace wrtc { peerConnection->AddIceCandidate(parseIceCandidate(rawCandidate)); } - void PeerConnection::addTrack(const rtc::scoped_refptr& track) { + std::unique_ptr PeerConnection::addTrack(const rtc::scoped_refptr& track) { if (!peerConnection) { throw RTCException("Cannot add track; PeerConnection is closed"); } if (const auto result = peerConnection->AddTrack(track, {}); !result.ok()) { throw wrapRTCError(result.error()); } + return std::make_unique([track](const bool enable) { + if (track != nullptr) { + track->set_enabled(enable); + } + }); } void PeerConnection::restartIce() const { diff --git a/wrtc/interfaces/peer_connection.hpp b/wrtc/interfaces/peer_connection.hpp index a21272d1..fd36bb44 100644 --- a/wrtc/interfaces/peer_connection.hpp +++ b/wrtc/interfaces/peer_connection.hpp @@ -34,7 +34,7 @@ namespace wrtc { void setRemoteDescription(const Description &description) const; - void addTrack(const rtc::scoped_refptr& track) override; + std::unique_ptr addTrack(const rtc::scoped_refptr& track) override; void addIceCandidate(const IceCandidate& rawCandidate) const override; From 246e0c6c8101158f66e63d3bac5cb74ba9fa89cb Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 11:10:03 +0200 Subject: [PATCH 28/48] Version Bump --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 917c8df8..403ee8ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ if(DEFINED PY_VERSION_INFO) set(IS_PYTHON TRUE) endif() -project(ntgcalls VERSION 1.2.1 LANGUAGES C CXX) +project(ntgcalls VERSION 1.2.2.1 LANGUAGES C CXX) add_compile_definitions(NTG_VERSION="${CMAKE_PROJECT_VERSION}") find_package(Threads REQUIRED) From abd86ac6266b6c1cfd91e3ce7debef266563dc75 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 11:16:13 +0200 Subject: [PATCH 29/48] Switch to clang-19 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0f773c53..3749a8ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ RUN yum install -y \ ENV MAKEFLAGS="-j$(nproc)" ENV CFLAGS="-march=native -O3" ENV CXXFLAGS="-march=native -O3" -RUN git clone --depth=1 -b release/18.x https://github.com/llvm/llvm-project.git \ +RUN git clone --depth=1 -b release/19.x https://github.com/llvm/llvm-project.git \ && cd llvm-project \ && mkdir build \ && cd build \ @@ -14,5 +14,5 @@ RUN git clone --depth=1 -b release/18.x https://github.com/llvm/llvm-project.git && make install \ && cd ../.. \ && rm -rf llvm-project \ - && ln -s /usr/local/bin/clang++ /usr/local/bin/clang++-18 \ + && ln -s /usr/local/bin/clang++ /usr/local/bin/clang++-19 \ && ln -s /usr/local/bin/python3.12 /usr/local/bin/python3 From 503bab4b946051f542268e9ee1058823716898ae Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 11:28:45 +0200 Subject: [PATCH 30/48] Updated Github Actions --- .github/workflows/build.yml | 2 +- .github/workflows/build_clang.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 489fad58..b460aba7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -238,7 +238,7 @@ jobs: - name: Create Release id: create-new-release - uses: softprops/action-gh-release@v2.0.4 + uses: softprops/action-gh-release@v2 with: files: | ./releases/* diff --git a/.github/workflows/build_clang.yml b/.github/workflows/build_clang.yml index aab1a362..85c53add 100644 --- a/.github/workflows/build_clang.yml +++ b/.github/workflows/build_clang.yml @@ -17,7 +17,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Docker image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: context: . file: Dockerfile From 038618a771b450698b3cc4ad200f20c77b2bf579 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 15:28:17 +0200 Subject: [PATCH 31/48] Create inline instance of JsepIceCandidate with patchedCandidate --- wrtc/interfaces/native_connection.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index 799cda48..9e682b2d 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -261,8 +261,7 @@ namespace wrtc { signalingThread()->PostTask([this, candidate] { cricket::Candidate patchedCandidate = candidate; patchedCandidate.set_component(1); - webrtc::JsepIceCandidate iceCandidate{std::string(),0}; - iceCandidate.SetCandidate(patchedCandidate); + webrtc::JsepIceCandidate iceCandidate{std::string(),0, patchedCandidate}; (void) iceCandidateCallback(IceCandidate(&iceCandidate)); }); } From caefd044561b3f1b72b1551e09ecc846b704a54b Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 15:32:17 +0200 Subject: [PATCH 32/48] Fixed wrong RTCServer ids Co-Authored-By: John Preston --- ntgcalls/models/rtc_server.cpp | 15 ++++++++++++++- ntgcalls/models/rtc_server.hpp | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ntgcalls/models/rtc_server.cpp b/ntgcalls/models/rtc_server.cpp index b8ccb401..5bfc9c5c 100644 --- a/ntgcalls/models/rtc_server.cpp +++ b/ntgcalls/models/rtc_server.cpp @@ -5,6 +5,16 @@ #include "rtc_server.hpp" namespace ntgcalls { + std::set RTCServer::collectEndpointIds(const std::vector& servers) { + std::set result; + for (const auto &server: servers) { + if (server.peerTag) { + result.emplace(server.id); + } + } + return result; + } + RTCServer::RTCServer( const uint64_t id, std::string ipv4, @@ -30,6 +40,7 @@ namespace ntgcalls { } std::vector RTCServer::toRtcServers(const std::vector& servers) { + const auto ids = collectEndpointIds(servers); std::vector wrtcServers; for (const auto& server: servers) { if (server.peerTag) { @@ -45,9 +56,11 @@ namespace ntgcalls { } return result; }; + const auto i = ids.find(server.id); + const auto id = static_cast(std::distance(ids.begin(), i) + 1); const auto pushPhone = [&](const std::string &host) { wrtc::RTCServer rtcServer; - rtcServer.id = server.id; + rtcServer.id = id; rtcServer.host = host; rtcServer.port = server.port; rtcServer.login = "reflector"; diff --git a/ntgcalls/models/rtc_server.hpp b/ntgcalls/models/rtc_server.hpp index 49d85cc2..eadc844c 100644 --- a/ntgcalls/models/rtc_server.hpp +++ b/ntgcalls/models/rtc_server.hpp @@ -12,6 +12,8 @@ namespace ntgcalls { class RTCServer { + static std::set collectEndpointIds(const std::vector& servers); + public: uint64_t id; std::string ipv4, ipv6; From dbfdc5edacc257fae934faf494262befccc74955 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 16:09:34 +0200 Subject: [PATCH 33/48] Fixed Boost Repository Co-Authored-By: nick <64551534+null-nick@users.noreply.github.com> --- cmake/FindBoost.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindBoost.cmake b/cmake/FindBoost.cmake index 4fb4f31d..1bf21a18 100644 --- a/cmake/FindBoost.cmake +++ b/cmake/FindBoost.cmake @@ -61,7 +61,7 @@ if(NOT DEFINED LAST_BOOST_LIBS OR set(BOOST_DOWNLOAD_DIR ${BOOST_DIR}/download) DownloadProject( - URL https://boostorg.jfrog.io/artifactory/main/release/${BOOST_REVISION}/source/boost_${BOOST_REVISION_UNDERSCORE}.tar.gz + URL https://archives.boost.io/release/${BOOST_REVISION}/source/boost_${BOOST_REVISION_UNDERSCORE}.tar.gz DOWNLOAD_DIR ${BOOST_DOWNLOAD_DIR} SOURCE_DIR ${BOOST_WORKDIR} ) From bd3305b8dfcf11e509a3a6185db7d21fc1cfe84b Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 18:02:12 +0200 Subject: [PATCH 34/48] Fixed function names typo --- wrtc/interfaces/native_connection.cpp | 2 +- wrtc/models/content_negotiation_context.cpp | 8 ++++---- wrtc/models/content_negotiation_context.hpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index 9e682b2d..d5027184 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -434,7 +434,7 @@ namespace wrtc { } std::unique_ptr NativeConnection::setPendingAnswer(std::unique_ptr answer) const { - return contentNegotiationContext->setPendingAnwer(std::move(answer)); + return contentNegotiationContext->setPendingAnswer(std::move(answer)); } std::unique_ptr NativeConnection::addTrack(const rtc::scoped_refptr& track) { diff --git a/wrtc/models/content_negotiation_context.cpp b/wrtc/models/content_negotiation_context.cpp index d5710602..53b2e618 100644 --- a/wrtc/models/content_negotiation_context.cpp +++ b/wrtc/models/content_negotiation_context.cpp @@ -160,7 +160,7 @@ namespace wrtc { auto mappedOffer = std::make_unique(); mappedOffer->exchangeId = pendingOutgoingOffer->exchangeId; for (const auto &content : offer->contents()) { - auto mappedContent = convertContentInfoToSingalingContent(content); + auto mappedContent = convertContentInfoToSignalingContent(content); if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kSendOnly) { mappedOffer->contents.push_back(std::move(mappedContent)); for (auto &channel : outgoingChannelDescriptions) { @@ -174,7 +174,7 @@ namespace wrtc { return mappedOffer; } - std::unique_ptr ContentNegotiationContext::setPendingAnwer(std::unique_ptr&& answer) { + std::unique_ptr ContentNegotiationContext::setPendingAnswer(std::unique_ptr&& answer) { if (!answer) { return nullptr; } @@ -381,7 +381,7 @@ namespace wrtc { return mappedContentInfo; } - MediaContent ContentNegotiationContext::convertContentInfoToSingalingContent(const cricket::ContentInfo &content) { + MediaContent ContentNegotiationContext::convertContentInfoToSignalingContent(const cricket::ContentInfo &content) { MediaContent mappedContent; switch (content.media_description()->type()) { case cricket::MediaType::MEDIA_TYPE_AUDIO: @@ -622,7 +622,7 @@ namespace wrtc { std::vector incomingChannels; for (const auto &content : answer->contents()) { - auto mappedContent = convertContentInfoToSingalingContent(content); + auto mappedContent = convertContentInfoToSignalingContent(content); if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kRecvOnly) { for (const auto &[type, ssrc, ssrcGroups, payloadTypes, rtpExtensions] : offer->contents) { diff --git a/wrtc/models/content_negotiation_context.hpp b/wrtc/models/content_negotiation_context.hpp index 73c75087..1d5db064 100644 --- a/wrtc/models/content_negotiation_context.hpp +++ b/wrtc/models/content_negotiation_context.hpp @@ -62,7 +62,7 @@ namespace wrtc { static cricket::ContentInfo convertSignalingContentToContentInfo(const std::string &contentId, const MediaContent &content, webrtc::RtpTransceiverDirection direction); - static MediaContent convertContentInfoToSingalingContent(const cricket::ContentInfo &content); + static MediaContent convertContentInfoToSignalingContent(const cricket::ContentInfo &content); static cricket::ContentInfo createInactiveContentInfo(const std::string &contentId); @@ -86,7 +86,7 @@ namespace wrtc { std::unique_ptr getPendingOffer(); - std::unique_ptr setPendingAnwer(std::unique_ptr&& answer); + std::unique_ptr setPendingAnswer(std::unique_ptr&& answer); [[nodiscard]] std::unique_ptr coordinatedState() const; From 3d5ff370b4d461e5ffb9087e287a8f5cf58a72d0 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 18:50:31 +0200 Subject: [PATCH 35/48] Fixed macOS build --- ntgcalls/signaling/messages/candidates_message.cpp | 2 +- wrtc/interfaces/reflector_port.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ntgcalls/signaling/messages/candidates_message.cpp b/ntgcalls/signaling/messages/candidates_message.cpp index 98b3a554..d7c424ca 100644 --- a/ntgcalls/signaling/messages/candidates_message.cpp +++ b/ntgcalls/signaling/messages/candidates_message.cpp @@ -23,7 +23,7 @@ namespace signaling { json j = json::parse(data.begin(), data.end()); auto message = std::make_unique(); for (const auto& iceCandidate : j["candidates"]) { - message->iceCandidates.emplace_back(iceCandidate["sdpString"]); + message->iceCandidates.push_back(IceCandidate{iceCandidate["sdpString"]}); } return std::move(message); } diff --git a/wrtc/interfaces/reflector_port.cpp b/wrtc/interfaces/reflector_port.cpp index d2eeeae5..ebf5d72f 100644 --- a/wrtc/interfaces/reflector_port.cpp +++ b/wrtc/interfaces/reflector_port.cpp @@ -33,13 +33,14 @@ namespace wrtc { const bool standaloneReflectorMode, const uint32_t standaloneReflectorRoleId): Port( - PortParametersRef( + PortParametersRef{ args.network_thread, args.socket_factory, args.network, args.username, - args.password - ), + args.password, + args.field_trials + }, webrtc::IceCandidateType::kRelay ), serverAddress(*args.server_address), @@ -74,13 +75,14 @@ namespace wrtc { const bool standaloneReflectorMode, const uint32_t standaloneReflectorRoleId): Port( - PortParametersRef( + PortParametersRef{ args.network_thread, args.socket_factory, args.network, args.username, - args.password - ), + args.password, + args.field_trials + }, webrtc::IceCandidateType::kRelay, min_port, max_port // TODO: Check if "Shared" is needed From 3e8fa1178811e6a4c741d851441a9ba1f75a66ba Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 19:54:05 +0200 Subject: [PATCH 36/48] Added missing "ScreenCaptureKit" for macOS --- cmake/macOS.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/macOS.cmake b/cmake/macOS.cmake index e40269d7..3395713e 100644 --- a/cmake/macOS.cmake +++ b/cmake/macOS.cmake @@ -26,4 +26,5 @@ target_link_libraries(${target_name} PUBLIC "-framework MetalKit" "-framework OpenGL" "-framework IOSurface" + "-framework ScreenCaptureKit" ) \ No newline at end of file From 72ab1f69a76b05f599c4f738e50e5900f3ceca5b Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 20:37:24 +0200 Subject: [PATCH 37/48] Fixed Windows Build --- setup.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index a643d8ce..279410b4 100644 --- a/setup.py +++ b/setup.py @@ -148,9 +148,12 @@ def get_os(): return subprocess.run(['uname', '-o'], stdout=subprocess.PIPE, text=True).stdout.strip() -def get_os_cmake_args(): +def get_os_cmake_args(debug: bool): if sys.platform.startswith('win32'): - pass + cxx_flags = ["/EHsc", "/D_ITERATOR_DEBUG_LEVEL=0", "/MTd" if debug else "/MT"] + return [ + f'-DCMAKE_CXX_Flags={" ".join(cxx_flags)}', + ] elif sys.platform.startswith('darwin'): return [ '-DCMAKE_OSX_ARCHITECTURES=arm64', @@ -200,7 +203,7 @@ def build_extension(self, ext: CMakeExtension) -> None: '--config', cfg, f'-j{multiprocessing.cpu_count()}', ] - cmake_args += get_os_cmake_args() + cmake_args += get_os_cmake_args('b' in version) build_temp = Path(self.build_temp) / ext.name if not build_temp.exists(): @@ -237,7 +240,7 @@ def run(self): f'-DCMAKE_BUILD_TYPE={cfg}', f'-DSTATIC_BUILD={"ON" if self.static else "OFF"}', ] - cmake_args += get_os_cmake_args() + cmake_args += get_os_cmake_args(self.debug) build_args = [ '--config', cfg, f'-j{multiprocessing.cpu_count()}', From d4ecb10b2ffe17c8618c791ee53982500b9fcd57 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Thu, 5 Sep 2024 21:06:52 +0200 Subject: [PATCH 38/48] Switch to Windows 2022 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b460aba7..636c4398 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,7 +57,7 @@ jobs: build_list+=",{\"os\":\"macos-14\",\"cibw_archs\":\"native\",\"shared_name\":\"macos-arm64\",\"cibw_os_build\":\"macosx_\"}" fi if [[ "${{ github.event.inputs.windows-build }}" == "true" ]]; then - build_list+=",{\"os\":\"windows-2019\",\"cibw_archs\":\"native\",\"shared_name\":\"windows-x86_64\"}" + build_list+=",{\"os\":\"windows-2022\",\"cibw_archs\":\"native\",\"shared_name\":\"windows-x86_64\"}" fi if [[ -n "$build_list" ]]; then build_list="${build_list:1}" From 3c39f35518029de1cd8d33c9ac2968e5e0f78a5f Mon Sep 17 00:00:00 2001 From: Laky64 Date: Fri, 6 Sep 2024 13:10:52 +0200 Subject: [PATCH 39/48] Updated submodules --- deps/json | 2 +- deps/pybind11 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/json b/deps/json index 199dea11..b36f4c47 160000 --- a/deps/json +++ b/deps/json @@ -1 +1 @@ -Subproject commit 199dea11b17c533721b26249e2dcaee6ca1d51d3 +Subproject commit b36f4c477c40356a0ae1204b567cca3c2a57d201 diff --git a/deps/pybind11 b/deps/pybind11 index 705efcce..cb9abc80 160000 --- a/deps/pybind11 +++ b/deps/pybind11 @@ -1 +1 @@ -Subproject commit 705efccecdf8632612dfb538008efa02c862d615 +Subproject commit cb9abc80aa092995d81a1aaf4b6d61f52e337665 From 191dae2ec4e41db6517ecc2ab5bf22606fc7f42f Mon Sep 17 00:00:00 2001 From: Laky64 Date: Fri, 6 Sep 2024 18:39:15 +0200 Subject: [PATCH 40/48] Fixed function name typo --- ntgcalls/ntgcalls.cpp | 4 ++-- ntgcalls/ntgcalls.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ntgcalls/ntgcalls.cpp b/ntgcalls/ntgcalls.cpp index 29d71a09..10e9c69a 100644 --- a/ntgcalls/ntgcalls.cpp +++ b/ntgcalls/ntgcalls.cpp @@ -73,7 +73,7 @@ namespace ntgcalls { SafeCall(connections[chatId].get())->onSignalingData([this, chatId](const bytes::binary& data) { WORKER("onSignalingData", updateThread, this, chatId, data) THREAD_SAFE - (void) emitCallaback(chatId, CAST_BYTES(data)); + (void) emitCallback(chatId, CAST_BYTES(data)); END_THREAD_SAFE END_WORKER }); @@ -174,7 +174,7 @@ namespace ntgcalls { void NTgCalls::onSignalingData(const std::function& callback) { std::lock_guard lock(mutex); - emitCallaback = callback; + emitCallback = callback; } ASYNC_RETURN(void) NTgCalls::sendSignalingData(const int64_t chatId, const BYTES(bytes::binary) &msgKey) { diff --git a/ntgcalls/ntgcalls.hpp b/ntgcalls/ntgcalls.hpp index bd768cde..cd2da69e 100644 --- a/ntgcalls/ntgcalls.hpp +++ b/ntgcalls/ntgcalls.hpp @@ -31,7 +31,7 @@ namespace ntgcalls { wrtc::synchronized_callback onEof; wrtc::synchronized_callback mediaStateCallback; wrtc::synchronized_callback connectionChangeCallback; - wrtc::synchronized_callback emitCallaback; + wrtc::synchronized_callback emitCallback; std::unique_ptr updateThread; std::unique_ptr hardwareInfo; std::mutex mutex; From 8c6a0be5ce2369f50ecb568217e84530b00b2cdb Mon Sep 17 00:00:00 2001 From: Laky64 Date: Fri, 6 Sep 2024 18:39:30 +0200 Subject: [PATCH 41/48] Removed unused variable --- wrtc/interfaces/native_connection.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/wrtc/interfaces/native_connection.hpp b/wrtc/interfaces/native_connection.hpp index d35b78a6..d17e1531 100644 --- a/wrtc/interfaces/native_connection.hpp +++ b/wrtc/interfaces/native_connection.hpp @@ -46,7 +46,6 @@ namespace wrtc { absl::optional currentRouteDescription; absl::optional currentConnectionDescription; std::unique_ptr eventLog; - std::unique_ptr taskQueueFactory; std::unique_ptr contentNegotiationContext; std::optional audioChannelId, videoChannelId; std::unique_ptr call; From 380d856849a387b8ecd143118b506e01d7a6c12e Mon Sep 17 00:00:00 2001 From: Laky64 Date: Fri, 6 Sep 2024 19:54:41 +0200 Subject: [PATCH 42/48] Fixed missing eventLog initialization --- ntgcalls/instances/call_interface.cpp | 2 ++ ntgcalls/instances/call_interface.hpp | 1 + wrtc/interfaces/native_connection.cpp | 9 ++++++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ntgcalls/instances/call_interface.cpp b/ntgcalls/instances/call_interface.cpp index 40902469..8d1dcf4c 100644 --- a/ntgcalls/instances/call_interface.cpp +++ b/ntgcalls/instances/call_interface.cpp @@ -13,6 +13,7 @@ namespace ntgcalls { CallInterface::~CallInterface() { RTC_LOG(LS_VERBOSE) << "Destroying CallInterface"; + isExiting = true; std::lock_guard lock(mutex); connectionChangeCallback = nullptr; stream = nullptr; @@ -79,6 +80,7 @@ namespace ntgcalls { RTC_LOG(LS_INFO) << "Connecting..."; (void) connectionChangeCallback(ConnectionState::Connecting); connection->onConnectionChange([this](const wrtc::ConnectionState state) { + if (isExiting) return; std::lock_guard lock(mutex); switch (state) { case wrtc::ConnectionState::Connecting: diff --git a/ntgcalls/instances/call_interface.hpp b/ntgcalls/instances/call_interface.hpp index 0d6f61c6..081c9edc 100644 --- a/ntgcalls/instances/call_interface.hpp +++ b/ntgcalls/instances/call_interface.hpp @@ -11,6 +11,7 @@ namespace ntgcalls { class CallInterface { bool connected = false; + std::atomic_bool isExiting; void cancelNetworkListener(); diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index d5027184..875956b1 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -20,10 +20,13 @@ namespace wrtc { - NativeConnection::NativeConnection( - std::vector rtcServers, + NativeConnection::NativeConnection(std::vector rtcServers, const bool enableP2P, - const bool isOutgoing): isOutgoing(isOutgoing), enableP2P(enableP2P), rtcServers(std::move(rtcServers)) { + const bool isOutgoing): + isOutgoing(isOutgoing), + enableP2P(enableP2P), + rtcServers(std::move(rtcServers)), + eventLog(std::make_unique()) { networkThread()->PostTask([this] { localParameters = PeerIceParameters( rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), From 5dbec64ea2a9e9e443ab79883ba72329259c230a Mon Sep 17 00:00:00 2001 From: Laky64 Date: Fri, 6 Sep 2024 19:56:16 +0200 Subject: [PATCH 43/48] Fixed segfault when deallocating DataChannel --- wrtc/interfaces/native_connection.cpp | 38 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index 875956b1..9f1b6ad6 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -229,9 +229,6 @@ namespace wrtc { NativeConnection::~NativeConnection() { close(); - isExiting = true; - dtlsSrtpTransport = nullptr; - asyncResolverFactory = nullptr; } void NativeConnection::notifyStateUpdated() const { @@ -368,24 +365,37 @@ namespace wrtc { } void NativeConnection::close() { + isExiting = true; + audioChannel = nullptr; + videoChannel = nullptr; + channelManager = nullptr; + if (factory) { + workerThread()->BlockingCall([&] { + call = nullptr; + }); + } + contentNegotiationContext = nullptr; if (factory) { - audioChannel = nullptr; - videoChannel = nullptr; - networkThread()->BlockingCall([this] { - dtlsSrtpTransport->UnsubscribeReadyToSend(this); - transportChannel->SignalCandidateGathered.disconnect(this); - transportChannel->SignalIceTransportStateChanged.disconnect(this); - transportChannel->SignalNetworkRouteChanged.disconnect(this); - dtlsTransport->SignalWritableState.disconnect(this); - dtlsTransport->SignalReceivingState.disconnect(this); - dtlsSrtpTransport->SetDtlsTransports(nullptr, nullptr); + networkThread()->BlockingCall([&] { + if (transportChannel) { + transportChannel->SignalCandidateGathered.disconnect(this); + transportChannel->SignalIceTransportStateChanged.disconnect(this); + transportChannel->SignalNetworkRouteChanged.disconnect(this); + } dataChannelInterface = nullptr; + if (dtlsTransport) { + dtlsTransport->SignalWritableState.disconnect(this); + dtlsTransport->SignalReceivingState.disconnect(this); + } + if (dtlsSrtpTransport) { + dtlsSrtpTransport->SetDtlsTransports(nullptr, nullptr); + } dtlsTransport = nullptr; transportChannel = nullptr; portAllocator = nullptr; }); - NetworkInterface::close(); } + NetworkInterface::close(); } void NativeConnection::sendDataChannelMessage(const bytes::binary& data) const { From bbd85c105690d0dced1e1b1847b2f39b0b42ddf6 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Fri, 6 Sep 2024 19:57:15 +0200 Subject: [PATCH 44/48] Version Bump --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 403ee8ad..67493f18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ if(DEFINED PY_VERSION_INFO) set(IS_PYTHON TRUE) endif() -project(ntgcalls VERSION 1.2.2.1 LANGUAGES C CXX) +project(ntgcalls VERSION 1.2.2.2 LANGUAGES C CXX) add_compile_definitions(NTG_VERSION="${CMAKE_PROJECT_VERSION}") find_package(Threads REQUIRED) From 5044a3d82ad3d8139e6ad08878eb8b1c40a127e9 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Mon, 9 Sep 2024 23:22:17 +0200 Subject: [PATCH 45/48] Switch to "atomic_bool" --- wrtc/interfaces/media/tracks/media_track_interface.hpp | 2 +- wrtc/interfaces/native_connection.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wrtc/interfaces/media/tracks/media_track_interface.hpp b/wrtc/interfaces/media/tracks/media_track_interface.hpp index a044a53a..76e05bfa 100644 --- a/wrtc/interfaces/media/tracks/media_track_interface.hpp +++ b/wrtc/interfaces/media/tracks/media_track_interface.hpp @@ -10,7 +10,7 @@ namespace wrtc { class MediaTrackInterface { synchronized_callback enableCallback; - std::atomic status = true; + std::atomic_bool status = true; public: explicit MediaTrackInterface(const std::function& enableCallback); diff --git a/wrtc/interfaces/native_connection.hpp b/wrtc/interfaces/native_connection.hpp index d17e1531..2a5d2ddd 100644 --- a/wrtc/interfaces/native_connection.hpp +++ b/wrtc/interfaces/native_connection.hpp @@ -30,7 +30,7 @@ namespace wrtc { class NativeConnection final : public sigslot::has_slots<>, public InstanceNetworking, public NetworkInterface { json customParameters; bool connected = false, failed = false; - std::atomic isExiting; + std::atomic_bool isExiting; bool isOutgoing, enableP2P; int64_t lastDisconnectedTimestamp = 0; std::vector rtcServers; From ebf01be77ee4cad15f9b0f22a867b5ed36e63dcc Mon Sep 17 00:00:00 2001 From: Laky64 Date: Mon, 9 Sep 2024 23:22:37 +0200 Subject: [PATCH 46/48] Fixed "idling" resume --- ntgcalls/stream.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ntgcalls/stream.cpp b/ntgcalls/stream.cpp index 5fae8952..f0c546eb 100644 --- a/ntgcalls/stream.cpp +++ b/ntgcalls/stream.cpp @@ -58,6 +58,7 @@ namespace ntgcalls { RTC_LOG(LS_INFO) << "Setting AVStream, Lock acquired"; const auto audioConfig = streamConfig.audio; const auto videoConfig = streamConfig.video; + const bool wasIdling = idling; idling = false; if (audioConfig) { audio->setConfig( @@ -82,7 +83,7 @@ namespace ntgcalls { RTC_LOG(LS_INFO) << "Creating MediaReaderFactory"; reader = std::make_unique(streamConfig, audio->frameSize(), video->frameSize()); RTC_LOG(LS_INFO) << "MediaReaderFactory created"; - if (wasVideo != hasVideo && !noUpgrade) { + if ((wasVideo != hasVideo || wasIdling) && !noUpgrade) { checkUpgrade(); } changing = false; From 3ceb7e9206c2f5e4a1bac6b04947fdccf137b807 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Tue, 10 Sep 2024 01:24:23 +0200 Subject: [PATCH 47/48] Code Cleanup --- wrtc/interfaces/instance_networking.cpp | 21 ------ wrtc/interfaces/instance_networking.hpp | 88 ------------------------- wrtc/interfaces/native_connection.cpp | 8 +++ wrtc/interfaces/native_connection.hpp | 7 +- wrtc/models/candidate_description.cpp | 21 ++++++ wrtc/models/candidate_description.hpp | 21 ++++++ wrtc/models/connection_description.cpp | 21 ++++++ wrtc/models/connection_description.hpp | 17 +++++ wrtc/models/route_description.cpp | 27 ++++++++ wrtc/models/route_description.hpp | 20 ++++++ 10 files changed, 140 insertions(+), 111 deletions(-) delete mode 100644 wrtc/interfaces/instance_networking.cpp delete mode 100644 wrtc/interfaces/instance_networking.hpp create mode 100644 wrtc/models/candidate_description.cpp create mode 100644 wrtc/models/candidate_description.hpp create mode 100644 wrtc/models/connection_description.cpp create mode 100644 wrtc/models/connection_description.hpp create mode 100644 wrtc/models/route_description.cpp create mode 100644 wrtc/models/route_description.hpp diff --git a/wrtc/interfaces/instance_networking.cpp b/wrtc/interfaces/instance_networking.cpp deleted file mode 100644 index dca6f7f2..00000000 --- a/wrtc/interfaces/instance_networking.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by Laky64 on 29/03/2024. -// - -#include "instance_networking.hpp" - -namespace wrtc { - InstanceNetworking::RouteDescription::RouteDescription(std::string localDescription_, - std::string remoteDescription_): - localDescription(std::move(localDescription_)), - remoteDescription(std::move(remoteDescription_)) - {} - - InstanceNetworking::ConnectionDescription::CandidateDescription InstanceNetworking::connectionDescriptionFromCandidate(const cricket::Candidate &candidate) { - ConnectionDescription::CandidateDescription result; - result.type = candidate.type_name(); - result.protocol = candidate.protocol(); - result.address = candidate.address().ToString(); - return result; - } -} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/instance_networking.hpp b/wrtc/interfaces/instance_networking.hpp deleted file mode 100644 index 95b500d5..00000000 --- a/wrtc/interfaces/instance_networking.hpp +++ /dev/null @@ -1,88 +0,0 @@ -// -// Created by Laky64 on 29/03/2024. -// - -#pragma once -#include -#include - -namespace wrtc { - - class InstanceNetworking { - public: - struct ConnectionDescription { - struct CandidateDescription { - std::string protocol; - std::string type; - std::string address; - - bool operator==(CandidateDescription const &rhs) const { - if (protocol != rhs.protocol) { - return false; - } - if (type != rhs.type) { - return false; - } - if (address != rhs.address) { - return false; - } - - return true; - } - - bool operator!=(const CandidateDescription& rhs) const { - return !(*this == rhs); - } - }; - - CandidateDescription local; - CandidateDescription remote; - - bool operator==(ConnectionDescription const &rhs) const { - if (local != rhs.local) { - return false; - } - if (remote != rhs.remote) { - return false; - } - - return true; - } - - bool operator!=(const ConnectionDescription& rhs) const { - return !(*this == rhs); - } - }; - - struct RouteDescription { - explicit RouteDescription(std::string localDescription_, std::string remoteDescription_); - - std::string localDescription; - std::string remoteDescription; - - bool operator==(RouteDescription const &rhs) const { - if (localDescription != rhs.localDescription) { - return false; - } - if (remoteDescription != rhs.remoteDescription) { - return false; - } - - return true; - } - - bool operator!=(const RouteDescription& rhs) const { - return !(*this == rhs); - } - }; - - struct State { - bool isReadyToSendData = false; - bool isFailed = false; - absl::optional route; - absl::optional connection; - }; - - static ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(const cricket::Candidate &candidate); - }; -} // wrtc \ No newline at end of file diff --git a/wrtc/interfaces/native_connection.cpp b/wrtc/interfaces/native_connection.cpp index 9f1b6ad6..a9f5976a 100644 --- a/wrtc/interfaces/native_connection.cpp +++ b/wrtc/interfaces/native_connection.cpp @@ -166,6 +166,14 @@ namespace wrtc { return customParameters[name].is_boolean() && customParameters[name]; } + CandidateDescription NativeConnection::connectionDescriptionFromCandidate(const cricket::Candidate& candidate) { + CandidateDescription result; + result.type = candidate.type_name(); + result.protocol = candidate.protocol(); + result.address = candidate.address().ToString(); + return result; + } + void NativeConnection::createChannels() { const auto coordinatedState = contentNegotiationContext->coordinatedState(); if (!coordinatedState) { diff --git a/wrtc/interfaces/native_connection.hpp b/wrtc/interfaces/native_connection.hpp index 2a5d2ddd..df70c52f 100644 --- a/wrtc/interfaces/native_connection.hpp +++ b/wrtc/interfaces/native_connection.hpp @@ -12,7 +12,6 @@ #include #include -#include "instance_networking.hpp" #include "network_interface.hpp" #include "sctp_data_channel_provider_interface_impl.hpp" #include "media/channel_manager.hpp" @@ -24,10 +23,13 @@ #include "wrtc/models/rtc_server.hpp" #include +#include "wrtc/models/connection_description.hpp" +#include "wrtc/models/route_description.hpp" + namespace wrtc { using nlohmann::json; - class NativeConnection final : public sigslot::has_slots<>, public InstanceNetworking, public NetworkInterface { + class NativeConnection final : public sigslot::has_slots<>, public NetworkInterface { json customParameters; bool connected = false, failed = false; std::atomic_bool isExiting; @@ -81,6 +83,7 @@ namespace wrtc { bool getCustomParameterBool(const std::string& name) const; + static CandidateDescription connectionDescriptionFromCandidate(const cricket::Candidate &candidate); public: explicit NativeConnection(std::vector rtcServers, bool enableP2P, bool isOutgoing); diff --git a/wrtc/models/candidate_description.cpp b/wrtc/models/candidate_description.cpp new file mode 100644 index 00000000..c00d1f62 --- /dev/null +++ b/wrtc/models/candidate_description.cpp @@ -0,0 +1,21 @@ +// +// Created by Laky64 on 10/09/2024. +// + +#include "candidate_description.hpp" + +namespace wrtc { + bool CandidateDescription::operator==(CandidateDescription const& rhs) const { + if (protocol != rhs.protocol) { + return false; + } + if (type != rhs.type) { + return false; + } + if (address != rhs.address) { + return false; + } + + return true; + } +} // wrtc \ No newline at end of file diff --git a/wrtc/models/candidate_description.hpp b/wrtc/models/candidate_description.hpp new file mode 100644 index 00000000..a4ad4e77 --- /dev/null +++ b/wrtc/models/candidate_description.hpp @@ -0,0 +1,21 @@ +// +// Created by Laky64 on 10/09/2024. +// + +#pragma once + +#include + +namespace wrtc { + struct CandidateDescription { + std::string protocol; + std::string type; + std::string address; + + bool operator==(CandidateDescription const &rhs) const; + + bool operator!=(const CandidateDescription& rhs) const { + return !(*this == rhs); + } + }; +} // wrtc diff --git a/wrtc/models/connection_description.cpp b/wrtc/models/connection_description.cpp new file mode 100644 index 00000000..3e2de0ff --- /dev/null +++ b/wrtc/models/connection_description.cpp @@ -0,0 +1,21 @@ +// +// Created by Laky64 on 10/09/2024. +// + +#include "connection_description.hpp" + +namespace wrtc { + bool ConnectionDescription::operator==(ConnectionDescription const& rhs) const { + if (local != rhs.local) { + return false; + } + if (remote != rhs.remote) { + return false; + } + return true; + } + + bool ConnectionDescription::operator!=(const ConnectionDescription& rhs) const { + return !(*this == rhs); + } +} // wrtc \ No newline at end of file diff --git a/wrtc/models/connection_description.hpp b/wrtc/models/connection_description.hpp new file mode 100644 index 00000000..7c6f11d9 --- /dev/null +++ b/wrtc/models/connection_description.hpp @@ -0,0 +1,17 @@ +// +// Created by Laky64 on 10/09/2024. +// + +#pragma once +#include "candidate_description.hpp" + +namespace wrtc { + struct ConnectionDescription { + CandidateDescription local; + CandidateDescription remote; + + bool operator==(ConnectionDescription const &rhs) const; + + bool operator!=(const ConnectionDescription& rhs) const; + }; +} // wrtc diff --git a/wrtc/models/route_description.cpp b/wrtc/models/route_description.cpp new file mode 100644 index 00000000..22122998 --- /dev/null +++ b/wrtc/models/route_description.cpp @@ -0,0 +1,27 @@ +// +// Created by Laky64 on 10/09/2024. +// + +#include "route_description.hpp" + +namespace wrtc { + RouteDescription::RouteDescription(std::string localDescription, std::string remoteDescription): + localDescription(std::move(localDescription)), + remoteDescription(std::move(remoteDescription)){} + + + bool RouteDescription::operator==(RouteDescription const& rhs) const { + if (localDescription != rhs.localDescription) { + return false; + } + if (remoteDescription != rhs.remoteDescription) { + return false; + } + + return true; + } + + bool RouteDescription::operator!=(const RouteDescription& rhs) const { + return !(*this == rhs); + } +} // wrtc \ No newline at end of file diff --git a/wrtc/models/route_description.hpp b/wrtc/models/route_description.hpp new file mode 100644 index 00000000..ba3ee82c --- /dev/null +++ b/wrtc/models/route_description.hpp @@ -0,0 +1,20 @@ +// +// Created by Laky64 on 10/09/2024. +// + +#pragma once + +#include + +namespace wrtc { + struct RouteDescription { + explicit RouteDescription(std::string localDescription, std::string remoteDescription); + + std::string localDescription; + std::string remoteDescription; + + bool operator==(RouteDescription const &rhs) const; + + bool operator!=(const RouteDescription& rhs) const; + }; +} // wrtc From b243d85dc58f85c34cf9c9a3d6ed70c4018f30c8 Mon Sep 17 00:00:00 2001 From: Laky64 Date: Tue, 10 Sep 2024 01:24:40 +0200 Subject: [PATCH 48/48] Version Bump --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67493f18..de559b76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ if(DEFINED PY_VERSION_INFO) set(IS_PYTHON TRUE) endif() -project(ntgcalls VERSION 1.2.2.2 LANGUAGES C CXX) +project(ntgcalls VERSION 1.2.2 LANGUAGES C CXX) add_compile_definitions(NTG_VERSION="${CMAKE_PROJECT_VERSION}") find_package(Threads REQUIRED)