diff --git a/.gclient b/.gclient new file mode 100644 index 0000000000..d7647d7bac --- /dev/null +++ b/.gclient @@ -0,0 +1 @@ +'solutions=[{name:src,url:https://www.github.com/ispysoftware/webrtc@origin/nudi}]' \ No newline at end of file diff --git a/api/jsep.h b/api/jsep.h index 110c1c03a3..7a25a64513 100644 --- a/api/jsep.h +++ b/api/jsep.h @@ -110,7 +110,7 @@ enum class SdpType { // Description must be treated as an SDP final answer, and the offer- // answer exchange must be considered complete after receiving this. - kAnswer = WEBRTC_SDP_TYPE_ANSER + kAnswer = WEBRTC_SDP_TYPE_ANSWER }; // Returns the string form of the given SDP type. String forms are defined in diff --git a/media/base/video_adapter.cc b/media/base/video_adapter.cc index c4f281980c..999579b2ae 100644 --- a/media/base/video_adapter.cc +++ b/media/base/video_adapter.cc @@ -220,22 +220,24 @@ bool VideoAdapter::AdaptFrameResolution(int in_width, const Fraction scale = FindScale((*cropped_width) * (*cropped_height), target_pixel_count, max_pixel_count); - // Make sure the resulting dimensions are aligned correctly to be nice to - // hardware encoders. - *out_width = - roundUp(*cropped_width * scale.numerator, - scale.denominator * required_resolution_alignment_, - in_width * scale.numerator) / scale.denominator; - *out_height = - roundUp(*cropped_height * scale.numerator, - scale.denominator * required_resolution_alignment_, - in_height * scale.numerator) / scale.denominator; + // Adjust cropping slightly to get even integer output size and a perfect + // scale factor. Make sure the resulting dimensions are aligned correctly + // to be nice to hardware encoders. + *cropped_width = + roundUp(*cropped_width, + scale.denominator * required_resolution_alignment_, in_width); + *cropped_height = + roundUp(*cropped_height, + scale.denominator * required_resolution_alignment_, in_height); + RTC_DCHECK_EQ(0, *cropped_width % scale.denominator); + RTC_DCHECK_EQ(0, *cropped_height % scale.denominator); + + // Calculate final output size. + *out_width = *cropped_width / scale.denominator * scale.numerator; + *out_height = *cropped_height / scale.denominator * scale.numerator; RTC_DCHECK_EQ(0, *out_width % required_resolution_alignment_); RTC_DCHECK_EQ(0, *out_height % required_resolution_alignment_); - *cropped_width = (*out_width * scale.denominator + scale.numerator - 1) / scale.numerator; - *cropped_height = (*out_height * scale.denominator + scale.numerator - 1) / scale.numerator; - ++frames_out_; if (scale.numerator != scale.denominator) ++frames_scaled_; diff --git a/modules/desktop_capture/win/dxgi_output_duplicator.cc b/modules/desktop_capture/win/dxgi_output_duplicator.cc index db7ba251c2..f2e9656670 100644 --- a/modules/desktop_capture/win/dxgi_output_duplicator.cc +++ b/modules/desktop_capture/win/dxgi_output_duplicator.cc @@ -273,7 +273,7 @@ bool DxgiOutputDuplicator::DoDetectUpdatedRegion( return false; } - if (metadata_.capacity() < frame_info.TotalMetadataBufferSize) { + if (metadata_.size() < frame_info.TotalMetadataBufferSize) { metadata_.clear(); // Avoid data copy metadata_.reserve(frame_info.TotalMetadataBufferSize); } @@ -283,7 +283,7 @@ bool DxgiOutputDuplicator::DoDetectUpdatedRegion( reinterpret_cast(metadata_.data()); size_t move_rects_count = 0; _com_error error = duplication_->GetFrameMoveRects( - static_cast(metadata_.capacity()), move_rects, &buff_size); + static_cast(metadata_.size()), move_rects, &buff_size); if (error.Error() != S_OK) { RTC_LOG(LS_ERROR) << "Failed to get move rectangles, error " << error.ErrorMessage() << ", code " << error.Error(); @@ -294,7 +294,7 @@ bool DxgiOutputDuplicator::DoDetectUpdatedRegion( RECT* dirty_rects = reinterpret_cast(metadata_.data() + buff_size); size_t dirty_rects_count = 0; error = duplication_->GetFrameDirtyRects( - static_cast(metadata_.capacity()) - buff_size, dirty_rects, + static_cast(metadata_.size()) - buff_size, dirty_rects, &buff_size); if (error.Error() != S_OK) { RTC_LOG(LS_ERROR) << "Failed to get dirty rectangles, error " diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index 3ef59f3c5d..da2801ce9b 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -239,7 +239,6 @@ if (rtc_include_tests) { } rtc_source_set("p2p_server_utils") { - testonly = true sources = [ "base/relay_server.cc", "base/relay_server.h", diff --git a/p2p/base/turn_server.cc b/p2p/base/turn_server.cc index cd00e5fdef..119c9cacbf 100644 --- a/p2p/base/turn_server.cc +++ b/p2p/base/turn_server.cc @@ -464,7 +464,7 @@ TurnServerAllocation* TurnServer::CreateAllocation(TurnServerConnection* conn, RTC_DCHECK(thread_checker_.IsCurrent()); rtc::AsyncPacketSocket* external_socket = (external_socket_factory_) - ? external_socket_factory_->CreateUdpSocket(external_addr_, 0, 0) + ? external_socket_factory_->CreateUdpSocket(external_addr_, min_udp_port_, max_udp_port_) : NULL; if (!external_socket) { return NULL; diff --git a/p2p/base/turn_server.h b/p2p/base/turn_server.h index 7308cd529a..1bd0eccfd0 100644 --- a/p2p/base/turn_server.h +++ b/p2p/base/turn_server.h @@ -191,6 +191,10 @@ class TurnServer : public sigslot::has_slots<> { RTC_DCHECK(thread_checker_.IsCurrent()); realm_ = realm; } + void set_port_range(size_t min_udp_port, size_t max_udp_port){ + min_udp_port_ = min_udp_port; + max_udp_port_ = max_udp_port; + } // Gets/sets the value for the SOFTWARE attribute for TURN messages. const std::string& software() const { @@ -336,6 +340,8 @@ class TurnServer : public sigslot::has_slots<> { std::vector> sockets_to_delete_; std::unique_ptr external_socket_factory_; rtc::SocketAddress external_addr_; + size_t min_udp_port_ = 0; + size_t max_udp_port_ = 0; AllocationMap allocations_; diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 7b0108d45d..f5c7e900b5 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -1007,7 +1007,6 @@ rtc_source_set("testclient") { } rtc_source_set("rtc_base_tests_utils") { - testonly = true sources = [ "cpu_time.cc", "cpu_time.h", diff --git a/rtc_base/virtual_socket_server.cc b/rtc_base/virtual_socket_server.cc index 83cf05827a..8c7ebb96e4 100644 --- a/rtc_base/virtual_socket_server.cc +++ b/rtc_base/virtual_socket_server.cc @@ -1026,10 +1026,9 @@ void VirtualSocketServer::UpdateDelayDistribution() { } } -static double PI = 4 * atan(1.0); - static double Normal(double x, double mean, double stddev) { double a = (x - mean) * (x - mean) / (2 * stddev * stddev); + double PI = 4 * atan(1.0); return exp(-a) / (stddev * sqrt(2 * PI)); } diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 1ff81e3e71..875f709382 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -796,9 +796,7 @@ if (is_ios || is_mac) { "..:no_global_constructors", ] sources = [ - "objc/api/peerconnection/RTCAudioDeviceModule+Private.h", "objc/api/peerconnection/RTCAudioDeviceModule.h", - "objc/api/peerconnection/RTCAudioDeviceModule.mm", "objc/api/peerconnection/RTCAudioSource+Private.h", "objc/api/peerconnection/RTCAudioSource.h", "objc/api/peerconnection/RTCAudioSource.mm", @@ -954,6 +952,10 @@ if (is_ios || is_mac) { ] if (is_ios) { + sources += [ + "objc/api/peerconnection/RTCAudioDeviceModule+Private.h", + "objc/api/peerconnection/RTCAudioDeviceModule.mm", + ] deps += [ ":native_api_audio_device_module" ] } } diff --git a/sdk/c/BUILD.gn b/sdk/c/BUILD.gn index 93b3827073..bd51633744 100644 --- a/sdk/c/BUILD.gn +++ b/sdk/c/BUILD.gn @@ -12,6 +12,7 @@ rtc_source_set("c") { deps = [ "api:audio_codecs", "api:create_peerconnection_factory", + "api:create_turn_server", "api:libjingle_peerconnection_api", "api:video_codecs", "api/video:video_frame", diff --git a/sdk/c/api/BUILD.gn b/sdk/c/api/BUILD.gn index 8b5e1d57e5..4f164b967b 100644 --- a/sdk/c/api/BUILD.gn +++ b/sdk/c/api/BUILD.gn @@ -63,3 +63,17 @@ rtc_source_set("video_codecs") { "../../../api/video_codecs:builtin_video_encoder_factory", ] } + +rtc_source_set("create_turn_server") { + sources = [ + "create_turn_server.cc", + "create_turn_server.h", + ] + deps = [ + "../../../p2p:p2p_server_utils", + "../../../p2p:rtc_p2p", + "../../../pc:rtc_pc", + "../../../rtc_base", + "../../../rtc_base:rtc_base_approved", + ] +} diff --git a/sdk/c/api/create_turn_server.cc b/sdk/c/api/create_turn_server.cc new file mode 100644 index 0000000000..a463843f64 --- /dev/null +++ b/sdk/c/api/create_turn_server.cc @@ -0,0 +1,95 @@ +/* + * Copyright 2012 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include +#include +#include +#include + +#include "p2p/base/basic_packet_socket_factory.h" +#include "p2p/base/port_interface.h" +#include "p2p/base/turn_server.h" +#include "rtc_base/async_udp_socket.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/socket_server.h" +#include "rtc_base/thread.h" +#include "rtc_base/string_encode.h" +#include "create_turn_server.h" + + +namespace { +class TurnFileAuth : public cricket::TurnAuthInterface { + public: + explicit TurnFileAuth(std::map name_to_key) + : name_to_key_(std::move(name_to_key)) {} + + virtual bool GetKey(const std::string& username, + const std::string& realm, + std::string* key) { + auto it = name_to_key_.find(username); + std::cerr << "TURN: Welcome " << username << std::endl; + if (it == name_to_key_.end()) { + return false; + } + *key = it->second; + return true; + } + + private: + const std::map name_to_key_; +}; + +} // namespace + +RTC_EXPORT extern "C" void webrtcCreateTURNServer( + RtcThread* network_thread, + const char* local_addr, + const char* ip_addr, + size_t min_port, + size_t max_port) { + + rtc::Thread* thread = rtc::ToCplusplus(network_thread); + rtc::SocketAddress int_addr; + if (!int_addr.FromString(local_addr)) { + std::cerr << "Unable to parse socket address: " << local_addr << std::endl; + return; + } + + rtc::IPAddress ext_addr; + if (!IPFromString(ip_addr, &ext_addr)) { + std::cerr << "Unable to parse IP address: " << ip_addr << std::endl; + return; + } + + rtc::AsyncUDPSocket* int_socket = + rtc::AsyncUDPSocket::Create(thread->socketserver(), int_addr); + if (!int_socket) { + std::cerr << "Failed to create a UDP socket bound at " << int_addr.ToString() + << std::endl; + return; + } + + cricket::TurnServer server(thread); + std::string pwd = "a8a16ccd399c0a716e1bca6be0016d6f"; + char buf[32]; + size_t len = rtc::hex_decode(buf, sizeof(buf), pwd.data(),pwd.size()); + std::map usrs = { + {"user",std::string(buf, len)} + }; + TurnFileAuth auth(usrs); + server.set_realm("Agent"); + server.set_software("Agent"); + server.set_port_range(min_port,max_port); + server.set_auth_hook(&auth); + server.AddInternalSocket(int_socket, cricket::PROTO_UDP); + server.SetExternalSocketFactory(new rtc::BasicPacketSocketFactory(), + rtc::SocketAddress(ext_addr, 0)); + thread->Run(); +} diff --git a/sdk/c/api/create_turn_server.h b/sdk/c/api/create_turn_server.h new file mode 100644 index 0000000000..68b41884c5 --- /dev/null +++ b/sdk/c/api/create_turn_server.h @@ -0,0 +1,25 @@ +/* + * Copyright 2019 pixiv Inc. All Rights Reserved. + * + * Use of this source code is governed by a license that can be + * found in the LICENSE.pixiv file in the root of the source tree. + */ + +#include "sdk/c/interop.h" +#include "sdk/c/rtc_base/thread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +RTC_EXPORT void webrtcCreateTURNServer( + RtcThread* network_thread, + const char* local_addr, + const char* ip_addr, + size_t min_port, + size_t max_port); + +#ifdef __cplusplus +} +#endif + diff --git a/sdk/c/api/data_channel_interface.cc b/sdk/c/api/data_channel_interface.cc index 75bf7fa36b..ff8c8ecea9 100644 --- a/sdk/c/api/data_channel_interface.cc +++ b/sdk/c/api/data_channel_interface.cc @@ -1,5 +1,5 @@ /* - * Copyright 2019 pixiv Inc. All Rights Reserved. + * Copyright 2019 developerinabox. All Rights Reserved. * * Use of this source code is governed by a license that can be * found in the LICENSE.pixiv file in the root of the source tree. @@ -7,7 +7,92 @@ #include "sdk/c/api/data_channel_interface.h" -RTC_EXPORT extern "C" void webrtcDataChannelInterfaceRelease( +namespace webrtc { + +class DelegatingDataChannelObserver + : public DataChannelObserver { + public: + DelegatingDataChannelObserver( + void* context, + const struct WebrtcDataChannelObserverFunctions* functions) { + context_ = context; + functions_ = functions; + } + + ~DelegatingDataChannelObserver() { + functions_->on_destruction(context_); + } + + void OnStateChange() override { + functions_->on_state_change(context_); + } + + void OnMessage(const DataBuffer& buffer) override { + auto * data = buffer.data.data(); + functions_->on_message(context_, buffer.binary, data, buffer.size()); + } + + void OnBufferedAmountChange(uint64_t sent_data_size) override { + functions_->on_buffered_amount_change(context_, sent_data_size); + } + + private: + void* context_; + const struct WebrtcDataChannelObserverFunctions* functions_; + }; +} + +extern "C" void webrtcDataChannelInterfaceRelease( const WebrtcDataChannelInterface* channel) { rtc::ToCplusplus(channel)->Release(); } + +extern "C" RtcString* webrtcDataChannelLabel( + const WebrtcDataChannelInterface* channel) { + return rtc::ToC(new auto(rtc::ToCplusplus(channel)->label())); +} + +extern "C" int webrtcDataChannelStatus( + const WebrtcDataChannelInterface* channel) { + auto chan = rtc::ToCplusplus(channel); + return chan->state(); +} + +extern "C" bool webrtcDataChannelSendText( + WebrtcDataChannelInterface* channel, + const char* text + ) { + auto chan = rtc::ToCplusplus(channel); + const auto db = webrtc::DataBuffer(text); + return chan->Send(db); +} + +extern "C" bool webrtcDataChannelSendData( + WebrtcDataChannelInterface* channel, + const char* data, + size_t len + ) { + auto chan = rtc::ToCplusplus(channel); + rtc::CopyOnWriteBuffer writeBuffer(data, len); + const auto db = webrtc::DataBuffer(writeBuffer, true); + return chan->Send(db); + } + +extern "C" WebrtcDataChannelObserver* webrtcDataChannelRegisterObserver( + void* context, + WebrtcDataChannelInterface* channel, + const struct WebrtcDataChannelObserverFunctions* functions) { + + auto chan = rtc::ToCplusplus(channel); + auto obs = new webrtc::DelegatingDataChannelObserver(context, functions); + chan->RegisterObserver(obs); + auto o = rtc::ToC(obs); + return o; +} + +extern "C" void webrtcDataChannelUnregisterObserver( + WebrtcDataChannelInterface* channel + ) { + auto chan = rtc::ToCplusplus(channel); + chan->UnregisterObserver(); +} diff --git a/sdk/c/api/data_channel_interface.h b/sdk/c/api/data_channel_interface.h index c929bf619f..a67e677be3 100644 --- a/sdk/c/api/data_channel_interface.h +++ b/sdk/c/api/data_channel_interface.h @@ -1,5 +1,5 @@ /* - * Copyright 2019 pixiv Inc. All Rights Reserved. + * Copyright 2020 developerinabox. All Rights Reserved. * * Use of this source code is governed by a license that can be * found in the LICENSE.pixiv file in the root of the source tree. @@ -15,12 +15,43 @@ extern "C" { #endif - RTC_C_CLASS(webrtc::DataChannelInterface, WebrtcDataChannelInterface) +RTC_C_CLASS(webrtc::DataChannelObserver, WebrtcDataChannelObserver) + +struct WebrtcDataChannelObserverFunctions { + void (*on_destruction)(void*); + void (*on_state_change)(void*); + void (*on_message)(void*, bool binary, const uint8_t* data, size_t len); + void (*on_buffered_amount_change)(void*, uint64_t); +}; RTC_EXPORT void webrtcDataChannelInterfaceRelease( const WebrtcDataChannelInterface* channel); +RTC_EXPORT RtcString* webrtcDataChannelLabel( + const WebrtcDataChannelInterface* channel); + +RTC_EXPORT int webrtcDataChannelStatus( + const WebrtcDataChannelInterface* channel); + +RTC_EXPORT bool webrtcDataChannelSendText( + WebrtcDataChannelInterface* channel, + const char* text); + +RTC_EXPORT bool webrtcDataChannelSendData( + WebrtcDataChannelInterface* channel, + const char* data, + size_t len); + +RTC_EXPORT WebrtcDataChannelObserver* webrtcDataChannelRegisterObserver( + void* context, + WebrtcDataChannelInterface* channel, + const struct WebrtcDataChannelObserverFunctions* functions); + +RTC_EXPORT void webrtcDataChannelUnregisterObserver( + WebrtcDataChannelInterface* channel); + + #ifdef __cplusplus } #endif diff --git a/sdk/c/api/jsep.cc b/sdk/c/api/jsep.cc index 29da980bff..15588fcf59 100644 --- a/sdk/c/api/jsep.cc +++ b/sdk/c/api/jsep.cc @@ -67,6 +67,21 @@ class DelegatingSetSessionDescriptionObserver }; } +extern "C" WebrtcIceCandidateInterface* webrtcCreateIceCandidate( + const char* sdp_mid, + int sdp_mline_index, + const char* sdp, + WebrtcSdpParseError** cerror) { + webrtc::SdpParseError* error = nullptr; + if (cerror) { + error = new webrtc::SdpParseError(); + *cerror = rtc::ToC(error); + } + + return rtc::ToC( + webrtc::CreateIceCandidate(sdp_mid, sdp_mline_index, sdp, error)); +} + extern "C" WebrtcSessionDescriptionInterface* webrtcCreateSessionDescription( enum WebrtcSdpType ctype, const char* sdp, @@ -86,11 +101,26 @@ extern "C" void webrtcCreateSessionDescriptionObserverRelease( rtc::ToCplusplus(observer)->Release(); } +extern "C" void webrtcDeleteIceCandidateInterface( + WebrtcIceCandidateInterface* candidate) { + delete rtc::ToCplusplus(candidate); +} + extern "C" void webrtcDeleteSessionDescriptionInterface( WebrtcSessionDescriptionInterface* desc) { delete rtc::ToCplusplus(desc); } +extern "C" RtcString* webrtcIceCandidateInterfaceSdp_mid( + const WebrtcIceCandidateInterface* candidate) { + return rtc::ToC(new auto(rtc::ToCplusplus(candidate)->sdp_mid())); +} + +extern "C" int webrtcIceCandidateInterfaceSdp_mline_index( + const WebrtcIceCandidateInterface* candidate) { + return rtc::ToCplusplus(candidate)->sdp_mline_index(); +} + extern "C" bool webrtcIceCandidateInterfaceToString( const WebrtcIceCandidateInterface* candidate, RtcString** out) { @@ -128,6 +158,24 @@ webrtcNewSetSessionDescriptionObserver( static_cast(observer)); } +extern "C" enum WebrtcOptionalSdpType webrtcSdpTypeFromString( + const char* type_str) { + auto optional = webrtc::SdpTypeFromString(type_str); + + return optional.has_value() ? + static_cast(optional.value()) : + WEBRTC_OPTIONAL_SDP_TYPE_NULLOPT; +} + +extern "C" const char* webrtcSdpTypeToString(enum WebrtcSdpType type) { + return webrtc::SdpTypeToString(static_cast(type)); +} + +extern "C" enum WebrtcSdpType webrtcSessionDescriptionInterfaceGetType( + const WebrtcSessionDescriptionInterface* desc) { + return static_cast(rtc::ToCplusplus(desc)->GetType()); +} + extern "C" bool webrtcSessionDescriptionInterfaceToString( const WebrtcSessionDescriptionInterface* desc, RtcString** out) { @@ -144,3 +192,31 @@ extern "C" void webrtcSetSessionDescriptionObserverRelease( const WebrtcSetSessionDescriptionObserver* observer) { rtc::ToCplusplus(observer)->Release(); } + + + + + + + + +extern "C" bool webrtcIceCandidateInterfaceResolve( + const WebrtcIceCandidateInterface* candidate, + RtcString** sdpMid, + int* sdpMLineIndex, + RtcString** sdp + ) { + + auto c = rtc::ToCplusplus(candidate); + + auto i = c->sdp_mline_index(); + sdpMLineIndex = &i; + + auto g = new std::string(); + *sdpMid = rtc::ToC(g); + g->assign(c->sdp_mid()); + + auto s = new std::string(); + *sdp = rtc::ToC(s); + return c->ToString(s); +} diff --git a/sdk/c/api/jsep.h b/sdk/c/api/jsep.h index 20e42ec1a4..021428a68b 100644 --- a/sdk/c/api/jsep.h +++ b/sdk/c/api/jsep.h @@ -13,7 +13,14 @@ enum WebrtcSdpType { WEBRTC_SDP_TYPE_OFFER, WEBRTC_SDP_TYPE_PR_ANSWER, - WEBRTC_SDP_TYPE_ANSER + WEBRTC_SDP_TYPE_ANSWER +}; + +enum WebrtcOptionalSdpType { + WEBRTC_OPTIONAL_SDP_TYPE_OFFER = WEBRTC_SDP_TYPE_OFFER, + WEBRTC_OPTIONAL_SDP_TYPE_PR_ANSWER = WEBRTC_SDP_TYPE_PR_ANSWER, + WEBRTC_OPTIONAL_SDP_TYPE_ANSWER = WEBRTC_SDP_TYPE_ANSWER, + WEBRTC_OPTIONAL_SDP_TYPE_NULLOPT }; #ifdef __cplusplus @@ -49,6 +56,12 @@ struct WebrtcSetSessionDescriptionObserverFunctions { void (*on_failure)(void*, enum WebrtcRTCErrorType, const char*); }; +RTC_EXPORT WebrtcIceCandidateInterface* webrtcCreateIceCandidate( + const char* sdp_mid, + int sdp_mline_index, + const char* sdp, + WebrtcSdpParseError** error); + RTC_EXPORT WebrtcSessionDescriptionInterface* webrtcCreateSessionDescription( enum WebrtcSdpType type, const char* sdp, @@ -57,9 +70,18 @@ RTC_EXPORT WebrtcSessionDescriptionInterface* webrtcCreateSessionDescription( RTC_EXPORT void webrtcCreateSessionDescriptionObserverRelease( const WebrtcCreateSessionDescriptionObserver* observer); +RTC_EXPORT void webrtcDeleteIceCandidateInterface( + WebrtcIceCandidateInterface* candidate); + RTC_EXPORT void webrtcDeleteSessionDescriptionInterface( WebrtcSessionDescriptionInterface* desc); +RTC_EXPORT RtcString* webrtcIceCandidateInterfaceSdp_mid( + const WebrtcIceCandidateInterface* candidate); + +RTC_EXPORT int webrtcIceCandidateInterfaceSdp_mline_index( + const WebrtcIceCandidateInterface* candidate); + RTC_EXPORT bool webrtcIceCandidateInterfaceToString( const WebrtcIceCandidateInterface* candidate, RtcString** out); @@ -74,6 +96,14 @@ webrtcNewSetSessionDescriptionObserver( void* context, const struct WebrtcSetSessionDescriptionObserverFunctions* functions); +RTC_EXPORT enum WebrtcOptionalSdpType +webrtcSdpTypeFromString(const char* type_str); + +RTC_EXPORT const char* webrtcSdpTypeToString(enum WebrtcSdpType type); + +RTC_EXPORT enum WebrtcSdpType webrtcSessionDescriptionInterfaceGetType( + const WebrtcSessionDescriptionInterface* desc); + RTC_EXPORT bool webrtcSessionDescriptionInterfaceToString( const WebrtcSessionDescriptionInterface* desc, RtcString** out); @@ -85,4 +115,4 @@ RTC_EXPORT void webrtcSetSessionDescriptionObserverRelease( } #endif -#endif +#endif \ No newline at end of file diff --git a/sdk/c/api/peer_connection_interface.cc b/sdk/c/api/peer_connection_interface.cc index 4a3ff579c7..1ad749348f 100644 --- a/sdk/c/api/peer_connection_interface.cc +++ b/sdk/c/api/peer_connection_interface.cc @@ -4,7 +4,7 @@ * Use of this source code is governed by a license that can be * found in the LICENSE.pixiv file in the root of the source tree. */ - +#include #include "api/peer_connection_interface.h" #include "sdk/c/api/peer_connection_interface.h" @@ -145,12 +145,12 @@ class DelegatingPeerConnectionObserver : public PeerConnectionObserver { } -RTC_EXPORT extern "C" void webrtcDeletePeerConnectionObserver( +extern "C" void webrtcDeletePeerConnectionObserver( WebrtcPeerConnectionObserver* observer) { delete rtc::ToCplusplus(observer); } -RTC_EXPORT extern "C" WebrtcPeerConnectionObserver* +extern "C" WebrtcPeerConnectionObserver* webrtcNewPeerConnectionObserver( void* context, const struct WebrtcPeerConnectionObserverFunctions* functions) { @@ -158,7 +158,7 @@ webrtcNewPeerConnectionObserver( new webrtc::DelegatingPeerConnectionObserver(context, functions))); } -RTC_EXPORT extern "C" WebrtcPeerConnectionInterface* +extern "C" WebrtcPeerConnectionInterface* webrtcPeerConnectionFactoryInterfaceCreatePeerConnection( WebrtcPeerConnectionFactoryInterface* factory, const struct WebrtcPeerConnectionInterfaceRTCConfiguration* cconfiguration, @@ -183,7 +183,12 @@ webrtcPeerConnectionFactoryInterfaceCreatePeerConnection( configuration.set_experiment_cpu_load_estimator( cconfiguration->flags & - WEBRTC_RTC_CONFIGURaTION_FLAG_EXPERIMENT_CPU_LOAD_ESTIMATOR); + WEBRTC_RTC_CONFIGURATION_FLAG_EXPERIMENT_CPU_LOAD_ESTIMATOR); + + if (cconfiguration->flags & WEBRTC_RTC_CONFIGURATION_FLAG_OVERRIDE_ENABLE_DTLS_SRTP) { + auto flag = cconfiguration->flags & WEBRTC_RTC_CONFIGURATION_FLAG_ENABLE_DTLS_SRTP; + configuration.enable_dtls_srtp = flag != 0; + } configuration.set_audio_rtcp_report_interval_ms( cconfiguration->audio_rtcp_report_interval_ms); @@ -274,7 +279,7 @@ webrtcPeerConnectionFactoryInterfaceCreatePeerConnection( return rtc::ToC(connection.release()); } -RTC_EXPORT extern "C" WebrtcAudioTrackInterface* +extern "C" WebrtcAudioTrackInterface* webrtcPeerConnectionFactoryInterfaceCreateAudioTrack( WebrtcPeerConnectionFactoryInterface* factory, const char* label, @@ -284,7 +289,7 @@ webrtcPeerConnectionFactoryInterfaceCreateAudioTrack( .release()); } -RTC_EXPORT extern "C" WebrtcVideoTrackInterface* +extern "C" WebrtcVideoTrackInterface* webrtcPeerConnectionFactoryInterfaceCreateVideoTrack( WebrtcPeerConnectionFactoryInterface* factory, const char* label, @@ -294,12 +299,12 @@ webrtcPeerConnectionFactoryInterfaceCreateVideoTrack( .release()); } -RTC_EXPORT extern "C" void webrtcPeerConnectionFactoryInterfaceRelease( +extern "C" void webrtcPeerConnectionFactoryInterfaceRelease( const WebrtcPeerConnectionFactoryInterface* factory) { rtc::ToCplusplus(factory)->Release(); } -RTC_EXPORT extern "C" WebrtcPeerConnectionInterfaceAddTrackResult +extern "C" WebrtcPeerConnectionInterfaceAddTrackResult webrtcPeerConnectionInterfaceAddTrack(WebrtcPeerConnectionInterface* connection, WebrtcMediaStreamTrackInterface* track, const char* const* data, @@ -324,12 +329,19 @@ webrtcPeerConnectionInterfaceAddTrack(WebrtcPeerConnectionInterface* connection, return cresult; } -RTC_EXPORT extern "C" void webrtcPeerConnectionInterfaceClose( +extern "C" bool webrtcPeerConnectionInterfaceAddIceCandidate( + WebrtcPeerConnectionInterface* connection, + const WebrtcIceCandidateInterface* candidate) { + return rtc::ToCplusplus(connection)->AddIceCandidate( + rtc::ToCplusplus(candidate)); +} + +extern "C" void webrtcPeerConnectionInterfaceClose( WebrtcPeerConnectionInterface* connection) { rtc::ToCplusplus(connection)->Close(); } -RTC_EXPORT extern "C" void webrtcPeerConnectionInterfaceCreateAnswer( +extern "C" void webrtcPeerConnectionInterfaceCreateAnswer( WebrtcPeerConnectionInterface* connection, WebrtcCreateSessionDescriptionObserver* observer, const struct WebrtcPeerConnectionInterfaceRTCOfferAnswerOptions* options) { @@ -345,6 +357,11 @@ extern "C" void webrtcPeerConnectionInterfaceCreateOffer( ->CreateOffer(rtc::ToCplusplus(observer), *options); } +extern "C" WebrtcRtpSenderInterfaces* webrtcPeerConnectionInterfaceGetSenders( + const WebrtcPeerConnectionInterface* connection) { + return rtc::ToC(new auto(rtc::ToCplusplus(connection)->GetSenders())); +} + extern "C" void webrtcPeerConnectionInterfaceRelease( const WebrtcPeerConnectionInterface* connection) { rtc::ToCplusplus(connection)->Release(); @@ -372,3 +389,19 @@ extern "C" void webrtcPeerConnectionInterfaceSetRemoteDescription( ->SetRemoteDescription(rtc::ToCplusplus(observer), rtc::ToCplusplus(desc)); } + +extern "C" void webrtcRtpSenderInterfacesMove( + WebrtcRtpSenderInterfaces* ccplusplus, + WebrtcRtpSenderInterface** c) { + auto cplusplusRaw = rtc::ToCplusplus(ccplusplus); + auto cplusplus = std::unique_ptr>>(cplusplusRaw); + + for (size_t index = 0; index < cplusplus->size(); index++) { + c[index] = rtc::ToC((*cplusplus)[index].release()); + } +} + +extern "C" size_t webrtcRtpSenderInterfacesSize( + const WebrtcRtpSenderInterfaces* interfaces) { + return rtc::ToCplusplus(interfaces)->size(); +} diff --git a/sdk/c/api/peer_connection_interface.h b/sdk/c/api/peer_connection_interface.h index 8cee9d5af3..7ec66cbb96 100644 --- a/sdk/c/api/peer_connection_interface.h +++ b/sdk/c/api/peer_connection_interface.h @@ -33,7 +33,9 @@ enum { WEBRTC_RTC_CONFIGURATION_FLAG_CPU_ADAPTATION = 1 << 2, WEBRTC_RTC_CONFIGURATION_FLAG_SUSPEND_BELOW_MIN_BITRATE = 1 << 3, WEBRTC_RTC_CONFIGURATION_FLAG_PRERENDERER_SMOOTHING = 1 << 4, - WEBRTC_RTC_CONFIGURaTION_FLAG_EXPERIMENT_CPU_LOAD_ESTIMATOR = 1 << 5, + WEBRTC_RTC_CONFIGURATION_FLAG_EXPERIMENT_CPU_LOAD_ESTIMATOR = 1 << 5, + WEBRTC_RTC_CONFIGURATION_FLAG_OVERRIDE_ENABLE_DTLS_SRTP = 1 << 6, + WEBRTC_RTC_CONFIGURATION_FLAG_ENABLE_DTLS_SRTP = 1 << 7, }; enum WebrtcSdpSemantics { @@ -169,6 +171,9 @@ struct WebrtcPeerConnectionInterfaceRTCOfferAnswerOptions { extern "C" { #endif +RTC_C_CLASS(std::vector>, + WebrtcRtpSenderInterfaces) + RTC_C_CLASS(webrtc::PeerConnectionObserver, WebrtcPeerConnectionObserver) RTC_C_CLASS(webrtc::PeerConnectionInterface, WebrtcPeerConnectionInterface) @@ -269,6 +274,9 @@ struct WebrtcPeerConnectionObserverFunctions { RTC_EXPORT void webrtcDeletePeerConnectionObserver( WebrtcPeerConnectionObserver* observer); + +RTC_EXPORT void webrtcDeleteRtpSenderInterfaces( + WebrtcRtpSenderInterfaces* interfaces); RTC_EXPORT WebrtcPeerConnectionObserver* webrtcNewPeerConnectionObserver( void* context, @@ -294,6 +302,10 @@ webrtcPeerConnectionFactoryInterfaceCreateVideoTrack( RTC_EXPORT void webrtcPeerConnectionFactoryInterfaceRelease( const WebrtcPeerConnectionFactoryInterface* factory); + +RTC_EXPORT bool webrtcPeerConnectionInterfaceAddIceCandidate( + WebrtcPeerConnectionInterface* connection, + const WebrtcIceCandidateInterface* candidate); RTC_EXPORT struct WebrtcPeerConnectionInterfaceAddTrackResult webrtcPeerConnectionInterfaceAddTrack(WebrtcPeerConnectionInterface* connection, @@ -313,6 +325,9 @@ RTC_EXPORT void webrtcPeerConnectionInterfaceCreateOffer( WebrtcPeerConnectionInterface* connection, WebrtcCreateSessionDescriptionObserver* observer, const struct WebrtcPeerConnectionInterfaceRTCOfferAnswerOptions* options); + +RTC_EXPORT WebrtcRtpSenderInterfaces* webrtcPeerConnectionInterfaceGetSenders( + const WebrtcPeerConnectionInterface* connection); RTC_EXPORT void webrtcPeerConnectionInterfaceRelease( const WebrtcPeerConnectionInterface* connection); @@ -331,6 +346,13 @@ RTC_EXPORT void webrtcPeerConnectionInterfaceSetRemoteDescription( WebrtcSetSessionDescriptionObserver* observer, WebrtcSessionDescriptionInterface* desc); +RTC_EXPORT void webrtcRtpSenderInterfacesMove( + WebrtcRtpSenderInterfaces* cplusplus, + WebrtcRtpSenderInterface** c); + +RTC_EXPORT size_t webrtcRtpSenderInterfacesSize( + const WebrtcRtpSenderInterfaces* interfaces); + #ifdef __cplusplus } #endif diff --git a/sdk/c/api/rtp_sender_interface.cc b/sdk/c/api/rtp_sender_interface.cc index 57904aa515..dc0500c77c 100644 --- a/sdk/c/api/rtp_sender_interface.cc +++ b/sdk/c/api/rtp_sender_interface.cc @@ -7,7 +7,38 @@ #include "sdk/c/api/rtp_sender_interface.h" +extern "C" void webrtcDeleteRtpSenderInterfaceStreamIds( + WebrtcRtpSenderInterfaceStreamIds* ids) { + delete rtc::ToCplusplus(ids); +} + extern "C" void webrtcRtpSenderInterfaceRelease( const WebrtcRtpSenderInterface* sender) { rtc::ToCplusplus(sender)->Release(); } + +extern "C" WebrtcRtpSenderInterfaceStreamIds* webrtcRtpSenderInterfaceStream_ids( + const WebrtcRtpSenderInterface* sender) { + return rtc::ToC(new auto(rtc::ToCplusplus(sender)->stream_ids())); +} + +extern "C" WebrtcMediaStreamTrackInterface* webrtcRtpSenderInterfaceTrack( + const WebrtcRtpSenderInterface* sender) { + return rtc::ToC(rtc::ToCplusplus(sender)->track()); +} + +extern "C" void webrtcRtpSenderInterfaceStreamIdsData( + WebrtcRtpSenderInterfaceStreamIds* ids, + const char** data) { + auto cplusplusRaw = rtc::ToCplusplus(ids); + auto cplusplus = std::unique_ptr>(cplusplusRaw); + + for (size_t index = 0; index < cplusplus->size(); index++) { + data[index] = (*cplusplus)[index].c_str(); + } +} + +extern "C" size_t webrtcRtpSenderInterfaceStreamIdsSize( + const WebrtcRtpSenderInterfaceStreamIds* ids) { + return rtc::ToCplusplus(ids)->size(); +} \ No newline at end of file diff --git a/sdk/c/api/rtp_sender_interface.h b/sdk/c/api/rtp_sender_interface.h index 639a142a66..c2f5abe5a7 100644 --- a/sdk/c/api/rtp_sender_interface.h +++ b/sdk/c/api/rtp_sender_interface.h @@ -9,20 +9,40 @@ #define SDK_C_API_RTP_SENDER_INTERFACE_H_ #include "sdk/c/interop.h" +#include "sdk/c/api/media_stream_interface.h" #ifdef __cplusplus #include "api/rtp_sender_interface.h" extern "C" { +#else +#include #endif +RTC_C_CLASS(std::vector, WebrtcRtpSenderInterfaceStreamIds) RTC_C_CLASS(webrtc::RtpSenderInterface, WebrtcRtpSenderInterface) +RTC_EXPORT void webrtcDeleteRtpSenderInterfaceStreamIds( + WebrtcRtpSenderInterfaceStreamIds* ids); + RTC_EXPORT void webrtcRtpSenderInterfaceRelease( const WebrtcRtpSenderInterface* sender); +RTC_EXPORT WebrtcRtpSenderInterfaceStreamIds* webrtcRtpSenderInterfaceStream_ids( + const WebrtcRtpSenderInterface* sender); + +RTC_EXPORT WebrtcMediaStreamTrackInterface* webrtcRtpSenderInterfaceTrack( + const WebrtcRtpSenderInterface* sender); + +RTC_EXPORT void webrtcRtpSenderInterfaceStreamIdsData( + WebrtcRtpSenderInterfaceStreamIds* ids, + const char** data); + +RTC_EXPORT size_t webrtcRtpSenderInterfaceStreamIdsSize( + const WebrtcRtpSenderInterfaceStreamIds* ids); + #ifdef __cplusplus } #endif -#endif +#endif \ No newline at end of file diff --git a/sdk/c_headers_test.c b/sdk/c_headers_test.c index 073b2f24b7..7ec675b20c 100644 --- a/sdk/c_headers_test.c +++ b/sdk/c_headers_test.c @@ -5,9 +5,6 @@ * found in the LICENSE.pixiv file in the root of the source tree. */ -#include "sdk/c/api/task_queue/queued_task.h" -#include "sdk/c/api/task_queue/task_queue_base.h" -#include "sdk/c/api/task_queue/task_queue_factory.h" #include "sdk/c/api/video/video_buffer.h" #include "sdk/c/api/video/video_frame.h" #include "sdk/c/api/video/video_rotation.h" @@ -18,6 +15,7 @@ #include "sdk/c/api/video/video_sink_interface.h" #include "sdk/c/api/audio_codecs.h" #include "sdk/c/api/create_peerconnection_factory.h" +#include "sdk/c/api/create_turn_server.h" #include "sdk/c/api/data_channel_interface.h" #include "sdk/c/api/jsep.h" #include "sdk/c/api/rtc_error.h" diff --git a/sdk/dotnet/unity/unity.msbuildproj b/sdk/dotnet/unity/unity.msbuildproj index c4f8832182..8f27b0674d 100644 --- a/sdk/dotnet/unity/unity.msbuildproj +++ b/sdk/dotnet/unity/unity.msbuildproj @@ -9,7 +9,7 @@ bin/ - Debug + Release $(BaseOutputPath)$(Configuration)/ @@ -19,7 +19,7 @@ $([System.IO.Path]::GetFullPath('$(OutputPath)')) true false - Android;Ios;LinuxX64;MacX64;WinX64 + WinX64;WinX86 ;$(Targets); "$(FullIntermediateOutputPath.Replace('\', '\\').Replace('"', '\"'))" $(FullOutputPath.Replace('\', '\\').Replace('"', '\"')) @@ -28,8 +28,8 @@ "$(OutputPath.Replace('\', '\\').Replace('"', '\"'))" "$(Targets.Replace('\', '\\').Replace('"', '\"'))" - - + + @@ -67,26 +67,33 @@ - + + + + + + Ptr; - - internal DisposableDataChannelInterface(IntPtr ptr) - { - Ptr = ptr; - } - - private protected override void FreePtr() - { - Interop.DataChannelInterface.Release(Ptr); - } - } -} - -namespace Pixiv.Webrtc.Interop -{ - public static class DataChannelInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDataChannelInterfaceRelease")] - public static extern void Release(IntPtr ptr); - } -} +/* + * Copyright 2019 pixiv Inc. All Rights Reserved. + * + * Use of this source code is governed by a license that can be + * found in the LICENSE.pixiv file in the root of the source tree. + */ +using Pixiv.Rtc; +using System; +using System.Runtime.InteropServices; + +namespace Pixiv.Webrtc +{ + public interface IDataChannelInterface + { + IntPtr Ptr { get; } + } + + public interface IDisposableDataChannelInterface : + IDataChannelInterface, Rtc.IDisposable + { + } + + public sealed class DisposableDataChannelInterface : + DisposablePtr, IDisposableDataChannelInterface + { + IntPtr IDataChannelInterface.Ptr => Ptr; + + internal DisposableDataChannelInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.DataChannelInterface.Release(Ptr); + } + + public IntPtr GetPtr + { + get + { + return Ptr; + } + } + } + public static class DataChannelInterfaceExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcDataChannelRegisterObserver(IntPtr s, IntPtr o); + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcDataChannelUnRegisterObserver(IntPtr s); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcDataChannelLabel( + IntPtr ptr + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern int webrtcDataChannelStatus( + IntPtr ptr + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern bool webrtcDataChannelSendText( + IntPtr ptr, string text + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern bool webrtcDataChannelSendData( + IntPtr ptr, IntPtr data, int len + ); + + public static string Label(this IDisposableDataChannelInterface channel) + { + return Rtc.Interop.String.MoveToString( + webrtcDataChannelLabel(channel.Ptr)); + } + + public static int Status(this IDisposableDataChannelInterface channel) + { + return webrtcDataChannelStatus(channel.Ptr); + } + + public static bool Send(this IDisposableDataChannelInterface channel, byte[] data) + { + var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + return webrtcDataChannelSendData(channel.Ptr, handle.AddrOfPinnedObject(), data.Length); + } + finally + { + handle.Free(); + } + } + + public static bool Send(this IDisposableDataChannelInterface channel, string text) + { + return webrtcDataChannelSendText(channel.Ptr, text); + } + + public static void UnRegisterObserver(this IDisposableDataChannelInterface channel) + { + webrtcDataChannelUnRegisterObserver(channel.Ptr); + } + } +} + +namespace Pixiv.Webrtc.Interop +{ + public static class DataChannelInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDataChannelInterfaceRelease")] + public static extern void Release(IntPtr ptr); + } +} diff --git a/sdk/dotnet/webrtc/api/jsep.cs b/sdk/dotnet/webrtc/api/jsep.cs index 2c99089e1d..7ec44b1021 100644 --- a/sdk/dotnet/webrtc/api/jsep.cs +++ b/sdk/dotnet/webrtc/api/jsep.cs @@ -1,323 +1,452 @@ -/* - * Copyright 2019 pixiv Inc. All Rights Reserved. - * - * Use of this source code is governed by a license that can be - * found in the LICENSE.pixiv file in the root of the source tree. - */ - -using Pixiv.Rtc; -using System; -using System.Runtime.InteropServices; - -namespace Pixiv.Webrtc -{ - public enum SdpType - { - Offer, - PrAnswer, - Answer - } - - public interface ICreateSessionDescriptionObserver - { - IntPtr Ptr { get; } - } - - public interface IDisposableCreateSessionDescriptionObserver : - ICreateSessionDescriptionObserver, Rtc.IDisposable - { - } - - public interface IDisposableIceCandidateInterface : - IIceCandidateInterface, Rtc.IDisposable - { - } - - public interface IDisposableSessionDescriptionInterface : - ISessionDescriptionInterface, Rtc.IDisposable - { - } - - public interface IDisposableSetSessionDescriptionObserver : - ISetSessionDescriptionObserver, Rtc.IDisposable - { - } - - public interface IIceCandidateInterface - { - IntPtr Ptr { get; } - } - - public interface IManagedCreateSessionDescriptionObserver - { - void OnSuccess(DisposableSessionDescriptionInterface desc); - void OnFailure(RtcError error); - } - - public interface IManagedSetSessionDescriptionObserver - { - void OnSuccess(); - void OnFailure(RtcError error); - } - - public interface ISessionDescriptionInterface - { - IntPtr Ptr { get; } - } - - public interface ISetSessionDescriptionObserver - { - IntPtr Ptr { get; } - } - - public sealed class DisposableCreateSessionDescriptionObserver : - DisposablePtr, IDisposableCreateSessionDescriptionObserver - { - IntPtr ICreateSessionDescriptionObserver.Ptr => Ptr; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void DestructionHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SuccessHandler(IntPtr context, IntPtr desc); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void FailureHandler(IntPtr context, RtcError error); - - private static readonly FunctionPtrArray s_functions = new FunctionPtrArray( - (DestructionHandler)OnDestruction, - (SuccessHandler)OnSuccess, - (FailureHandler)OnFailure - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcNewCreateSessionDescriptionObserver( - IntPtr context, - IntPtr functions - ); - - [MonoPInvokeCallback(typeof(DestructionHandler))] - private static void OnDestruction(IntPtr context) - { - ((GCHandle)context).Free(); - } - - [MonoPInvokeCallback(typeof(SuccessHandler))] - private static void OnSuccess(IntPtr context, IntPtr desc) - { - var handle = (GCHandle)context; - - ((IManagedCreateSessionDescriptionObserver)handle.Target).OnSuccess( - new DisposableSessionDescriptionInterface(desc) - ); - } - - [MonoPInvokeCallback(typeof(FailureHandler))] - private static void OnFailure(IntPtr context, RtcError error) - { - var handle = (GCHandle)context; - - ((IManagedCreateSessionDescriptionObserver)handle.Target).OnFailure( - error - ); - } - - public DisposableCreateSessionDescriptionObserver(IntPtr ptr) - { - Ptr = ptr; - } - - public DisposableCreateSessionDescriptionObserver( - IManagedCreateSessionDescriptionObserver implementation) - { - Ptr = webrtcNewCreateSessionDescriptionObserver( - (IntPtr)GCHandle.Alloc(implementation), - s_functions.Ptr - ); - } - - private protected override void FreePtr() - { - Interop.CreateSessionDescriptionObserver.Release(Ptr); - } - } - - public sealed class DisposableSessionDescriptionInterface : - DisposablePtr, IDisposableSessionDescriptionInterface - { - IntPtr ISessionDescriptionInterface.Ptr => Ptr; - - public DisposableSessionDescriptionInterface(IntPtr ptr) - { - Ptr = ptr; - } - - private protected override void FreePtr() - { - Interop.SessionDescriptionInterface.Delete(Ptr); - } - } - - public sealed class DisposableSetSessionDescriptionObserver : - DisposablePtr, IDisposableSetSessionDescriptionObserver - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void DestructionHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SuccessHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void FailureHandler(IntPtr context, RtcError error); - - private static FunctionPtrArray s_functions = new FunctionPtrArray( - (DestructionHandler)OnDestruction, - (SuccessHandler)OnSuccess, - (FailureHandler)OnFailure - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcNewSetSessionDescriptionObserver( - IntPtr context, - IntPtr functions - ); - - [MonoPInvokeCallback(typeof(DestructionHandler))] - private static void OnDestruction(IntPtr context) - { - ((GCHandle)context).Free(); - } - - [MonoPInvokeCallback(typeof(SuccessHandler))] - private static void OnSuccess(IntPtr context) - { - var handle = (GCHandle)context; - ((IManagedSetSessionDescriptionObserver)handle.Target).OnSuccess(); - } - - [MonoPInvokeCallback(typeof(FailureHandler))] - private static void OnFailure(IntPtr context, RtcError error) - { - var handle = (GCHandle)context; - - ((IManagedSetSessionDescriptionObserver)handle.Target).OnFailure( - error - ); - } - - IntPtr ISetSessionDescriptionObserver.Ptr => Ptr; - - public DisposableSetSessionDescriptionObserver(IntPtr ptr) - { - Ptr = ptr; - } - - public DisposableSetSessionDescriptionObserver( - IManagedSetSessionDescriptionObserver implementation) - { - Ptr = webrtcNewSetSessionDescriptionObserver( - (IntPtr)GCHandle.Alloc(implementation), - s_functions.Ptr - ); - } - - private protected override void FreePtr() - { - Interop.SetSessionDescriptionObserver.Release(Ptr); - } - } - - public sealed class IceCandidateInterface : IIceCandidateInterface - { - public IntPtr Ptr { get; } - - public IceCandidateInterface(IntPtr ptr) - { - Ptr = ptr; - } - } - - public static class IceCandidateInterfaceExtension - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern bool webrtcIceCandidateInterfaceToString( - IntPtr ptr, - out IntPtr result - ); - - public static bool TryToString( - this IIceCandidateInterface candidate, out string s) - { - var result = webrtcIceCandidateInterfaceToString( - candidate.Ptr, out var webrtcString); - - s = Rtc.Interop.String.MoveToString(webrtcString); - - return result; - } - } - - public static class SessionDescription - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcCreateSessionDescription( - SdpType type, - string sdp, - IntPtr error - ); - - public static DisposableSessionDescriptionInterface Create( - SdpType type, - string sdp, - IntPtr error) - { - var ptr = webrtcCreateSessionDescription(type, sdp, error); - - return ptr == IntPtr.Zero ? - null : new DisposableSessionDescriptionInterface(ptr); - } - } - - public static class SessionDescriptionInterfaceExtension - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - private static extern bool webrtcSessionDescriptionInterfaceToString( - IntPtr ptr, - out IntPtr result - ); - - public static bool TryToString( - this ISessionDescriptionInterface desc, - out string s) - { - var result = webrtcSessionDescriptionInterfaceToString( - desc.Ptr, out var webrtcString); - - s = Rtc.Interop.String.MoveToString(webrtcString); - - return result; - } - } -} - -namespace Pixiv.Webrtc.Interop -{ - public static class CreateSessionDescriptionObserver - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcCreateSessionDescriptionObserverRelease")] - public static extern void Release(IntPtr ptr); - } - - public static class SessionDescriptionInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDeleteSessionDescriptionInterface")] - public static extern void Delete(IntPtr ptr); - } - - public static class SetSessionDescriptionObserver - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcSetSessionDescriptionObserverRelease")] - public static extern void Release(IntPtr ptr); - } -} +/* + * Copyright 2019 pixiv Inc. All Rights Reserved. + * + * Use of this source code is governed by a license that can be + * found in the LICENSE.pixiv file in the root of the source tree. + */ + +using Pixiv.Rtc; +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Pixiv.Webrtc +{ + public enum SdpType + { + Offer, + PrAnswer, + Answer + } + + public interface ICreateSessionDescriptionObserver + { + IntPtr Ptr { get; } + } + + public interface IDisposableCreateSessionDescriptionObserver : + ICreateSessionDescriptionObserver, Rtc.IDisposable + { + } + + public interface IDisposableIceCandidateInterface : + IIceCandidateInterface, Rtc.IDisposable + { + } + + public interface IDisposableSessionDescriptionInterface : + ISessionDescriptionInterface, Rtc.IDisposable + { + } + + public interface IDisposableSetSessionDescriptionObserver : + ISetSessionDescriptionObserver, Rtc.IDisposable + { + } + + public interface IIceCandidateInterface + { + IntPtr Ptr { get; } + } + + public interface IManagedCreateSessionDescriptionObserver + { + void OnSuccess(DisposableSessionDescriptionInterface desc); + void OnFailure(RtcError error); + } + + public interface IManagedSetSessionDescriptionObserver + { + void OnSuccess(); + void OnFailure(RtcError error); + } + + public interface ISessionDescriptionInterface + { + IntPtr Ptr { get; } + } + + public interface ISetSessionDescriptionObserver + { + IntPtr Ptr { get; } + } + + public sealed class DisposableCreateSessionDescriptionObserver : + DisposablePtr, IDisposableCreateSessionDescriptionObserver + { + IntPtr ICreateSessionDescriptionObserver.Ptr => Ptr; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DestructionHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SuccessHandler(IntPtr context, IntPtr desc); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void FailureHandler(IntPtr context, RtcError error); + + private static readonly FunctionPtrArray s_functions = new FunctionPtrArray( + (DestructionHandler)OnDestruction, + (SuccessHandler)OnSuccess, + (FailureHandler)OnFailure + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcNewCreateSessionDescriptionObserver( + IntPtr context, + IntPtr functions + ); + + [MonoPInvokeCallback(typeof(DestructionHandler))] + private static void OnDestruction(IntPtr context) + { + ((GCHandle)context).Free(); + } + + [MonoPInvokeCallback(typeof(SuccessHandler))] + private static void OnSuccess(IntPtr context, IntPtr desc) + { + var handle = (GCHandle)context; + + ((IManagedCreateSessionDescriptionObserver)handle.Target).OnSuccess( + new DisposableSessionDescriptionInterface(desc) + ); + } + + [MonoPInvokeCallback(typeof(FailureHandler))] + private static void OnFailure(IntPtr context, RtcError error) + { + var handle = (GCHandle)context; + + ((IManagedCreateSessionDescriptionObserver)handle.Target).OnFailure( + error + ); + } + + public DisposableCreateSessionDescriptionObserver(IntPtr ptr) + { + Ptr = ptr; + } + + public DisposableCreateSessionDescriptionObserver( + IManagedCreateSessionDescriptionObserver implementation) + { + Ptr = webrtcNewCreateSessionDescriptionObserver( + (IntPtr)GCHandle.Alloc(implementation), + s_functions.Ptr + ); + } + + private protected override void FreePtr() + { + Interop.CreateSessionDescriptionObserver.Release(Ptr); + } + } + + public sealed class DisposableIceCandidateInterface : + DisposablePtr, IDisposableIceCandidateInterface + { + IntPtr IIceCandidateInterface.Ptr => Ptr; + + public DisposableIceCandidateInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.IceCandidateInterface.Delete(Ptr); + } + } + + public sealed class DisposableSessionDescriptionInterface : + DisposablePtr, IDisposableSessionDescriptionInterface + { + IntPtr ISessionDescriptionInterface.Ptr => Ptr; + + public DisposableSessionDescriptionInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.SessionDescriptionInterface.Delete(Ptr); + } + } + + public sealed class DisposableSetSessionDescriptionObserver : + DisposablePtr, IDisposableSetSessionDescriptionObserver + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DestructionHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SuccessHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void FailureHandler(IntPtr context, RtcError error); + + private static FunctionPtrArray s_functions = new FunctionPtrArray( + (DestructionHandler)OnDestruction, + (SuccessHandler)OnSuccess, + (FailureHandler)OnFailure + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcNewSetSessionDescriptionObserver( + IntPtr context, + IntPtr functions + ); + + [MonoPInvokeCallback(typeof(DestructionHandler))] + private static void OnDestruction(IntPtr context) + { + ((GCHandle)context).Free(); + } + + [MonoPInvokeCallback(typeof(SuccessHandler))] + private static void OnSuccess(IntPtr context) + { + var handle = (GCHandle)context; + ((IManagedSetSessionDescriptionObserver)handle.Target).OnSuccess(); + } + + [MonoPInvokeCallback(typeof(FailureHandler))] + private static void OnFailure(IntPtr context, RtcError error) + { + var handle = (GCHandle)context; + + ((IManagedSetSessionDescriptionObserver)handle.Target).OnFailure( + error + ); + } + + IntPtr ISetSessionDescriptionObserver.Ptr => Ptr; + + public DisposableSetSessionDescriptionObserver(IntPtr ptr) + { + Ptr = ptr; + } + + public DisposableSetSessionDescriptionObserver( + IManagedSetSessionDescriptionObserver implementation) + { + Ptr = webrtcNewSetSessionDescriptionObserver( + (IntPtr)GCHandle.Alloc(implementation), + s_functions.Ptr + ); + } + + private protected override void FreePtr() + { + Interop.SetSessionDescriptionObserver.Release(Ptr); + } + } + + public sealed class IceCandidateInterface : IIceCandidateInterface + { + public IntPtr Ptr { get; } + + public IceCandidateInterface(IntPtr ptr) + { + Ptr = ptr; + } + } + + public static class IceCandidate + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcCreateIceCandidate( + string sdpMid, + int sdpMlineIndex, + string sdp, + IntPtr error + ); + + public static DisposableIceCandidateInterface Create( + string sdpMid, + int sdpMlineIndex, + string sdp, + IntPtr error) + { + var ptr = webrtcCreateIceCandidate( + sdpMid, sdpMlineIndex, sdp, error); + + return ptr == IntPtr.Zero ? + null : new DisposableIceCandidateInterface(ptr); + } + } + + public static class IceCandidateInterfaceExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern bool webrtcIceCandidateInterfaceToString( + IntPtr ptr, + out IntPtr result + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcIceCandidateInterfaceSdp_mid( + IntPtr ptr + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern int webrtcIceCandidateInterfaceSdp_mline_index( + IntPtr ptr + ); + + public static string SdpMid(this IIceCandidateInterface candidate) + { + if (candidate == null) + { + throw new ArgumentNullException(nameof(candidate)); + } + + var ptr = webrtcIceCandidateInterfaceSdp_mid(candidate.Ptr); + GC.KeepAlive(candidate); + return Rtc.Interop.String.MoveToString(ptr); + } + + public static int SdpMlineIndex(this IIceCandidateInterface candidate) + { + if (candidate == null) + { + throw new ArgumentNullException(nameof(candidate)); + } + + return webrtcIceCandidateInterfaceSdp_mline_index(candidate.Ptr); + } + + public static bool TryToString( + this IIceCandidateInterface candidate, out string s) + { + if (candidate == null) + { + throw new ArgumentNullException(nameof(candidate)); + } + + var result = webrtcIceCandidateInterfaceToString( + candidate.Ptr, out var webrtcString); + + GC.KeepAlive(candidate); + s = Rtc.Interop.String.MoveToString(webrtcString); + + return result; + } + } + + public static class SdpTypeExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern int webrtcSdpTypeFromString(string typeStr); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcSdpTypeToString(SdpType type); + + public static SdpType? FromString(string typeStr) + { + var type = webrtcSdpTypeFromString(typeStr); + return type == 3 ? default(SdpType?) : (SdpType)type; + } + + public static string ToSdpString(this SdpType type) + { + return Marshal.PtrToStringAnsi(webrtcSdpTypeToString(type)); + } + } + + public static class SessionDescription + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcCreateSessionDescription( + SdpType type, + string sdp, + IntPtr error + ); + + public static DisposableSessionDescriptionInterface Create( + SdpType type, + string sdp, + IntPtr error) + { + var ptr = webrtcCreateSessionDescription(type, sdp, error); + + return ptr == IntPtr.Zero ? + null : new DisposableSessionDescriptionInterface(ptr); + } + } + + public static class SessionDescriptionInterfaceExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern SdpType webrtcSessionDescriptionInterfaceGetType( + IntPtr ptr + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private static extern bool webrtcSessionDescriptionInterfaceToString( + IntPtr ptr, + out IntPtr result + ); + + public static SdpType GetSdpType(this ISessionDescriptionInterface desc) + { + if (desc == null) + { + throw new ArgumentNullException(nameof(desc)); + } + + var result = webrtcSessionDescriptionInterfaceGetType(desc.Ptr); + GC.KeepAlive(desc); + + return result; + } + + public static bool TryToString( + this ISessionDescriptionInterface desc, + out string s) + { + if (desc == null) + { + throw new ArgumentNullException(nameof(desc)); + } + + var result = webrtcSessionDescriptionInterfaceToString( + desc.Ptr, out var webrtcString); + + GC.KeepAlive(desc); + s = Rtc.Interop.String.MoveToString(webrtcString); + + return result; + } + } +} + +namespace Pixiv.Webrtc.Interop +{ + public static class CreateSessionDescriptionObserver + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcCreateSessionDescriptionObserverRelease")] + public static extern void Release(IntPtr ptr); + } + + public static class IceCandidateInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDeleteIceCandidateInterface")] + public static extern void Delete(IntPtr ptr); + } + + public static class SessionDescriptionInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDeleteSessionDescriptionInterface")] + public static extern void Delete(IntPtr ptr); + } + + public static class SetSessionDescriptionObserver + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcSetSessionDescriptionObserverRelease")] + public static extern void Release(IntPtr ptr); + } +} \ No newline at end of file diff --git a/sdk/dotnet/webrtc/api/jsep_dc.cs b/sdk/dotnet/webrtc/api/jsep_dc.cs new file mode 100644 index 0000000000..9c038a06fb --- /dev/null +++ b/sdk/dotnet/webrtc/api/jsep_dc.cs @@ -0,0 +1,142 @@ +/* + * Copyright 2019 pixiv Inc. All Rights Reserved. + * + * Use of this source code is governed by a license that can be + * found in the LICENSE.pixiv file in the root of the source tree. + */ + +using Pixiv.Rtc; +using System; +using System.Runtime.InteropServices; + +namespace Pixiv.Webrtc +{ + public interface IRTCDataBufferInterface + { + IntPtr Ptr { get; } + } + + public sealed class RTCDataBufferInterface : IRTCDataBufferInterface + { + public IntPtr Ptr { get; } + + public RTCDataBufferInterface(IntPtr ptr) + { + Ptr = ptr; + } + } + + public interface IManagedDataChannelObserver + { + DisposableDataChannelInterface DataChannel { get; } + void OnStateChange(); + void OnMessage(bool binary, IntPtr data, int data_size); + void OnBufferedAmountChange(UInt64 sent_data_size); + } + + public interface IDataChannelObserver + { + IntPtr Ptr { get; } + } + + public interface IDisposableDataChannelObserver : + IDataChannelObserver, Rtc.IDisposable + { + } + + public sealed class DisposableDataChannelObserver : DisposablePtr, IDisposableDataChannelObserver + { + IntPtr IDataChannelObserver.Ptr => Ptr; + + + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DestructionHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void OnBufferedAmountChangeHandler(IntPtr context, ulong sent_data_size); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void OnMessageHandler(IntPtr context, bool binary, IntPtr data, int data_size); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void OnStateChangeHandler(IntPtr context); + + private static readonly FunctionPtrArray s_functions = new FunctionPtrArray( + (DestructionHandler)OnDestruction, + (OnStateChangeHandler)OnStateChange, + (OnMessageHandler)OnMessage, + (OnBufferedAmountChangeHandler)OnBufferedAmountChange + ); + + [MonoPInvokeCallback(typeof(OnStateChangeHandler))] + private static void OnStateChange(IntPtr context) + { + var handle = (GCHandle)context; + + ((IManagedDataChannelObserver)handle.Target).OnStateChange(); + } + + [MonoPInvokeCallback(typeof(OnMessageHandler))] + private static void OnMessage(IntPtr context, bool binary, IntPtr data, int data_size) + { + var handle = (GCHandle)context; + + ((IManagedDataChannelObserver)handle.Target).OnMessage(binary, data, data_size); + } + + [MonoPInvokeCallback(typeof(OnBufferedAmountChangeHandler))] + private static void OnBufferedAmountChange(IntPtr context, ulong sent_data_size) + { + var handle = (GCHandle)context; + + ((IManagedDataChannelObserver)handle.Target).OnBufferedAmountChange(sent_data_size); + } + + private protected override void FreePtr() + { + Interop.DataChannel.UnregisterObserver(DataChannel.GetPtr); + DataChannel.Dispose(); + } + + [MonoPInvokeCallback(typeof(DestructionHandler))] + private static void OnDestruction(IntPtr context) + { + ((GCHandle)context).Free(); + } + + public DisposableDataChannelObserver( + IManagedDataChannelObserver implementation) + { + DataChannel = implementation.DataChannel; + Ptr = Interop.DataChannel.RegisterObserver( + (IntPtr)GCHandle.Alloc(implementation), + implementation.DataChannel.GetPtr, + s_functions.Ptr + ); + } + + public DisposableDataChannelInterface DataChannel + { + get; private set; + } + + } +} +namespace Pixiv.Webrtc.Interop +{ + public static class DataChannel + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDataChannelRegisterObserver")] + public static extern IntPtr RegisterObserver( + IntPtr context, + IntPtr dataChannel, + IntPtr functions + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDataChannelUnregisterObserver")] + public static extern void UnregisterObserver( + IntPtr context + ); + } +} diff --git a/sdk/dotnet/webrtc/api/media_stream_interface.cs b/sdk/dotnet/webrtc/api/media_stream_interface.cs index 54aa3cb512..43319c0f23 100644 --- a/sdk/dotnet/webrtc/api/media_stream_interface.cs +++ b/sdk/dotnet/webrtc/api/media_stream_interface.cs @@ -1,535 +1,600 @@ -/* - * Copyright 2019 pixiv Inc. All Rights Reserved. - * - * Use of this source code is governed by a license that can be - * found in the LICENSE.pixiv file in the root of the source tree. - */ - -using Pixiv.Rtc; -using System; -using System.Runtime.InteropServices; - -namespace Pixiv.Webrtc -{ - public interface IAudioSourceInterface : IMediaSourceInterface - { - new IntPtr Ptr { get; } - } - - public interface IAudioTrackInterface - { - IntPtr Ptr { get; } - } - - public interface IAudioTrackSinkInterface - { - IntPtr Ptr { get; } - } - - public interface IDisposableAudioSourceInterface : - IAudioSourceInterface, IDisposableMediaSourceInterface - { - } - - public interface IDisposableAudioTrackInterface : - IAudioTrackInterface, Rtc.IDisposable - { - } - - public interface IDisposableAudioTrackSinkInterface : - IAudioTrackSinkInterface, Rtc.IDisposable - { - } - - public interface IDisposableMediaSourceInterface : - IMediaSourceInterface, Rtc.IDisposable - { - } - - public interface IDisposableMediaStreamInterface : - IMediaStreamInterface, Rtc.IDisposable - { - } - - public interface IDisposableMediaStreamTrackInterface : - IMediaStreamTrackInterface, Rtc.IDisposable - { - } - - public interface IDisposableVideoTrackSourceInterface : - IDisposableMediaSourceInterface, IVideoTrackSourceInterface - { - } - - public interface IDisposableVideoTrackInterface : - IVideoTrackInterface, IDisposableMediaStreamTrackInterface - { - } - - public interface IManagedNotifierInterface - { - void RegisterObserver(ObserverInterface observer); - void UnregisterObserver(ObserverInterface observer); - } - - public interface IManagedMediaSourceInterface : IManagedNotifierInterface - { - MediaSourceInterface.SourceState State { get; } - bool Remote { get; } - } - - public interface IManagedAudioSourceInterface : - IManagedMediaSourceInterface - { - void AddSink(AudioTrackSinkInterface sink); - void RemoveSink(AudioTrackSinkInterface sink); - } - - public interface IMediaSourceInterface - { - IntPtr Ptr { get; } - } - - public interface IMediaStreamInterface - { - IntPtr Ptr { get; } - } - - public interface IMediaStreamTrackInterface - { - IntPtr Ptr { get; } - } - - public interface IObserverInterface - { - IntPtr Ptr { get; } - } - - public interface IVideoTrackInterface : IMediaStreamTrackInterface - { - new IntPtr Ptr { get; } - } - - public interface IVideoTrackSourceInterface : IMediaSourceInterface - { - new IntPtr Ptr { get; } - } - - public sealed class DisposableMediaSourceInterface : - DisposablePtr, IDisposableMediaSourceInterface - { - IntPtr IMediaSourceInterface.Ptr => Ptr; - - public DisposableMediaSourceInterface(IntPtr ptr) - { - Ptr = ptr; - } - - private protected override void FreePtr() - { - Interop.MediaSourceInterface.Release(Ptr); - } - } - - public sealed class DisposableMediaStreamInterface : - DisposablePtr, IDisposableMediaStreamInterface - { - IntPtr IMediaStreamInterface.Ptr => Ptr; - - public DisposableMediaStreamInterface(IntPtr ptr) - { - Ptr = ptr; - } - - private protected override void FreePtr() - { - Interop.MediaStreamInterface.Release(Ptr); - } - } - - public abstract class DisposableMediaStreamTrackInterface : - DisposablePtr, IDisposableMediaStreamTrackInterface - { - IntPtr IMediaStreamTrackInterface.Ptr => Ptr; - - internal DisposableMediaStreamTrackInterface() - { - } - - private protected override void FreePtr() - { - Interop.MediaStreamTrackInterface.Release(Ptr); - } - } - - public sealed class DisposableVideoTrackInterface : - DisposableMediaStreamTrackInterface, IDisposableVideoTrackInterface - { - IntPtr IVideoTrackInterface.Ptr => - Interop.VideoTrackInterface.FromWebrtcMediaStreamTrackInterface( - ((IMediaStreamTrackInterface)this).Ptr); - - internal DisposableVideoTrackInterface(IntPtr ptr) - { - Ptr = ptr; - } - } - - public sealed class DisposableAudioTrackInterface : - DisposableMediaStreamTrackInterface, IDisposableAudioTrackInterface - { - IntPtr IAudioTrackInterface.Ptr => - Interop.AudioTrackInterface.FromWebrtcMediaStreamTrackInterface( - ((IMediaStreamTrackInterface)this).Ptr); - - internal DisposableAudioTrackInterface(IntPtr ptr) - { - Ptr = ptr; - } - } - - public sealed class DisposableAudioSourceInterface : - DisposablePtr, IDisposableAudioSourceInterface - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void DestructionHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate MediaSourceInterface.SourceState StateHandler( - IntPtr context - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate bool RemoteHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void ObserverHandler(IntPtr context, IntPtr observer); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SinkHandler(IntPtr context, IntPtr sink); - - private static readonly FunctionPtrArray s_functions = new FunctionPtrArray( - (DestructionHandler)OnDestruction, - (ObserverHandler)RegisterObserver, - (ObserverHandler)UnregisterObserver, - (StateHandler)State, - (RemoteHandler)Remote, - (SinkHandler)AddSink, - (SinkHandler)RemoveSink - ); - - [MonoPInvokeCallback(typeof(DestructionHandler))] - private static void OnDestruction(IntPtr context) - { - ((GCHandle)context).Free(); - } - - [MonoPInvokeCallback(typeof(ObserverHandler))] - private static void RegisterObserver(IntPtr context, IntPtr observer) - { - var target = ((GCHandle)context).Target; - var source = (IManagedAudioSourceInterface)target; - source.RegisterObserver(new ObserverInterface(observer)); - } - - [MonoPInvokeCallback(typeof(ObserverHandler))] - private static void UnregisterObserver(IntPtr context, IntPtr observer) - { - var target = ((GCHandle)context).Target; - var source = (IManagedAudioSourceInterface)target; - source.UnregisterObserver(new ObserverInterface(observer)); - } - - [MonoPInvokeCallback(typeof(StateHandler))] - private static MediaSourceInterface.SourceState State(IntPtr context) - { - var target = ((GCHandle)context).Target; - return ((IManagedAudioSourceInterface)target).State; - } - - [MonoPInvokeCallback(typeof(RemoteHandler))] - private static bool Remote(IntPtr context) - { - var target = ((GCHandle)context).Target; - return ((IManagedAudioSourceInterface)target).Remote; - } - - [MonoPInvokeCallback(typeof(SinkHandler))] - private static void AddSink(IntPtr context, IntPtr sinkPtr) - { - var target = ((GCHandle)context).Target; - var sink = new AudioTrackSinkInterface(sinkPtr); - ((IManagedAudioSourceInterface)target).AddSink(sink); - } - - [MonoPInvokeCallback(typeof(SinkHandler))] - private static void RemoveSink(IntPtr context, IntPtr sinkPtr) - { - var target = ((GCHandle)context).Target; - var sink = new AudioTrackSinkInterface(sinkPtr); - ((IManagedAudioSourceInterface)target).RemoveSink(sink); - } - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcNewAudioSourceInterface( - IntPtr context, - IntPtr functions - ); - - IntPtr IMediaSourceInterface.Ptr => - Interop.AudioSourceInterface.ToWebrtcMediaSourceInterface( - ((IAudioSourceInterface)this).Ptr - ); - - IntPtr IAudioSourceInterface.Ptr => Ptr; - - public DisposableAudioSourceInterface( - IManagedAudioSourceInterface managed) - { - Ptr = webrtcNewAudioSourceInterface( - (IntPtr)GCHandle.Alloc(managed), - s_functions.Ptr - ); - } - - private protected override void FreePtr() - { - Interop.MediaSourceInterface.Release( - Interop.AudioSourceInterface.ToWebrtcMediaSourceInterface( - Ptr - ) - ); - } - } - - public sealed class AudioTrackSinkInterface : IAudioTrackSinkInterface - { - public delegate void ManagedDataHandler( - IntPtr audioData, - int bitsPerSample, - int sampleRate, - int numberOfChannels, - int numberOfFrame - ); - - public IntPtr Ptr { get; } - - public AudioTrackSinkInterface(IntPtr ptr) - { - Ptr = ptr; - } - } - - public sealed class ObserverInterface : IObserverInterface - { - public IntPtr Ptr { get; } - - public ObserverInterface(IntPtr ptr) - { - Ptr = ptr; - } - } - - public static class AudioTrackSinkInterfaceExtension - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcAudioTrackSinkInterfaceOnData( - IntPtr sink, - IntPtr audioData, - int bitsPerSample, - int sampleRate, - [MarshalAs(UnmanagedType.SysUInt)] int numberOfChannels, - [MarshalAs(UnmanagedType.SysUInt)] int numberOfFrames - ); - - public static void OnData( - this IAudioTrackSinkInterface sink, - IntPtr audioData, - int bitsPerSample, - int sampleRate, - int numberOfChannels, - int numberOfFrames) - { - webrtcAudioTrackSinkInterfaceOnData( - sink.Ptr, - audioData, - bitsPerSample, - sampleRate, - numberOfChannels, - numberOfFrames - ); - } - } - - public static class MediaSourceInterface - { - public enum SourceState - { - Initializing, - Live, - Enabled, - Muted - } - } - - public static class AudioTrackInterfaceExtension - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcAudioTrackInterfaceAddSink( - IntPtr track, - IntPtr sink - ); - - public static void AddSink( - this IAudioTrackInterface track, - IAudioTrackSinkInterface sink) - { - webrtcAudioTrackInterfaceAddSink(track.Ptr, sink.Ptr); - } - } - - public static class MediaStreamTrackInterfaceExtension - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcMediaStreamTrackInterfaceId( - IntPtr ptr - ); - - public static string ID(this IMediaStreamTrackInterface track) - { - return Rtc.Interop.String.MoveToString( - webrtcMediaStreamTrackInterfaceId(track.Ptr)); - } - } - - public static class VideoTrackInterfaceExtension - { - private struct RtcVideoSinkWants - { - [MarshalAs(UnmanagedType.I1)] - public bool RotationApplied; - - [MarshalAs(UnmanagedType.I1)] - public bool BlackFrames; - - [MarshalAs(UnmanagedType.I1)] - public bool HasTargetPixelCount; - - public int MaxPixelCount; - public int TargetPixelCount; - public int MaxFramerateFps; - } - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcVideoTrackInterfaceAddOrUpdateSink( - IntPtr stream, - IntPtr sink, - in RtcVideoSinkWants wants - ); - - public static void AddOrUpdateSink( - this IVideoTrackInterface track, - IVideoSinkInterface sink, - VideoSinkWants wants) - { - var unmanagedWants = new RtcVideoSinkWants(); - - unmanagedWants.RotationApplied = wants.RotationApplied; - unmanagedWants.BlackFrames = wants.BlackFrames; - unmanagedWants.HasTargetPixelCount = wants.TargetPixelCount.HasValue; - unmanagedWants.MaxPixelCount = wants.MaxPixelCount; - unmanagedWants.MaxFramerateFps = wants.MaxFramerateFps; - - if (wants.TargetPixelCount.HasValue) - { - unmanagedWants.TargetPixelCount = (int)wants.TargetPixelCount; - } - - webrtcVideoTrackInterfaceAddOrUpdateSink( - track.Ptr, - sink.Ptr, - unmanagedWants - ); - } - } -} - -namespace Pixiv.Webrtc.Interop -{ - public static class AudioSourceInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcAudioSourceInterfaceToWebrtcMediaSourceInterface")] - public static extern IntPtr ToWebrtcMediaSourceInterface(IntPtr ptr); - } - - public static class AudioTrackInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamTrackInterfaceToWebrtcAudioTrackInterface")] - public static extern IntPtr FromWebrtcMediaStreamTrackInterface(IntPtr ptr); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcAudioTrackInterfaceToWebrtcMediaStreamTrackInterface")] - public static extern IntPtr ToWebrtcMediaStreamTrackInterface(IntPtr ptr); - } - - public static class MediaSourceInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaSourceInterfaceRelease")] - public static extern void Release(IntPtr ptr); - } - - public static class MediaStreamInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamInterfaceRelease")] - public static extern void Release(IntPtr ptr); - } - - public static class MediaStreamTrackInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcMediaStreamTrackInterfaceKAudioKind(); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcMediaStreamTrackInterfaceKVideoKind(); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcMediaStreamTrackInterfaceKind( - IntPtr ptr - ); - - public static DisposableMediaStreamTrackInterface WrapDisposable( - IntPtr ptr) - { - var kind = webrtcMediaStreamTrackInterfaceKind(ptr); - - if (kind == webrtcMediaStreamTrackInterfaceKAudioKind()) - { - return new DisposableAudioTrackInterface(ptr); - } - - if (kind == webrtcMediaStreamTrackInterfaceKVideoKind()) - { - return new DisposableVideoTrackInterface(ptr); - } - - throw new ArgumentException(nameof(ptr)); - } - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamTrackInterfaceRelease")] - public static extern void Release(IntPtr ptr); - } - - public static class VideoTrackInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamTrackInterfaceToWebrtcVideoTrackInterface")] - public static extern IntPtr FromWebrtcMediaStreamTrackInterface(IntPtr track); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcVideoTrackInterfaceToWebrtcMediaStreamTrackInterface")] - public static extern IntPtr ToWebrtcMediaStreamTrackInterface(IntPtr track); - } - - public static class VideoTrackSourceInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaSourceInterfaceToWebrtcVideoTrackSourceInterface")] - public static extern IntPtr FromWebrtcMediaSourceInterface(IntPtr ptr); - } -} +/* + * Copyright 2019 pixiv Inc. All Rights Reserved. + * + * Use of this source code is governed by a license that can be + * found in the LICENSE.pixiv file in the root of the source tree. + */ + +using Pixiv.Rtc; +using System; +using System.Runtime.InteropServices; + +namespace Pixiv.Webrtc +{ + public interface IAudioSourceInterface : IMediaSourceInterface + { + new IntPtr Ptr { get; } + } + + public interface IAudioTrackInterface + { + IntPtr Ptr { get; } + } + + public interface IAudioTrackSinkInterface + { + IntPtr Ptr { get; } + } + + public interface IDisposableAudioSourceInterface : + IAudioSourceInterface, IDisposableMediaSourceInterface + { + } + + public interface IDisposableAudioTrackInterface : + IAudioTrackInterface, Rtc.IDisposable + { + } + + public interface IDisposableAudioTrackSinkInterface : + IAudioTrackSinkInterface, Rtc.IDisposable + { + } + + public interface IDisposableMediaSourceInterface : + IMediaSourceInterface, Rtc.IDisposable + { + } + + public interface IDisposableMediaStreamInterface : + IMediaStreamInterface, Rtc.IDisposable + { + } + + public interface IDisposableMediaStreamTrackInterface : + IMediaStreamTrackInterface, Rtc.IDisposable + { + } + + public interface IDisposableVideoTrackSourceInterface : + IDisposableMediaSourceInterface, IVideoTrackSourceInterface + { + } + + public interface IDisposableVideoTrackInterface : + IVideoTrackInterface, IDisposableMediaStreamTrackInterface + { + } + + public interface IManagedNotifierInterface + { + void RegisterObserver(ObserverInterface observer); + void UnregisterObserver(ObserverInterface observer); + } + + public interface IManagedMediaSourceInterface : IManagedNotifierInterface + { + MediaSourceInterface.SourceState State { get; } + bool Remote { get; } + } + + public interface IManagedAudioSourceInterface : + IManagedMediaSourceInterface + { + void AddSink(AudioTrackSinkInterface sink); + void RemoveSink(AudioTrackSinkInterface sink); + } + + public interface IMediaSourceInterface + { + IntPtr Ptr { get; } + } + + public interface IMediaStreamInterface + { + IntPtr Ptr { get; } + } + + public interface IMediaStreamTrackInterface + { + IntPtr Ptr { get; } + } + + public interface IObserverInterface + { + IntPtr Ptr { get; } + } + + public interface IVideoTrackInterface : IMediaStreamTrackInterface + { + new IntPtr Ptr { get; } + } + + public interface IVideoTrackSourceInterface : IMediaSourceInterface + { + new IntPtr Ptr { get; } + } + + public sealed class DisposableMediaSourceInterface : + DisposablePtr, IDisposableMediaSourceInterface + { + IntPtr IMediaSourceInterface.Ptr => Ptr; + + public DisposableMediaSourceInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.MediaSourceInterface.Release(Ptr); + } + } + + public sealed class DisposableMediaStreamInterface : + DisposablePtr, IDisposableMediaStreamInterface + { + IntPtr IMediaStreamInterface.Ptr => Ptr; + + public DisposableMediaStreamInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.MediaStreamInterface.Release(Ptr); + } + } + + public abstract class DisposableMediaStreamTrackInterface : + DisposablePtr, IDisposableMediaStreamTrackInterface + { + IntPtr IMediaStreamTrackInterface.Ptr => Ptr; + + internal DisposableMediaStreamTrackInterface() + { + } + + private protected override void FreePtr() + { + Interop.MediaStreamTrackInterface.Release(Ptr); + } + } + + public sealed class DisposableVideoTrackInterface : + DisposableMediaStreamTrackInterface, IDisposableVideoTrackInterface + { + IntPtr IVideoTrackInterface.Ptr => + Interop.VideoTrackInterface.FromWebrtcMediaStreamTrackInterface( + ((IMediaStreamTrackInterface)this).Ptr); + + internal DisposableVideoTrackInterface(IntPtr ptr) + { + Ptr = ptr; + } + } + + public sealed class DisposableAudioTrackInterface : + DisposableMediaStreamTrackInterface, IDisposableAudioTrackInterface + { + IntPtr IAudioTrackInterface.Ptr => + Interop.AudioTrackInterface.FromWebrtcMediaStreamTrackInterface( + ((IMediaStreamTrackInterface)this).Ptr); + + internal DisposableAudioTrackInterface(IntPtr ptr) + { + Ptr = ptr; + } + } + + public sealed class DisposableAudioSourceInterface : + DisposablePtr, IDisposableAudioSourceInterface + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DestructionHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate MediaSourceInterface.SourceState StateHandler( + IntPtr context + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool RemoteHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void ObserverHandler(IntPtr context, IntPtr observer); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SinkHandler(IntPtr context, IntPtr sink); + + private static readonly FunctionPtrArray s_functions = new FunctionPtrArray( + (DestructionHandler)OnDestruction, + (ObserverHandler)RegisterObserver, + (ObserverHandler)UnregisterObserver, + (StateHandler)State, + (RemoteHandler)Remote, + (SinkHandler)AddSink, + (SinkHandler)RemoveSink + ); + + [MonoPInvokeCallback(typeof(DestructionHandler))] + private static void OnDestruction(IntPtr context) + { + ((GCHandle)context).Free(); + } + + [MonoPInvokeCallback(typeof(ObserverHandler))] + private static void RegisterObserver(IntPtr context, IntPtr observer) + { + var target = ((GCHandle)context).Target; + var source = (IManagedAudioSourceInterface)target; + source.RegisterObserver(new ObserverInterface(observer)); + } + + [MonoPInvokeCallback(typeof(ObserverHandler))] + private static void UnregisterObserver(IntPtr context, IntPtr observer) + { + var target = ((GCHandle)context).Target; + var source = (IManagedAudioSourceInterface)target; + source.UnregisterObserver(new ObserverInterface(observer)); + } + + [MonoPInvokeCallback(typeof(StateHandler))] + private static MediaSourceInterface.SourceState State(IntPtr context) + { + var target = ((GCHandle)context).Target; + return ((IManagedAudioSourceInterface)target).State; + } + + [MonoPInvokeCallback(typeof(RemoteHandler))] + private static bool Remote(IntPtr context) + { + var target = ((GCHandle)context).Target; + return ((IManagedAudioSourceInterface)target).Remote; + } + + [MonoPInvokeCallback(typeof(SinkHandler))] + private static void AddSink(IntPtr context, IntPtr sinkPtr) + { + var target = ((GCHandle)context).Target; + var sink = new AudioTrackSinkInterface(sinkPtr); + ((IManagedAudioSourceInterface)target).AddSink(sink); + } + + [MonoPInvokeCallback(typeof(SinkHandler))] + private static void RemoveSink(IntPtr context, IntPtr sinkPtr) + { + var target = ((GCHandle)context).Target; + var sink = new AudioTrackSinkInterface(sinkPtr); + ((IManagedAudioSourceInterface)target).RemoveSink(sink); + } + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcNewAudioSourceInterface( + IntPtr context, + IntPtr functions + ); + + IntPtr IMediaSourceInterface.Ptr => + Interop.AudioSourceInterface.ToWebrtcMediaSourceInterface( + ((IAudioSourceInterface)this).Ptr + ); + + IntPtr IAudioSourceInterface.Ptr => Ptr; + + public DisposableAudioSourceInterface( + IManagedAudioSourceInterface managed) + { + Ptr = webrtcNewAudioSourceInterface( + (IntPtr)GCHandle.Alloc(managed), + s_functions.Ptr + ); + } + + private protected override void FreePtr() + { + Interop.MediaSourceInterface.Release( + Interop.AudioSourceInterface.ToWebrtcMediaSourceInterface( + Ptr + ) + ); + } + } + + public sealed class AudioTrackSinkInterface : IAudioTrackSinkInterface + { + public delegate void ManagedDataHandler( + IntPtr audioData, + int bitsPerSample, + int sampleRate, + UIntPtr numberOfChannels, + UIntPtr numberOfFrames + ); + + public IntPtr Ptr { get; } + + public AudioTrackSinkInterface(IntPtr ptr) + { + Ptr = ptr; + } + } + + public sealed class ObserverInterface : IObserverInterface + { + public IntPtr Ptr { get; } + + public ObserverInterface(IntPtr ptr) + { + Ptr = ptr; + } + } + + public static class AudioTrackSinkInterfaceExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcAudioTrackSinkInterfaceOnData( + IntPtr sink, + IntPtr audioData, + int bitsPerSample, + int sampleRate, + UIntPtr numberOfChannels, + UIntPtr numberOfFrames + ); + + public static void OnData( + this IAudioTrackSinkInterface sink, + IntPtr audioData, + int bitsPerSample, + int sampleRate, + int numberOfChannels, + int numberOfFrames) + { + UIntPtr castedNumberOfChannels; + UIntPtr castedNumberOfFrames; + + if (sink == null) + { + throw new ArgumentNullException(nameof(sink)); + } + + try + { + castedNumberOfChannels = (UIntPtr)numberOfChannels; + } + catch (OverflowException inner) + { + throw new ArgumentOutOfRangeException(nameof(numberOfChannels), inner); + } + + try + { + castedNumberOfFrames = (UIntPtr)numberOfFrames; + } + catch (OverflowException inner) + { + throw new ArgumentOutOfRangeException(nameof(castedNumberOfFrames), inner); + } + + webrtcAudioTrackSinkInterfaceOnData( + sink.Ptr, + audioData, + bitsPerSample, + sampleRate, + castedNumberOfChannels, + castedNumberOfFrames + ); + + GC.KeepAlive(sink); + } + } + + public static class MediaSourceInterface + { + public enum SourceState + { + Initializing, + Live, + Enabled, + Muted + } + } + + public static class AudioTrackInterfaceExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcAudioTrackInterfaceAddSink( + IntPtr track, + IntPtr sink + ); + + public static void AddSink( + this IAudioTrackInterface track, + IAudioTrackSinkInterface sink) + { + if (track == null) + { + throw new ArgumentNullException(nameof(track)); + } + + if (sink == null) + { + throw new ArgumentNullException(nameof(sink)); + } + + webrtcAudioTrackInterfaceAddSink(track.Ptr, sink.Ptr); + GC.KeepAlive(track); + GC.KeepAlive(sink); + } + } + + public static class MediaStreamTrackInterfaceExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcMediaStreamTrackInterfaceId( + IntPtr ptr + ); + + public static string ID(this IMediaStreamTrackInterface track) + { + if (track == null) + { + throw new ArgumentNullException(nameof(track)); + } + + var id = webrtcMediaStreamTrackInterfaceId(track.Ptr); + GC.KeepAlive(track); + + return Rtc.Interop.String.MoveToString(id); + } + } + + public static class VideoTrackInterfaceExtension + { + private struct RtcVideoSinkWants + { + [MarshalAs(UnmanagedType.I1)] + public bool RotationApplied; + + [MarshalAs(UnmanagedType.I1)] + public bool BlackFrames; + + [MarshalAs(UnmanagedType.I1)] + public bool HasTargetPixelCount; + + public int MaxPixelCount; + public int TargetPixelCount; + public int MaxFramerateFps; + } + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcVideoTrackInterfaceAddOrUpdateSink( + IntPtr stream, + IntPtr sink, + in RtcVideoSinkWants wants + ); + + public static void AddOrUpdateSink( + this IVideoTrackInterface track, + IVideoSinkInterface sink, + VideoSinkWants wants) + { + if (track == null) + { + throw new ArgumentNullException(nameof(track)); + } + + if (sink == null) + { + throw new ArgumentNullException(nameof(sink)); + } + + if (wants == null) + { + throw new ArgumentNullException(nameof(wants)); + } + + var unmanagedWants = new RtcVideoSinkWants(); + + unmanagedWants.RotationApplied = wants.RotationApplied; + unmanagedWants.BlackFrames = wants.BlackFrames; + unmanagedWants.HasTargetPixelCount = wants.TargetPixelCount.HasValue; + unmanagedWants.MaxPixelCount = wants.MaxPixelCount; + unmanagedWants.MaxFramerateFps = wants.MaxFramerateFps; + + if (wants.TargetPixelCount.HasValue) + { + unmanagedWants.TargetPixelCount = (int)wants.TargetPixelCount; + } + + webrtcVideoTrackInterfaceAddOrUpdateSink( + track.Ptr, + sink.Ptr, + unmanagedWants + ); + + GC.KeepAlive(track); + GC.KeepAlive(sink); + } + } +} + +namespace Pixiv.Webrtc.Interop +{ + public static class AudioSourceInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcAudioSourceInterfaceToWebrtcMediaSourceInterface")] + public static extern IntPtr ToWebrtcMediaSourceInterface(IntPtr ptr); + } + + public static class AudioTrackInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamTrackInterfaceToWebrtcAudioTrackInterface")] + public static extern IntPtr FromWebrtcMediaStreamTrackInterface(IntPtr ptr); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcAudioTrackInterfaceToWebrtcMediaStreamTrackInterface")] + public static extern IntPtr ToWebrtcMediaStreamTrackInterface(IntPtr ptr); + } + + public static class MediaSourceInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaSourceInterfaceRelease")] + public static extern void Release(IntPtr ptr); + } + + public static class MediaStreamInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamInterfaceRelease")] + public static extern void Release(IntPtr ptr); + } + + public static class MediaStreamTrackInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcMediaStreamTrackInterfaceKAudioKind(); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcMediaStreamTrackInterfaceKVideoKind(); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcMediaStreamTrackInterfaceKind( + IntPtr ptr + ); + + public static DisposableMediaStreamTrackInterface WrapDisposable( + IntPtr ptr) + { + var kind = webrtcMediaStreamTrackInterfaceKind(ptr); + + if (kind == webrtcMediaStreamTrackInterfaceKAudioKind()) + { + return new DisposableAudioTrackInterface(ptr); + } + + if (kind == webrtcMediaStreamTrackInterfaceKVideoKind()) + { + return new DisposableVideoTrackInterface(ptr); + } + + throw new ArgumentException(nameof(ptr)); + } + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamTrackInterfaceRelease")] + public static extern void Release(IntPtr ptr); + } + + public static class VideoTrackInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaStreamTrackInterfaceToWebrtcVideoTrackInterface")] + public static extern IntPtr FromWebrtcMediaStreamTrackInterface(IntPtr track); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcVideoTrackInterfaceToWebrtcMediaStreamTrackInterface")] + public static extern IntPtr ToWebrtcMediaStreamTrackInterface(IntPtr track); + } + + public static class VideoTrackSourceInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcMediaSourceInterfaceToWebrtcVideoTrackSourceInterface")] + public static extern IntPtr FromWebrtcMediaSourceInterface(IntPtr ptr); + } +} diff --git a/sdk/dotnet/webrtc/api/peer_connection_interface.cs b/sdk/dotnet/webrtc/api/peer_connection_interface.cs index 5d30fb6a24..9bd625bf59 100644 --- a/sdk/dotnet/webrtc/api/peer_connection_interface.cs +++ b/sdk/dotnet/webrtc/api/peer_connection_interface.cs @@ -1,995 +1,1283 @@ -/* - * Copyright 2019 pixiv Inc. All Rights Reserved. - * - * Use of this source code is governed by a license that can be - * found in the LICENSE.pixiv file in the root of the source tree. - */ - -using Pixiv.Cricket; -using Pixiv.Rtc; -using System; -using System.Runtime.InteropServices; - -namespace Pixiv.Webrtc -{ - public enum SdpSemantics - { - PlanB, - UnifiedPlan - } - - public interface IDisposablePeerConnectionFactoryInterface : - IPeerConnectionFactoryInterface, Rtc.IDisposable - { - } - - public interface IDisposablePeerConnectionInterface : - IPeerConnectionInterface, Rtc.IDisposable - { - } - - public interface IDisposablePeerConnectionObserver : - IPeerConnectionObserver, Rtc.IDisposable - { - } - - public interface IManagedPeerConnectionObserver - { - void OnSignalingChange(PeerConnectionInterface.SignalingState newState); - void OnAddStream(DisposableMediaStreamInterface stream); - void OnRemoveStream(DisposableMediaStreamInterface stream); - void OnDataChannel(DisposableDataChannelInterface dataChannel); - void OnRenegotiationNeeded(); - void OnIceConnectionChange(PeerConnectionInterface.IceConnectionState newState); - void OnStandardizedIceConnectionChange(PeerConnectionInterface.IceConnectionState newState); - void OnConnectionChange(); - void OnIceGatheringChange(PeerConnectionInterface.IceGatheringState newState); - void OnIceCandidate(IceCandidateInterface candidate); - void OnIceCandidatesRemoved(DisposableCandidate[] candidates); - void OnIceConnectionReceivingChange(bool receiving); - void OnAddTrack(DisposableRtpReceiverInterface receiver, DisposableMediaStreamInterface[] streams); - void OnTrack(DisposableRtpTransceiverInterface transceiver); - void OnRemoveTrack(DisposableRtpReceiverInterface receiver); - void OnInterestingUsage(int usagePattern); - } - - public interface IPeerConnectionFactoryInterface - { - IntPtr Ptr { get; } - } - - public interface IPeerConnectionInterface - { - IntPtr Ptr { get; } - } - - public interface IPeerConnectionObserver - { - IntPtr Ptr { get; } - } - - public sealed class DisposablePeerConnectionFactoryInterface : - DisposablePtr, IDisposablePeerConnectionFactoryInterface - { - IntPtr IPeerConnectionFactoryInterface.Ptr => Ptr; - - public DisposablePeerConnectionFactoryInterface(IntPtr ptr) - { - Ptr = ptr; - } - - private protected override void FreePtr() - { - Interop.PeerConnectionFactoryInterface.Release(Ptr); - } - } - - public sealed class DisposablePeerConnectionInterface : - DisposablePtr, IDisposablePeerConnectionInterface - { - IntPtr IPeerConnectionInterface.Ptr => Ptr; - - public DisposablePeerConnectionInterface(IntPtr ptr) - { - Ptr = ptr; - } - - private protected override void FreePtr() - { - Interop.PeerConnectionInterface.Release(Ptr); - } - } - - public sealed class DisposablePeerConnectionObserver : - DisposablePtr, IDisposablePeerConnectionObserver - { - IntPtr IPeerConnectionObserver.Ptr => Ptr; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void DestructionHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SignalingChangeHandler( - IntPtr context, - PeerConnectionInterface.SignalingState newState - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void AddStreamHandler( - IntPtr context, - IntPtr stream - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void RemoveStreamHandler( - IntPtr context, - IntPtr stream - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void DataChannelHandler( - IntPtr context, - IntPtr dataChannel - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void RenegotiationNeededHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void IceConnectionChangeHandler( - IntPtr context, - PeerConnectionInterface.IceConnectionState newState - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void StandardizedIceConnectionChangeHandler( - IntPtr context, - PeerConnectionInterface.IceConnectionState newState - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void ConnectionChangeHandler(IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void IceGatheringChangeHandler( - IntPtr context, - PeerConnectionInterface.IceGatheringState newState - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void IceCandidateHandler( - IntPtr context, - IntPtr candidate - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void IceCandidatesRemovedHandler( - IntPtr context, - IntPtr data, - [MarshalAs(UnmanagedType.SysUInt)] int size - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void IceConnectionReceivingChangeHandler( - IntPtr context, - bool receiving - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void AddTrackHandler( - IntPtr context, - IntPtr receiver, - IntPtr data, - [MarshalAs(UnmanagedType.SysUInt)] int size - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void TrackHandler( - IntPtr context, - IntPtr transceiver - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void RemoveTrackHandler( - IntPtr context, - IntPtr receiver - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void InterestingUsageHandler( - IntPtr context, - int usagePattern - ); - - private static readonly FunctionPtrArray s_functions = new FunctionPtrArray( - (DestructionHandler)OnDestruction, - (SignalingChangeHandler)OnSignalingChange, - (AddStreamHandler)OnAddStream, - (RemoveStreamHandler)OnRemoveStream, - (DataChannelHandler)OnDataChannel, - (RenegotiationNeededHandler)OnRenegotiationNeeded, - (IceConnectionChangeHandler)OnIceConnectionChange, - (StandardizedIceConnectionChangeHandler)OnStandardizedIceConnectionChange, - (ConnectionChangeHandler)OnConnectionChange, - (IceGatheringChangeHandler)OnIceGatheringChange, - (IceCandidateHandler)OnIceCandidate, - (IceCandidatesRemovedHandler)OnIceCandidatesRemoved, - (IceConnectionReceivingChangeHandler)OnIceConnectionReceivingChange, - (AddTrackHandler)OnAddTrack, - (TrackHandler)OnTrack, - (RemoveTrackHandler)OnRemoveTrack, - (InterestingUsageHandler)OnInterestingUsage - ); - - private static IManagedPeerConnectionObserver GetContextTarget( - IntPtr context) - { - return (IManagedPeerConnectionObserver)((GCHandle)context).Target; - } - - [MonoPInvokeCallback(typeof(DestructionHandler))] - private static void OnDestruction(IntPtr context) - { - ((GCHandle)context).Free(); - } - - [MonoPInvokeCallback(typeof(SignalingChangeHandler))] - private static void OnSignalingChange( - IntPtr context, - PeerConnectionInterface.SignalingState newState) - { - GetContextTarget(context).OnSignalingChange(newState); - } - - [MonoPInvokeCallback(typeof(AddStreamHandler))] - private static void OnAddStream(IntPtr context, IntPtr stream) - { - GetContextTarget(context).OnAddStream( - new DisposableMediaStreamInterface(stream) - ); - } - - [MonoPInvokeCallback(typeof(RemoveStreamHandler))] - private static void OnRemoveStream(IntPtr context, IntPtr stream) - { - GetContextTarget(context).OnRemoveStream( - new DisposableMediaStreamInterface(stream) - ); - } - - [MonoPInvokeCallback(typeof(DataChannelHandler))] - private static void OnDataChannel(IntPtr context, IntPtr dataChannel) - { - GetContextTarget(context).OnDataChannel( - new DisposableDataChannelInterface(dataChannel) - ); - } - - [MonoPInvokeCallback(typeof(RenegotiationNeededHandler))] - private static void OnRenegotiationNeeded(IntPtr context) - { - GetContextTarget(context).OnRenegotiationNeeded(); - } - - [MonoPInvokeCallback(typeof(IceConnectionChangeHandler))] - private static void OnIceConnectionChange( - IntPtr context, - PeerConnectionInterface.IceConnectionState newState) - { - GetContextTarget(context).OnIceConnectionChange(newState); - } - - [MonoPInvokeCallback(typeof(StandardizedIceConnectionChangeHandler))] - private static void OnStandardizedIceConnectionChange( - IntPtr context, - PeerConnectionInterface.IceConnectionState newState) - { - GetContextTarget(context).OnStandardizedIceConnectionChange( - newState - ); - } - - [MonoPInvokeCallback(typeof(ConnectionChangeHandler))] - private static void OnConnectionChange(IntPtr context) - { - GetContextTarget(context).OnConnectionChange(); - } - - [MonoPInvokeCallback(typeof(IceGatheringChangeHandler))] - private static void OnIceGatheringChange( - IntPtr context, - PeerConnectionInterface.IceGatheringState newState) - { - GetContextTarget(context).OnIceGatheringChange(newState); - } - - [MonoPInvokeCallback(typeof(IceCandidateHandler))] - private static void OnIceCandidate(IntPtr context, IntPtr candidate) - { - GetContextTarget(context).OnIceCandidate( - new IceCandidateInterface(candidate) - ); - } - - [MonoPInvokeCallback(typeof(IceCandidatesRemovedHandler))] - private static void OnIceCandidatesRemoved( - IntPtr context, - IntPtr data, - int size) - { - var sizeOfIntPtr = Marshal.SizeOf(); - var candidates = new DisposableCandidate[size]; - - for (var index = 0; index < size; index++) - { - var ptr = Marshal.ReadIntPtr(data); - candidates[index] = new DisposableCandidate(ptr); - data += sizeOfIntPtr; - } - - GetContextTarget(context).OnIceCandidatesRemoved(candidates); - } - - [MonoPInvokeCallback(typeof(IceConnectionReceivingChangeHandler))] - private static void OnIceConnectionReceivingChange( - IntPtr context, - bool receiving) - { - GetContextTarget(context).OnIceConnectionReceivingChange( - receiving - ); - } - - [MonoPInvokeCallback(typeof(AddTrackHandler))] - private static void OnAddTrack( - IntPtr context, - IntPtr receiver, - IntPtr data, - int size) - { - var sizeOfIntPtr = Marshal.SizeOf(); - var streams = new DisposableMediaStreamInterface[size]; - - for (var index = 0; index < size; index++) - { - var ptr = Marshal.ReadIntPtr(data); - streams[index] = new DisposableMediaStreamInterface(ptr); - data += sizeOfIntPtr; - } - - GetContextTarget(context).OnAddTrack( - new DisposableRtpReceiverInterface(receiver), - streams - ); - } - - [MonoPInvokeCallback(typeof(TrackHandler))] - private static void OnTrack(IntPtr context, IntPtr transceiver) - { - GetContextTarget(context).OnTrack( - new DisposableRtpTransceiverInterface(transceiver) - ); - } - - [MonoPInvokeCallback(typeof(RemoveTrackHandler))] - private static void OnRemoveTrack(IntPtr context, IntPtr receiver) - { - GetContextTarget(context).OnRemoveTrack( - new DisposableRtpReceiverInterface(receiver) - ); - } - - [MonoPInvokeCallback(typeof(InterestingUsageHandler))] - private static void OnInterestingUsage(IntPtr context, int usagePattern) - { - GetContextTarget(context).OnInterestingUsage(usagePattern); - } - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcNewPeerConnectionObserver( - IntPtr context, - IntPtr functions - ); - - public DisposablePeerConnectionObserver( - IManagedPeerConnectionObserver managed) - { - Ptr = webrtcNewPeerConnectionObserver( - (IntPtr)GCHandle.Alloc(managed), - s_functions.Ptr - ); - } - - private protected override void FreePtr() - { - Interop.PeerConnectionObserver.Delete(Ptr); - } - } - - public sealed class PeerConnectionDependencies - { - public IPeerConnectionObserver Observer { get; set; } - - public IDisposablePortAllocator Allocator { get; set; } - - public IDisposableAsyncResolverFactory AsyncResolverFactory - { get; set; } - - public IDisposableRtcCertificateGeneratorInterface CertGenerator - { get; set; } - - public IDisposableSslCertificateVerifier TlsCertVerifier { get; set; } - - public IDisposableVideoBitrateAllocatorFactory VideoBitrateAllocatorFactory - { get; set; } - } - - public static class PeerConnectionFactoryInterfaceExtension - { - [Flags] - private enum WebrtcRTCConfigurationFlags - { - Dscp = 1 << 1, - CpuAdaptation = 1 << 2, - SuspendBelowMinBitrate = 1 << 3, - PrerendererSmoothing = 1 << 4, - ExperimentCpuLoadEstimator = 1 << 5 - } - - private struct WebrtcIceServer - { - public IntPtr Uri; - public IntPtr Urls; - public UIntPtr UrlsSize; - public IntPtr Username; - public IntPtr Password; - public PeerConnectionInterface.TlsCertPolicy TlsCertPolicy; - public IntPtr Hostname; - public IntPtr TlsAlpnProtocols; - public UIntPtr TlsAlpnProtocolsSize; - public IntPtr TlsEllipticCurves; - public UIntPtr TlsEllipticCurvesSize; - } - - private struct WebrtcPeerConnectionDependencies - { - public IntPtr Observer; - public IntPtr Allocator; - public IntPtr AsyncResolverFactory; - public IntPtr CertGenerator; - public IntPtr TlsCertVerifier; - public IntPtr VideoBitrateAllocatorFactory; - } - - private struct WebrtcRTCConfiguration - { - [MarshalAs(UnmanagedType.I4)] - public WebrtcRTCConfigurationFlags Flags; - public int AudioRtcpReportIntervalMs; - public int VideoRtcpReportIntervalMs; - public IntPtr Servers; - public UIntPtr ServersSize; - public PeerConnectionInterface.IceTransportsType Type; - public PeerConnectionInterface.BundlePolicy BundlePolicy; - public PeerConnectionInterface.RtcpMuxPolicy RtcpMuxPolicy; - public IntPtr Certificates; - public UIntPtr CertificatesSize; - public int IceCandidatePoolSize; - public SdpSemantics SdpSemantics; - } - - private static IntPtr StringArrayToHGlobalAnsiArrayPtr(string[] array) - { - var sizeOfIntPtr = Marshal.SizeOf(); - var ptr = Marshal.AllocHGlobal(array.Length * sizeOfIntPtr); - - for (var index = 0; index < array.Length; index++) - { - Marshal.WriteIntPtr( - ptr, - index * sizeOfIntPtr, - Marshal.StringToHGlobalAnsi(array[index]) - ); - } - - return ptr; - } - - private static void FreeHGlobalAnsiArrayPtr(IntPtr ptr, string[] array) - { - var sizeOfIntPtr = Marshal.SizeOf(); - - for (var index = 0; index < array.Length; index++) - { - Marshal.FreeHGlobal(Marshal.ReadIntPtr( - ptr, - index * sizeOfIntPtr - )); - } - - Marshal.FreeHGlobal(ptr); - } - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcPeerConnectionFactoryInterfaceCreatePeerConnection( - IntPtr factory, - in WebrtcRTCConfiguration configuration, - in WebrtcPeerConnectionDependencies dependencies); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcPeerConnectionFactoryInterfaceCreateAudioTrack( - IntPtr factory, - string label, - IntPtr source); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr webrtcPeerConnectionFactoryInterfaceCreateVideoTrack( - IntPtr factory, - string label, - IntPtr source); - - public static DisposablePeerConnectionInterface CreatePeerConnection( - this IPeerConnectionFactoryInterface factory, - PeerConnectionInterface.RtcConfiguration configuration, - PeerConnectionDependencies dependencies) - { - var servers = new WebrtcIceServer[configuration.Servers.Length]; - var sizeOfPtr = Marshal.SizeOf(); - var unmanagedConfiguration = new WebrtcRTCConfiguration(); - var unmanagedDependencies = new WebrtcPeerConnectionDependencies(); - - if (configuration.Dscp) - { - unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.Dscp; - } - - if (configuration.CpuAdaptation) - { - unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.CpuAdaptation; - } - - if (configuration.SuspendBelowMinBitrate) - { - unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.SuspendBelowMinBitrate; - } - - if (configuration.PrerendererSmoothing) - { - unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.PrerendererSmoothing; - } - - if (configuration.ExperimentCpuLoadEstimator) - { - unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.ExperimentCpuLoadEstimator; - } - - unmanagedConfiguration.AudioRtcpReportIntervalMs = configuration.AudioRtcpReportIntervalMs; - unmanagedConfiguration.VideoRtcpReportIntervalMs = configuration.VideoRtcpReportIntervalMs; - unmanagedConfiguration.ServersSize = new UIntPtr((uint)configuration.Servers.Length); - unmanagedConfiguration.Type = configuration.Type; - unmanagedConfiguration.BundlePolicy = configuration.BundlePolicy; - unmanagedConfiguration.RtcpMuxPolicy = configuration.RtcpMuxPolicy; - unmanagedConfiguration.CertificatesSize = new UIntPtr((uint)configuration.Certificates.Length); - unmanagedConfiguration.IceCandidatePoolSize = configuration.IceCandidatePoolSize; - unmanagedConfiguration.SdpSemantics = configuration.SdpSemantics; - - unmanagedDependencies.Observer = dependencies.Observer.Ptr; - - var serversHandle = GCHandle.Alloc(servers, GCHandleType.Pinned); - - for (var index = 0; index < configuration.Servers.Length; index++) - { - var server = configuration.Servers[index]; - ref var unmanagedServer = ref servers[index]; - - unmanagedServer.UrlsSize = new UIntPtr((uint)server.Urls.Length); - unmanagedServer.TlsCertPolicy = server.TlsCertPolicy; - unmanagedServer.TlsAlpnProtocolsSize = new UIntPtr((uint)server.TlsAlpnProtocols.Length); - unmanagedServer.TlsEllipticCurvesSize = new UIntPtr((uint)server.TlsEllipticCurves.Length); - - unmanagedServer.Uri = Marshal.StringToHGlobalAnsi(server.Uri); - unmanagedServer.Urls = StringArrayToHGlobalAnsiArrayPtr(server.Urls); - unmanagedServer.Username = Marshal.StringToHGlobalAnsi(server.Username); - unmanagedServer.Password = Marshal.StringToHGlobalAnsi(server.Password); - unmanagedServer.Hostname = Marshal.StringToHGlobalAnsi(server.Hostname); - unmanagedServer.TlsAlpnProtocols = StringArrayToHGlobalAnsiArrayPtr(server.TlsAlpnProtocols); - unmanagedServer.TlsEllipticCurves = StringArrayToHGlobalAnsiArrayPtr(server.TlsEllipticCurves); - } - - var certificatesCursor = Marshal.AllocHGlobal(sizeOfPtr * configuration.Certificates.Length); - unmanagedConfiguration.Certificates = certificatesCursor; - - try - { - if (dependencies.Allocator != null) - { - unmanagedDependencies.Allocator = dependencies.Allocator.Ptr; - dependencies.Allocator.ReleasePtr(); - } - - if (dependencies.AsyncResolverFactory != null) - { - unmanagedDependencies.AsyncResolverFactory = dependencies.AsyncResolverFactory.Ptr; - dependencies.AsyncResolverFactory.ReleasePtr(); - } - - if (dependencies.CertGenerator != null) - { - unmanagedDependencies.CertGenerator = dependencies.CertGenerator.Ptr; - dependencies.CertGenerator.ReleasePtr(); - } - - if (dependencies.TlsCertVerifier != null) - { - unmanagedDependencies.TlsCertVerifier = dependencies.TlsCertVerifier.Ptr; - dependencies.TlsCertVerifier.ReleasePtr(); - } - - if (dependencies.VideoBitrateAllocatorFactory != null) - { - unmanagedDependencies.VideoBitrateAllocatorFactory = dependencies.VideoBitrateAllocatorFactory.Ptr; - dependencies.VideoBitrateAllocatorFactory.ReleasePtr(); - } - - unmanagedConfiguration.Servers = serversHandle.AddrOfPinnedObject(); - - foreach (var certificate in configuration.Certificates) - { - Marshal.WriteIntPtr(certificatesCursor, certificate.Ptr); - certificatesCursor += sizeOfPtr; - } - - return new DisposablePeerConnectionInterface( - webrtcPeerConnectionFactoryInterfaceCreatePeerConnection( - factory.Ptr, - unmanagedConfiguration, - unmanagedDependencies - ) - ); - } - finally - { - for (var index = 0; index < configuration.Servers.Length; index++) - { - var server = configuration.Servers[index]; - ref var unmanagedServer = ref servers[index]; - - FreeHGlobalAnsiArrayPtr( - unmanagedServer.TlsEllipticCurves, - server.TlsEllipticCurves - ); - - FreeHGlobalAnsiArrayPtr( - unmanagedServer.TlsAlpnProtocols, - server.TlsAlpnProtocols - ); - - FreeHGlobalAnsiArrayPtr( - unmanagedServer.Urls, - server.Urls - ); - - Marshal.FreeHGlobal(unmanagedServer.Hostname); - Marshal.FreeHGlobal(unmanagedServer.Password); - Marshal.FreeHGlobal(unmanagedServer.Username); - Marshal.FreeHGlobal(unmanagedServer.Uri); - } - - Marshal.FreeHGlobal(unmanagedConfiguration.Certificates); - serversHandle.Free(); - } - } - - public static DisposableAudioTrackInterface CreateAudioTrack( - this IPeerConnectionFactoryInterface factory, - string label, - IAudioSourceInterface source) - { - return new DisposableAudioTrackInterface( - Interop.AudioTrackInterface.ToWebrtcMediaStreamTrackInterface( - webrtcPeerConnectionFactoryInterfaceCreateAudioTrack( - factory.Ptr, - label, - source.Ptr - ) - ) - ); - } - - public static DisposableVideoTrackInterface CreateVideoTrack( - this IPeerConnectionFactoryInterface factory, - string label, - IVideoTrackSourceInterface source) - { - return new DisposableVideoTrackInterface( - Interop.VideoTrackInterface.ToWebrtcMediaStreamTrackInterface( - webrtcPeerConnectionFactoryInterfaceCreateVideoTrack( - factory.Ptr, - label, - source.Ptr - ) - ) - ); - } - } - - public static class PeerConnectionInterface - { - public enum SignalingState - { - Stable, - HaveLocalOffer, - HaveLocalPrAnswer, - HaveRemoteOffer, - HaveRemotePrAnswer, - Closed, - } - - public enum IceGatheringState - { - New, - Gathering, - Complete - } - - public enum IceConnectionState - { - New, - Checking, - Connected, - Completed, - Failed, - Disconnected, - Closed, - Max, - } - - public enum IceTransportsType - { - None, - Relay, - NoHost, - All - } - - public enum BundlePolicy - { - Balanced, - MaxBundle, - MaxCompat - } - - public enum RtcpMuxPolicy - { - Negotiate, - Require, - } - - public enum TlsCertPolicy - { - Secure, - InsecureNoCheck - } - - public sealed class IceServer - { - public string Uri { get; set; } - public string[] Urls { get; set; } = new string[0]; - public string Username { get; set; } - public string Password { get; set; } - public TlsCertPolicy TlsCertPolicy { get; set; } = - TlsCertPolicy.Secure; - public string Hostname { get; set; } - public string[] TlsAlpnProtocols { get; set; } = new string[0]; - public string[] TlsEllipticCurves { get; set; } = new string[0]; - } - - public sealed class RtcConfiguration - { - public bool Dscp { get; set; } - public bool CpuAdaptation { get; set; } - public bool SuspendBelowMinBitrate { get; set; } - public bool PrerendererSmoothing { get; set; } - public bool ExperimentCpuLoadEstimator { get; set; } - public int AudioRtcpReportIntervalMs { get; set; } = 5000; - public int VideoRtcpReportIntervalMs { get; set; } = 1000; - public IceServer[] Servers { get; set; } = new IceServer[0]; - public IceTransportsType Type { get; set; } = IceTransportsType.All; - public BundlePolicy BundlePolicy { get; set; } = - BundlePolicy.Balanced; - public RtcpMuxPolicy RtcpMuxPolicy { get; set; } = - RtcpMuxPolicy.Require; - public IRtcCertificate[] Certificates { get; set; } = - new IRtcCertificate[0]; - public int IceCandidatePoolSize { get; set; } = 0; - public SdpSemantics SdpSemantics; - } - - [StructLayout(LayoutKind.Sequential)] - public sealed class RtcOfferAnswerOptions - { - public const int Undefined = -1; - public const int MaxOfferToReceiveMedia = 1; - public const int OfferToReceiveMediaTrue = 1; - - public int OfferToReceiveVideo { get; set; } = Undefined; - public int OfferToReceiveAudio { get; set; } = Undefined; - - [field: MarshalAs(UnmanagedType.I1)] - public bool VoiceActivityDetection { get; set; } = true; - - [field: MarshalAs(UnmanagedType.I1)] - public bool IceRestart { get; set; } = false; - - [field: MarshalAs(UnmanagedType.I1)] - public bool UseRtpMux { get; set; } = true; - - [field: MarshalAs(UnmanagedType.I1)] - public bool RawPacketizationForVideo { get; set; } = false; - - public int NumSimulcastLayers { get; set; } = 1; - - [field: MarshalAs(UnmanagedType.I1)] - public bool UseObsoleteSctpSdp = false; - } - } - - public static class PeerConnectionInterfaceExtension - { - private readonly struct WebrtcAddTrackResult - { -#pragma warning disable 0649 - public readonly IntPtr Error; - public readonly IntPtr Value; -#pragma warning restore 0649 - } - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern WebrtcAddTrackResult webrtcPeerConnectionInterfaceAddTrack( - IntPtr connection, - IntPtr track, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] string[] data, - [MarshalAs(UnmanagedType.SysUInt)] int size - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcPeerConnectionInterfaceCreateAnswer( - IntPtr connection, - IntPtr observer, - PeerConnectionInterface.RtcOfferAnswerOptions options - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcPeerConnectionInterfaceCreateOffer( - IntPtr connection, - IntPtr observer, - PeerConnectionInterface.RtcOfferAnswerOptions options - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcPeerConnectionInterfaceClose( - IntPtr connection - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcPeerConnectionInterfaceSetAudioRecording( - IntPtr connection, - [MarshalAs(UnmanagedType.I1)] bool recording - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcPeerConnectionInterfaceSetLocalDescription( - IntPtr connection, - IntPtr observer, - IntPtr desc - ); - - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] - private static extern void webrtcPeerConnectionInterfaceSetRemoteDescription( - IntPtr connection, - IntPtr observer, - IntPtr desc - ); - - public static RtcErrorOr AddTrack( - this IPeerConnectionInterface connection, - IMediaStreamTrackInterface track, - string[] streamIds) - { - RtcError error; - - var result = webrtcPeerConnectionInterfaceAddTrack( - connection.Ptr, track.Ptr, streamIds, streamIds.Length); - - try - { - error = new RtcError(result.Error); - } - finally - { - Interop.RtcError.Delete(result.Error); - } - - var value = error.Type == RtcErrorType.None ? - new DisposableRtpSenderInterface(result.Value) : null; - - return new RtcErrorOr(error, value); - } - - public static void Close(this IPeerConnectionInterface connection) - { - webrtcPeerConnectionInterfaceClose(connection.Ptr); - } - - public static void CreateAnswer( - this IPeerConnectionInterface connection, - ICreateSessionDescriptionObserver observer, - PeerConnectionInterface.RtcOfferAnswerOptions options) - { - webrtcPeerConnectionInterfaceCreateAnswer( - connection.Ptr, observer.Ptr, options); - } - - public static void CreateOffer( - this IPeerConnectionInterface connection, - ICreateSessionDescriptionObserver observer, - PeerConnectionInterface.RtcOfferAnswerOptions options) - { - webrtcPeerConnectionInterfaceCreateOffer( - connection.Ptr, observer.Ptr, options); - } - - public static void SetAudioRecording( - this IPeerConnectionInterface connection, - bool recording) - { - webrtcPeerConnectionInterfaceSetAudioRecording( - connection.Ptr, - recording - ); - } - - public static void SetLocalDescription( - this IPeerConnectionInterface connection, - ISetSessionDescriptionObserver observer, - IDisposableSessionDescriptionInterface desc) - { - webrtcPeerConnectionInterfaceSetLocalDescription( - connection.Ptr, observer.Ptr, desc.Ptr); - - desc.ReleasePtr(); - } - - public static void SetRemoteDescription( - this IPeerConnectionInterface connection, - ISetSessionDescriptionObserver observer, - IDisposableSessionDescriptionInterface desc) - { - webrtcPeerConnectionInterfaceSetRemoteDescription( - connection.Ptr, observer.Ptr, desc.Ptr); - - desc.ReleasePtr(); - } - } -} - -namespace Pixiv.Webrtc.Interop -{ - public static class PeerConnectionFactoryInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcPeerConnectionFactoryInterfaceRelease")] - public static extern void Release(IntPtr factory); - } - - public static class PeerConnectionInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcPeerConnectionInterfaceRelease")] - public static extern void Release(IntPtr ptr); - } - - public static class PeerConnectionObserver - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDeletePeerConnectionObserver")] - public static extern void Delete(IntPtr observer); - } -} +/* + * Copyright 2019 pixiv Inc. All Rights Reserved. + * + * Use of this source code is governed by a license that can be + * found in the LICENSE.pixiv file in the root of the source tree. + */ + +using Pixiv.Cricket; +using Pixiv.Rtc; +using System; +using System.Runtime.InteropServices; + +namespace Pixiv.Webrtc +{ + public enum SdpSemantics + { + PlanB, + UnifiedPlan + } + + public interface IDisposablePeerConnectionFactoryInterface : + IPeerConnectionFactoryInterface, Rtc.IDisposable + { + } + + public interface IDisposablePeerConnectionInterface : + IPeerConnectionInterface, Rtc.IDisposable + { + } + + public interface IDisposablePeerConnectionObserver : + IPeerConnectionObserver, Rtc.IDisposable + { + } + + public interface IManagedPeerConnectionObserver + { + void OnSignalingChange(PeerConnectionInterface.SignalingState newState); + void OnAddStream(DisposableMediaStreamInterface stream); + void OnRemoveStream(DisposableMediaStreamInterface stream); + void OnDataChannel(DisposableDataChannelInterface dataChannel); + void OnRenegotiationNeeded(); + void OnIceConnectionChange(PeerConnectionInterface.IceConnectionState newState); + void OnStandardizedIceConnectionChange(PeerConnectionInterface.IceConnectionState newState); + void OnConnectionChange(); + void OnIceGatheringChange(PeerConnectionInterface.IceGatheringState newState); + void OnIceCandidate(IceCandidateInterface candidate); + void OnIceCandidatesRemoved(DisposableCandidate[] candidates); + void OnIceConnectionReceivingChange(bool receiving); + void OnAddTrack(DisposableRtpReceiverInterface receiver, DisposableMediaStreamInterface[] streams); + void OnTrack(DisposableRtpTransceiverInterface transceiver); + void OnRemoveTrack(DisposableRtpReceiverInterface receiver); + void OnInterestingUsage(int usagePattern); + } + + public interface IPeerConnectionFactoryInterface + { + IntPtr Ptr { get; } + } + + public interface IPeerConnectionInterface + { + IntPtr Ptr { get; } + } + + public interface IPeerConnectionObserver + { + IntPtr Ptr { get; } + } + + public sealed class DisposablePeerConnectionFactoryInterface : + DisposablePtr, IDisposablePeerConnectionFactoryInterface + { + IntPtr IPeerConnectionFactoryInterface.Ptr => Ptr; + + public DisposablePeerConnectionFactoryInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.PeerConnectionFactoryInterface.Release(Ptr); + } + } + + public sealed class DisposablePeerConnectionInterface : + DisposablePtr, IDisposablePeerConnectionInterface + { + IntPtr IPeerConnectionInterface.Ptr => Ptr; + + public DisposablePeerConnectionInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.PeerConnectionInterface.Release(Ptr); + } + } + + public sealed class DisposablePeerConnectionObserver : + DisposablePtr, IDisposablePeerConnectionObserver + { + IntPtr IPeerConnectionObserver.Ptr => Ptr; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DestructionHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void SignalingChangeHandler( + IntPtr context, + PeerConnectionInterface.SignalingState newState + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void AddStreamHandler( + IntPtr context, + IntPtr stream + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void RemoveStreamHandler( + IntPtr context, + IntPtr stream + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DataChannelHandler( + IntPtr context, + IntPtr dataChannel + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void RenegotiationNeededHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IceConnectionChangeHandler( + IntPtr context, + PeerConnectionInterface.IceConnectionState newState + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void StandardizedIceConnectionChangeHandler( + IntPtr context, + PeerConnectionInterface.IceConnectionState newState + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void ConnectionChangeHandler(IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IceGatheringChangeHandler( + IntPtr context, + PeerConnectionInterface.IceGatheringState newState + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IceCandidateHandler( + IntPtr context, + IntPtr candidate + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IceCandidatesRemovedHandler( + IntPtr context, + IntPtr data, + UIntPtr size + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IceConnectionReceivingChangeHandler( + IntPtr context, + bool receiving + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void AddTrackHandler( + IntPtr context, + IntPtr receiver, + IntPtr data, + UIntPtr size + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void TrackHandler( + IntPtr context, + IntPtr transceiver + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void RemoveTrackHandler( + IntPtr context, + IntPtr receiver + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void InterestingUsageHandler( + IntPtr context, + int usagePattern + ); + + private static readonly FunctionPtrArray s_functions = new FunctionPtrArray( + (DestructionHandler)OnDestruction, + (SignalingChangeHandler)OnSignalingChange, + (AddStreamHandler)OnAddStream, + (RemoveStreamHandler)OnRemoveStream, + (DataChannelHandler)OnDataChannel, + (RenegotiationNeededHandler)OnRenegotiationNeeded, + (IceConnectionChangeHandler)OnIceConnectionChange, + (StandardizedIceConnectionChangeHandler)OnStandardizedIceConnectionChange, + (ConnectionChangeHandler)OnConnectionChange, + (IceGatheringChangeHandler)OnIceGatheringChange, + (IceCandidateHandler)OnIceCandidate, + (IceCandidatesRemovedHandler)OnIceCandidatesRemoved, + (IceConnectionReceivingChangeHandler)OnIceConnectionReceivingChange, + (AddTrackHandler)OnAddTrack, + (TrackHandler)OnTrack, + (RemoveTrackHandler)OnRemoveTrack, + (InterestingUsageHandler)OnInterestingUsage + ); + + private static IManagedPeerConnectionObserver GetContextTarget( + IntPtr context) + { + return (IManagedPeerConnectionObserver)((GCHandle)context).Target; + } + + [MonoPInvokeCallback(typeof(DestructionHandler))] + private static void OnDestruction(IntPtr context) + { + ((GCHandle)context).Free(); + } + + [MonoPInvokeCallback(typeof(SignalingChangeHandler))] + private static void OnSignalingChange( + IntPtr context, + PeerConnectionInterface.SignalingState newState) + { + GetContextTarget(context).OnSignalingChange(newState); + } + + [MonoPInvokeCallback(typeof(AddStreamHandler))] + private static void OnAddStream(IntPtr context, IntPtr stream) + { + GetContextTarget(context).OnAddStream( + new DisposableMediaStreamInterface(stream) + ); + } + + [MonoPInvokeCallback(typeof(RemoveStreamHandler))] + private static void OnRemoveStream(IntPtr context, IntPtr stream) + { + GetContextTarget(context).OnRemoveStream( + new DisposableMediaStreamInterface(stream) + ); + } + + [MonoPInvokeCallback(typeof(DataChannelHandler))] + private static void OnDataChannel(IntPtr context, IntPtr dataChannel) + { + GetContextTarget(context).OnDataChannel( + new DisposableDataChannelInterface(dataChannel) + ); + } + + [MonoPInvokeCallback(typeof(RenegotiationNeededHandler))] + private static void OnRenegotiationNeeded(IntPtr context) + { + GetContextTarget(context).OnRenegotiationNeeded(); + } + + [MonoPInvokeCallback(typeof(IceConnectionChangeHandler))] + private static void OnIceConnectionChange( + IntPtr context, + PeerConnectionInterface.IceConnectionState newState) + { + GetContextTarget(context).OnIceConnectionChange(newState); + } + + [MonoPInvokeCallback(typeof(StandardizedIceConnectionChangeHandler))] + private static void OnStandardizedIceConnectionChange( + IntPtr context, + PeerConnectionInterface.IceConnectionState newState) + { + GetContextTarget(context).OnStandardizedIceConnectionChange( + newState + ); + } + + [MonoPInvokeCallback(typeof(ConnectionChangeHandler))] + private static void OnConnectionChange(IntPtr context) + { + GetContextTarget(context).OnConnectionChange(); + } + + [MonoPInvokeCallback(typeof(IceGatheringChangeHandler))] + private static void OnIceGatheringChange( + IntPtr context, + PeerConnectionInterface.IceGatheringState newState) + { + GetContextTarget(context).OnIceGatheringChange(newState); + } + + [MonoPInvokeCallback(typeof(IceCandidateHandler))] + private static void OnIceCandidate(IntPtr context, IntPtr candidate) + { + GetContextTarget(context).OnIceCandidate( + new IceCandidateInterface(candidate) + ); + } + + [MonoPInvokeCallback(typeof(IceCandidatesRemovedHandler))] + private static void OnIceCandidatesRemoved( + IntPtr context, + IntPtr data, + UIntPtr nativeSize) + { + var size = (ulong)nativeSize; + var sizeOfIntPtr = Marshal.SizeOf(); + var candidates = new DisposableCandidate[size]; + + for (ulong index = 0; index < size; index++) + { + var ptr = Marshal.ReadIntPtr(data); + candidates[index] = new DisposableCandidate(ptr); + data += sizeOfIntPtr; + } + + GetContextTarget(context).OnIceCandidatesRemoved(candidates); + } + + [MonoPInvokeCallback(typeof(IceConnectionReceivingChangeHandler))] + private static void OnIceConnectionReceivingChange( + IntPtr context, + bool receiving) + { + GetContextTarget(context).OnIceConnectionReceivingChange( + receiving + ); + } + + [MonoPInvokeCallback(typeof(AddTrackHandler))] + private static void OnAddTrack( + IntPtr context, + IntPtr receiver, + IntPtr data, + UIntPtr nativeSize) + { + var size = (ulong)nativeSize; + var sizeOfIntPtr = Marshal.SizeOf(); + var streams = new DisposableMediaStreamInterface[size]; + + for (ulong index = 0; index < size; index++) + { + var ptr = Marshal.ReadIntPtr(data); + streams[index] = new DisposableMediaStreamInterface(ptr); + data += sizeOfIntPtr; + } + + GetContextTarget(context).OnAddTrack( + new DisposableRtpReceiverInterface(receiver), + streams + ); + } + + [MonoPInvokeCallback(typeof(TrackHandler))] + private static void OnTrack(IntPtr context, IntPtr transceiver) + { + GetContextTarget(context).OnTrack( + new DisposableRtpTransceiverInterface(transceiver) + ); + } + + [MonoPInvokeCallback(typeof(RemoveTrackHandler))] + private static void OnRemoveTrack(IntPtr context, IntPtr receiver) + { + GetContextTarget(context).OnRemoveTrack( + new DisposableRtpReceiverInterface(receiver) + ); + } + + [MonoPInvokeCallback(typeof(InterestingUsageHandler))] + private static void OnInterestingUsage(IntPtr context, int usagePattern) + { + GetContextTarget(context).OnInterestingUsage(usagePattern); + } + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcNewPeerConnectionObserver( + IntPtr context, + IntPtr functions + ); + + public DisposablePeerConnectionObserver( + IManagedPeerConnectionObserver managed) + { + try + { + Ptr = webrtcNewPeerConnectionObserver( + (IntPtr)GCHandle.Alloc(managed), + s_functions.Ptr + ); + } + catch (Exception ex) + { + var m = ex.Message; + } + } + + private protected override void FreePtr() + { + Interop.PeerConnectionObserver.Delete(Ptr); + } + } + + public sealed class PeerConnectionDependencies + { + public IPeerConnectionObserver Observer { get; set; } + + public IDisposablePortAllocator Allocator { get; set; } + + public IDisposableAsyncResolverFactory AsyncResolverFactory + { get; set; } + + public IDisposableRtcCertificateGeneratorInterface CertGenerator + { get; set; } + + public IDisposableSslCertificateVerifier TlsCertVerifier { get; set; } + + public IDisposableVideoBitrateAllocatorFactory VideoBitrateAllocatorFactory + { get; set; } + } + + public static class PeerConnectionFactoryInterfaceExtension + { + [Flags] + private enum WebrtcRTCConfigurationFlags + { + Dscp = 1 << 1, + CpuAdaptation = 1 << 2, + SuspendBelowMinBitrate = 1 << 3, + PrerendererSmoothing = 1 << 4, + ExperimentCpuLoadEstimator = 1 << 5, + OverrideEnableDtlsSrtp = 1 << 6, + EnableDtlsSrtp = 1 << 7, + } + + private struct WebrtcIceServer + { + public IntPtr Uri; + public IntPtr Urls; + public UIntPtr UrlsSize; + public IntPtr Username; + public IntPtr Password; + public PeerConnectionInterface.TlsCertPolicy TlsCertPolicy; + public IntPtr Hostname; + public IntPtr TlsAlpnProtocols; + public UIntPtr TlsAlpnProtocolsSize; + public IntPtr TlsEllipticCurves; + public UIntPtr TlsEllipticCurvesSize; + } + + private struct WebrtcPeerConnectionDependencies + { + public IntPtr Observer; + public IntPtr Allocator; + public IntPtr AsyncResolverFactory; + public IntPtr CertGenerator; + public IntPtr TlsCertVerifier; + public IntPtr VideoBitrateAllocatorFactory; + } + + private struct WebrtcRTCConfiguration + { + [MarshalAs(UnmanagedType.I4)] + public WebrtcRTCConfigurationFlags Flags; + public int AudioRtcpReportIntervalMs; + public int VideoRtcpReportIntervalMs; + public IntPtr Servers; + public UIntPtr ServersSize; + public PeerConnectionInterface.IceTransportsType Type; + public PeerConnectionInterface.BundlePolicy BundlePolicy; + public PeerConnectionInterface.RtcpMuxPolicy RtcpMuxPolicy; + public IntPtr Certificates; + public UIntPtr CertificatesSize; + public int IceCandidatePoolSize; + public SdpSemantics SdpSemantics; + } + + private static IntPtr StringArrayToHGlobalAnsiArrayPtr(string[] array) + { + var sizeOfIntPtr = Marshal.SizeOf(); + var ptr = Marshal.AllocHGlobal(array.Length * sizeOfIntPtr); + + for (var index = 0; index < array.Length; index++) + { + Marshal.WriteIntPtr( + ptr, + index * sizeOfIntPtr, + Marshal.StringToHGlobalAnsi(array[index]) + ); + } + + return ptr; + } + + private static void FreeHGlobalAnsiArrayPtr(IntPtr ptr, string[] array) + { + var sizeOfIntPtr = Marshal.SizeOf(); + + for (var index = 0; index < array.Length; index++) + { + Marshal.FreeHGlobal(Marshal.ReadIntPtr( + ptr, + index * sizeOfIntPtr + )); + } + + Marshal.FreeHGlobal(ptr); + } + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcPeerConnectionFactoryInterfaceCreatePeerConnection( + IntPtr factory, + in WebrtcRTCConfiguration configuration, + in WebrtcPeerConnectionDependencies dependencies); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcPeerConnectionFactoryInterfaceCreateAudioTrack( + IntPtr factory, + string label, + IntPtr source); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcPeerConnectionFactoryInterfaceCreateVideoTrack( + IntPtr factory, + string label, + IntPtr source); + + public static DisposablePeerConnectionInterface CreatePeerConnection( + this IPeerConnectionFactoryInterface factory, + PeerConnectionInterface.RtcConfiguration configuration, + PeerConnectionDependencies dependencies) + { + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (configuration.Servers == null) + { + throw new ArgumentException("Servers property is null", nameof(configuration)); + } + + if (configuration.Certificates == null) + { + throw new ArgumentException("Certificates property is null", nameof(configuration)); + } + + if (dependencies == null) + { + throw new ArgumentNullException(nameof(dependencies)); + } + + var servers = new WebrtcIceServer[configuration.Servers.LongLength]; + var sizeOfPtr = Marshal.SizeOf(); + var unmanagedConfiguration = new WebrtcRTCConfiguration(); + var unmanagedDependencies = new WebrtcPeerConnectionDependencies(); + + if (configuration.Dscp) + { + unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.Dscp; + } + + if (configuration.CpuAdaptation) + { + unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.CpuAdaptation; + } + + if (configuration.SuspendBelowMinBitrate) + { + unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.SuspendBelowMinBitrate; + } + + if (configuration.PrerendererSmoothing) + { + unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.PrerendererSmoothing; + } + + if (configuration.ExperimentCpuLoadEstimator) + { + unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.ExperimentCpuLoadEstimator; + } + + if (configuration.EnableDtlsSrtp.HasValue) + { + unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.OverrideEnableDtlsSrtp; + + if (configuration.EnableDtlsSrtp.Value) + { + unmanagedConfiguration.Flags |= WebrtcRTCConfigurationFlags.EnableDtlsSrtp; + } + } + + unmanagedConfiguration.AudioRtcpReportIntervalMs = configuration.AudioRtcpReportIntervalMs; + unmanagedConfiguration.VideoRtcpReportIntervalMs = configuration.VideoRtcpReportIntervalMs; + unmanagedConfiguration.ServersSize = new UIntPtr((uint)configuration.Servers.Length); + unmanagedConfiguration.Type = configuration.Type; + unmanagedConfiguration.BundlePolicy = configuration.BundlePolicy; + unmanagedConfiguration.RtcpMuxPolicy = configuration.RtcpMuxPolicy; + unmanagedConfiguration.CertificatesSize = new UIntPtr((uint)configuration.Certificates.Length); + unmanagedConfiguration.IceCandidatePoolSize = configuration.IceCandidatePoolSize; + unmanagedConfiguration.SdpSemantics = configuration.SdpSemantics; + + unmanagedDependencies.Observer = dependencies.Observer.Ptr; + + var serversHandle = GCHandle.Alloc(servers, GCHandleType.Pinned); + + var certificatesCursor = Marshal.AllocHGlobal(sizeOfPtr * configuration.Certificates.Length); + unmanagedConfiguration.Certificates = certificatesCursor; + + try + { + for (var index = 0; index < configuration.Servers.Length; index++) + { + var server = configuration.Servers[index]; + ref var unmanagedServer = ref servers[index]; + + if (server.Urls == null) + { + throw new ArgumentException("Urls property of an element of Server property is null", nameof(configuration)); + } + + if (server.TlsAlpnProtocols == null) + { + throw new ArgumentException("TlsAlpnProtocols property of an element of Server property is null", nameof(configuration)); + } + + unmanagedServer.UrlsSize = new UIntPtr((ulong)server.Urls.LongLength); + unmanagedServer.TlsCertPolicy = server.TlsCertPolicy; + unmanagedServer.TlsAlpnProtocolsSize = new UIntPtr((uint)server.TlsAlpnProtocols.Length); + unmanagedServer.TlsEllipticCurvesSize = new UIntPtr((uint)server.TlsEllipticCurves.Length); + + unmanagedServer.Uri = Marshal.StringToHGlobalAnsi(server.Uri); + unmanagedServer.Urls = StringArrayToHGlobalAnsiArrayPtr(server.Urls); + unmanagedServer.Username = Marshal.StringToHGlobalAnsi(server.Username); + unmanagedServer.Password = Marshal.StringToHGlobalAnsi(server.Password); + unmanagedServer.Hostname = Marshal.StringToHGlobalAnsi(server.Hostname); + unmanagedServer.TlsAlpnProtocols = StringArrayToHGlobalAnsiArrayPtr(server.TlsAlpnProtocols); + unmanagedServer.TlsEllipticCurves = StringArrayToHGlobalAnsiArrayPtr(server.TlsEllipticCurves); + } + + + if (dependencies.Allocator != null) + { + unmanagedDependencies.Allocator = dependencies.Allocator.Ptr; + dependencies.Allocator.ReleasePtr(); + } + + if (dependencies.AsyncResolverFactory != null) + { + unmanagedDependencies.AsyncResolverFactory = dependencies.AsyncResolverFactory.Ptr; + dependencies.AsyncResolverFactory.ReleasePtr(); + } + + if (dependencies.CertGenerator != null) + { + unmanagedDependencies.CertGenerator = dependencies.CertGenerator.Ptr; + dependencies.CertGenerator.ReleasePtr(); + } + + if (dependencies.TlsCertVerifier != null) + { + unmanagedDependencies.TlsCertVerifier = dependencies.TlsCertVerifier.Ptr; + dependencies.TlsCertVerifier.ReleasePtr(); + } + + if (dependencies.VideoBitrateAllocatorFactory != null) + { + unmanagedDependencies.VideoBitrateAllocatorFactory = dependencies.VideoBitrateAllocatorFactory.Ptr; + dependencies.VideoBitrateAllocatorFactory.ReleasePtr(); + } + + unmanagedConfiguration.Servers = serversHandle.AddrOfPinnedObject(); + + foreach (var certificate in configuration.Certificates) + { + if (certificate == null) + { + throw new ArgumentException("An element of Certificates property is null", nameof(configuration)); + } + Marshal.WriteIntPtr(certificatesCursor, certificate.Ptr); + certificatesCursor += sizeOfPtr; + } + + var connection = webrtcPeerConnectionFactoryInterfaceCreatePeerConnection( + factory.Ptr, + unmanagedConfiguration, + unmanagedDependencies + ); + + GC.KeepAlive(factory); + GC.KeepAlive(configuration); + + return new DisposablePeerConnectionInterface(connection); + } + finally + { + for (var index = 0; index < configuration.Servers.Length; index++) + { + var server = configuration.Servers[index]; + ref var unmanagedServer = ref servers[index]; + if (unmanagedServer.TlsEllipticCurves != IntPtr.Zero) + { + FreeHGlobalAnsiArrayPtr( + unmanagedServer.TlsEllipticCurves, + server.TlsEllipticCurves + ); + } + + if (unmanagedServer.TlsAlpnProtocols != IntPtr.Zero) + { + FreeHGlobalAnsiArrayPtr( + unmanagedServer.TlsAlpnProtocols, + server.TlsAlpnProtocols + ); + } + + if (unmanagedServer.Urls != IntPtr.Zero) + { + FreeHGlobalAnsiArrayPtr( + unmanagedServer.Urls, + server.Urls + ); + } + + if (unmanagedServer.Hostname != IntPtr.Zero) + { + Marshal.FreeHGlobal(unmanagedServer.Hostname); + } + + if (unmanagedServer.Password != IntPtr.Zero) + { + Marshal.FreeHGlobal(unmanagedServer.Password); + } + + if (unmanagedServer.Username != IntPtr.Zero) + { + Marshal.FreeHGlobal(unmanagedServer.Username); + } + + if (unmanagedServer.Uri != null) + { + Marshal.FreeHGlobal(unmanagedServer.Uri); + } + + } + + Marshal.FreeHGlobal(unmanagedConfiguration.Certificates); + serversHandle.Free(); + } + } + + public static DisposableAudioTrackInterface CreateAudioTrack( + this IPeerConnectionFactoryInterface factory, + string label, + IAudioSourceInterface source) + { + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + var track = webrtcPeerConnectionFactoryInterfaceCreateAudioTrack( + factory.Ptr, + label, + source.Ptr + ); + + GC.KeepAlive(factory); + GC.KeepAlive(source); + + return new DisposableAudioTrackInterface( + Interop.AudioTrackInterface.ToWebrtcMediaStreamTrackInterface( + track + ) + ); + } + + public static DisposableVideoTrackInterface CreateVideoTrack( + this IPeerConnectionFactoryInterface factory, + string label, + IVideoTrackSourceInterface source) + { + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + var track = webrtcPeerConnectionFactoryInterfaceCreateVideoTrack( + factory.Ptr, + label, + source.Ptr + ); + + GC.KeepAlive(factory); + GC.KeepAlive(source); + return new DisposableVideoTrackInterface( + Interop.VideoTrackInterface.ToWebrtcMediaStreamTrackInterface( + track + ) + ); + } + } + + public static class PeerConnectionInterface + { + public enum SignalingState + { + Stable, + HaveLocalOffer, + HaveLocalPrAnswer, + HaveRemoteOffer, + HaveRemotePrAnswer, + Closed, + } + + public enum IceGatheringState + { + New, + Gathering, + Complete + } + + public enum IceConnectionState + { + New, + Checking, + Connected, + Completed, + Failed, + Disconnected, + Closed, + Max, + } + + public enum IceTransportsType + { + None, + Relay, + NoHost, + All + } + + public enum BundlePolicy + { + Balanced, + MaxBundle, + MaxCompat + } + + public enum RtcpMuxPolicy + { + Negotiate, + Require, + } + + public enum TlsCertPolicy + { + Secure, + InsecureNoCheck + } + + public sealed class IceServer + { + public string Uri { get; set; } + public string[] Urls { get; set; } = new string[0]; + public string Username { get; set; } + public string Password { get; set; } + public TlsCertPolicy TlsCertPolicy { get; set; } = + TlsCertPolicy.Secure; + public string Hostname { get; set; } + public string[] TlsAlpnProtocols { get; set; } = new string[0]; + public string[] TlsEllipticCurves { get; set; } = new string[0]; + } + + public sealed class RtcConfiguration + { + public bool Dscp { get; set; } + public bool CpuAdaptation { get; set; } + public bool SuspendBelowMinBitrate { get; set; } + public bool PrerendererSmoothing { get; set; } + public bool ExperimentCpuLoadEstimator { get; set; } + public int AudioRtcpReportIntervalMs { get; set; } = 5000; + public int VideoRtcpReportIntervalMs { get; set; } = 1000; + public IceServer[] Servers { get; set; } = new IceServer[0]; + public IceTransportsType Type { get; set; } = IceTransportsType.All; + public BundlePolicy BundlePolicy { get; set; } = + BundlePolicy.Balanced; + public RtcpMuxPolicy RtcpMuxPolicy { get; set; } = + RtcpMuxPolicy.Require; + public IRtcCertificate[] Certificates { get; set; } = + new IRtcCertificate[0]; + public int IceCandidatePoolSize { get; set; } = 0; + public SdpSemantics SdpSemantics; + public bool? EnableDtlsSrtp { get; set; } + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class RtcOfferAnswerOptions + { + public const int Undefined = -1; + public const int MaxOfferToReceiveMedia = 1; + public const int OfferToReceiveMediaTrue = 1; + + public int OfferToReceiveVideo { get; set; } = 0; + public int OfferToReceiveAudio { get; set; } = 0; + + [field: MarshalAs(UnmanagedType.I1)] + public bool VoiceActivityDetection { get; set; } = true; + + [field: MarshalAs(UnmanagedType.I1)] + public bool IceRestart { get; set; } = false; + + [field: MarshalAs(UnmanagedType.I1)] + public bool UseRtpMux { get; set; } = true; + + [field: MarshalAs(UnmanagedType.I1)] + public bool RawPacketizationForVideo { get; set; } = false; + + public int NumSimulcastLayers { get; set; } = 1; + + [field: MarshalAs(UnmanagedType.I1)] + public bool UseObsoleteSctpSdp = false; + } + } + + public static class PeerConnectionInterfaceExtension + { + private readonly struct WebrtcAddTrackResult + { +#pragma warning disable 0649 + public readonly IntPtr Error; + public readonly IntPtr Value; +#pragma warning restore 0649 + } + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcDeleteRtpSenderInterfaces( + IntPtr interfaces); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern bool webrtcPeerConnectionInterfaceAddIceCandidate( + IntPtr connection, + IntPtr candidate + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern WebrtcAddTrackResult webrtcPeerConnectionInterfaceAddTrack( + IntPtr connection, + IntPtr track, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] string[] data, + UIntPtr size + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcPeerConnectionInterfaceCreateAnswer( + IntPtr connection, + IntPtr observer, + PeerConnectionInterface.RtcOfferAnswerOptions options + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcPeerConnectionInterfaceCreateOffer( + IntPtr connection, + IntPtr observer, + PeerConnectionInterface.RtcOfferAnswerOptions options + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcPeerConnectionInterfaceClose( + IntPtr connection + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcPeerConnectionInterfaceGetSenders( + IntPtr connection + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcPeerConnectionInterfaceSetAudioRecording( + IntPtr connection, + [MarshalAs(UnmanagedType.I1)] bool recording + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcRtpSenderInterfacesMove( + IntPtr cplusplus, + IntPtr c + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern UIntPtr webrtcRtpSenderInterfacesSize( + IntPtr interfaces + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcPeerConnectionInterfaceSetLocalDescription( + IntPtr connection, + IntPtr observer, + IntPtr desc + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcPeerConnectionInterfaceSetRemoteDescription( + IntPtr connection, + IntPtr observer, + IntPtr desc + ); + + public static RtcErrorOr AddTrack( + this IPeerConnectionInterface connection, + IMediaStreamTrackInterface track, + string[] streamIds) + { + RtcError error; + + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (track == null) + { + throw new ArgumentNullException(nameof(track)); + } + + if (streamIds == null) + { + throw new ArgumentNullException(nameof(streamIds)); + } + var size = new UIntPtr((ulong)streamIds.LongLength); + var result = webrtcPeerConnectionInterfaceAddTrack( + connection.Ptr, track.Ptr, streamIds, size); + + GC.KeepAlive(connection); + GC.KeepAlive(track); + + try + { + error = new RtcError(result.Error); + } + finally + { + Interop.RtcError.Delete(result.Error); + } + + var value = error.Type == RtcErrorType.None ? + new DisposableRtpSenderInterface(result.Value) : null; + + return new RtcErrorOr(error, value); + } + + public static bool AddIceCandidate( + this IPeerConnectionInterface connection, + IIceCandidateInterface candidate) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (candidate == null) + { + throw new ArgumentNullException(nameof(candidate)); + } + + var result = webrtcPeerConnectionInterfaceAddIceCandidate( + connection.Ptr, candidate.Ptr); + + GC.KeepAlive(connection); + GC.KeepAlive(candidate); + + return result; + } + + public static void Close(this IPeerConnectionInterface connection) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + webrtcPeerConnectionInterfaceClose(connection.Ptr); + GC.KeepAlive(connection); + + } + + public static void CreateAnswer( + this IPeerConnectionInterface connection, + ICreateSessionDescriptionObserver observer, + PeerConnectionInterface.RtcOfferAnswerOptions options) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + webrtcPeerConnectionInterfaceCreateAnswer( + connection.Ptr, observer.Ptr, options); + + GC.KeepAlive(connection); + GC.KeepAlive(observer); + } + + public static void CreateOffer( + this IPeerConnectionInterface connection, + ICreateSessionDescriptionObserver observer, + PeerConnectionInterface.RtcOfferAnswerOptions options) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (observer == null) + { + throw new ArgumentNullException(nameof(observer)); + } + + webrtcPeerConnectionInterfaceCreateOffer( + connection.Ptr, observer.Ptr, options); + + GC.KeepAlive(connection); + GC.KeepAlive(observer); + } + + public static DisposableRtpSenderInterface[] GetSenders( + this IPeerConnectionInterface connection) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + var cplusplus = + webrtcPeerConnectionInterfaceGetSenders(connection.Ptr); + GC.KeepAlive(connection); + var left = (long)webrtcRtpSenderInterfacesSize(cplusplus); + var interfaces = new DisposableRtpSenderInterface[left]; + var c = new IntPtr[left]; + + var handle = GCHandle.Alloc(c, GCHandleType.Pinned); + try + { + var addr = handle.AddrOfPinnedObject(); + webrtcRtpSenderInterfacesMove(cplusplus, addr); + } + finally + { + handle.Free(); + } + + while (left > 0) + { + left--; + interfaces[left] = new DisposableRtpSenderInterface(c[left]); + } + + return interfaces; + } + + public static void SetAudioRecording( + this IPeerConnectionInterface connection, + bool recording) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + webrtcPeerConnectionInterfaceSetAudioRecording( + connection.Ptr, + recording + ); + GC.KeepAlive(connection); + } + + public static void SetLocalDescription( + this IPeerConnectionInterface connection, + ISetSessionDescriptionObserver observer, + IDisposableSessionDescriptionInterface desc) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (observer == null) + { + throw new ArgumentNullException(nameof(observer)); + } + + if (desc == null) + { + throw new ArgumentNullException(nameof(desc)); + } + + webrtcPeerConnectionInterfaceSetLocalDescription( + connection.Ptr, observer.Ptr, desc.Ptr); + + GC.KeepAlive(connection); + GC.KeepAlive(observer); + + desc.ReleasePtr(); + } + + public static void SetRemoteDescription( + this IPeerConnectionInterface connection, + ISetSessionDescriptionObserver observer, + IDisposableSessionDescriptionInterface desc) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (observer == null) + { + throw new ArgumentNullException(nameof(observer)); + } + + if (desc == null) + { + throw new ArgumentNullException(nameof(desc)); + } + webrtcPeerConnectionInterfaceSetRemoteDescription( + connection.Ptr, observer.Ptr, desc.Ptr); + + GC.KeepAlive(connection); + GC.KeepAlive(observer); + desc.ReleasePtr(); + } + } +} + +namespace Pixiv.Webrtc.Interop +{ + public static class PeerConnectionFactoryInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcPeerConnectionFactoryInterfaceRelease")] + public static extern void Release(IntPtr factory); + } + + public static class PeerConnectionInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcPeerConnectionInterfaceRelease")] + public static extern void Release(IntPtr ptr); + } + + public static class PeerConnectionObserver + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcDeletePeerConnectionObserver")] + public static extern void Delete(IntPtr observer); + } +} \ No newline at end of file diff --git a/sdk/dotnet/webrtc/api/rtp_receiver_interface.cs b/sdk/dotnet/webrtc/api/rtp_receiver_interface.cs index 7f414b8dad..9272ace6be 100644 --- a/sdk/dotnet/webrtc/api/rtp_receiver_interface.cs +++ b/sdk/dotnet/webrtc/api/rtp_receiver_interface.cs @@ -46,8 +46,15 @@ IntPtr ptr public static DisposableMediaStreamTrackInterface Track( this IRtpReceiverInterface receiver) { - return Interop.MediaStreamTrackInterface.WrapDisposable( - webrtcRtpReceiverInterfaceTrack(receiver.Ptr)); + if (receiver == null) + { + throw new ArgumentNullException(nameof(receiver)); + } + + var track = webrtcRtpReceiverInterfaceTrack(receiver.Ptr); + GC.KeepAlive(receiver); + + return Interop.MediaStreamTrackInterface.WrapDisposable(track); } } } diff --git a/sdk/dotnet/webrtc/api/rtp_sender_interface.cs b/sdk/dotnet/webrtc/api/rtp_sender_interface.cs index 46e3d130da..ff62ce45c4 100644 --- a/sdk/dotnet/webrtc/api/rtp_sender_interface.cs +++ b/sdk/dotnet/webrtc/api/rtp_sender_interface.cs @@ -1,47 +1,121 @@ -/* - * Copyright 2019 pixiv Inc. All Rights Reserved. - * - * Use of this source code is governed by a license that can be - * found in the LICENSE.pixiv file in the root of the source tree. - */ - -using System; -using System.Runtime.InteropServices; - -namespace Pixiv.Webrtc -{ - public interface IDisposableRtpSenderInterface : - IRtpSenderInterface, Rtc.IDisposable - { - } - - public interface IRtpSenderInterface - { - IntPtr Ptr { get; } - } - - public sealed class DisposableRtpSenderInterface : - Rtc.DisposablePtr, IDisposableRtpSenderInterface - { - IntPtr IRtpSenderInterface.Ptr => Ptr; - - public DisposableRtpSenderInterface(IntPtr ptr) - { - Ptr = ptr; - } - - private protected override void FreePtr() - { - Interop.RtpSenderInterface.Release(Ptr); - } - } -} - -namespace Pixiv.Webrtc.Interop -{ - public static class RtpSenderInterface - { - [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcRtpSenderInterfaceRelease")] - public static extern void Release(IntPtr ptr); - } -} +/* + * Copyright 2019 pixiv Inc. All Rights Reserved. + * + * Use of this source code is governed by a license that can be + * found in the LICENSE.pixiv file in the root of the source tree. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Pixiv.Webrtc +{ + public interface IDisposableRtpSenderInterface : + IRtpSenderInterface, Rtc.IDisposable + { + } + + public interface IRtpSenderInterface + { + IntPtr Ptr { get; } + } + + public sealed class DisposableRtpSenderInterface : + Rtc.DisposablePtr, IDisposableRtpSenderInterface + { + IntPtr IRtpSenderInterface.Ptr => Ptr; + + public DisposableRtpSenderInterface(IntPtr ptr) + { + Ptr = ptr; + } + + private protected override void FreePtr() + { + Interop.RtpSenderInterface.Release(Ptr); + } + } + + public static class RtpSenderInterfaceExtension + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcDeleteRtpSenderInterfaceStreamIds( + IntPtr ids + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcRtpSenderInterfaceStream_ids( + IntPtr sender + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr webrtcRtpSenderInterfaceTrack( + IntPtr sender + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern void webrtcRtpSenderInterfaceStreamIdsData( + IntPtr ids, + IntPtr data + ); + + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl)] + private static extern UIntPtr webrtcRtpSenderInterfaceStreamIdsSize( + IntPtr ids + ); + + public static string[] StreamIds(this IRtpSenderInterface sender) + { + var ids = webrtcRtpSenderInterfaceStream_ids(sender.Ptr); + try + { + GC.KeepAlive(sender); + + var left = (long)webrtcRtpSenderInterfaceStreamIdsSize(ids); + var data = new IntPtr[left]; + var results = new string[left]; + + var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + var addr = handle.AddrOfPinnedObject(); + webrtcRtpSenderInterfaceStreamIdsData(ids, addr); + } + finally + { + handle.Free(); + } + + while (left > 0) + { + left--; + results[left] = Marshal.PtrToStringAnsi(data[left]); + } + + return results; + } + finally + { + webrtcDeleteRtpSenderInterfaceStreamIds(ids); + } + } + + public static DisposableMediaStreamTrackInterface Track( + this IRtpSenderInterface sender + ) + { + var ptr = webrtcRtpSenderInterfaceTrack(sender.Ptr); + GC.KeepAlive(sender); + return Interop.MediaStreamTrackInterface.WrapDisposable(ptr); + } + } +} + +namespace Pixiv.Webrtc.Interop +{ + public static class RtpSenderInterface + { + [DllImport(Dll.Name, CallingConvention = CallingConvention.Cdecl, EntryPoint = "webrtcRtpSenderInterfaceRelease")] + public static extern void Release(IntPtr ptr); + } +} \ No newline at end of file diff --git a/sdk/dotnet/webrtc/api/rtp_transceiver_interface.cs b/sdk/dotnet/webrtc/api/rtp_transceiver_interface.cs index 3e41471cc1..4d945a46a2 100644 --- a/sdk/dotnet/webrtc/api/rtp_transceiver_interface.cs +++ b/sdk/dotnet/webrtc/api/rtp_transceiver_interface.cs @@ -46,8 +46,15 @@ IntPtr ptr public static DisposableRtpReceiverInterface Receiver( this IRtpTransceiverInterface transceiver) { - return new DisposableRtpReceiverInterface( - webrtcRtpTransceiverInterfaceReceiver(transceiver.Ptr)); + if (transceiver == null) + { + throw new ArgumentNullException(nameof(transceiver)); + } + + var receiver = webrtcRtpTransceiverInterfaceReceiver(transceiver.Ptr); + GC.KeepAlive(transceiver); + + return new DisposableRtpReceiverInterface(receiver); } } } diff --git a/sdk/dotnet/webrtc/api/video/i420_buffer.cs b/sdk/dotnet/webrtc/api/video/i420_buffer.cs index a35a0dac85..84827d2350 100644 --- a/sdk/dotnet/webrtc/api/video/i420_buffer.cs +++ b/sdk/dotnet/webrtc/api/video/i420_buffer.cs @@ -94,7 +94,20 @@ IntPtr src public static void ScaleFrom( this II420Buffer dest, II420BufferInterface src) { + if (dest == null) + { + throw new ArgumentNullException(nameof(dest)); + } + + if (src == null) + { + throw new ArgumentNullException(nameof(src)); + } + webrtcI420BufferScaleFrom(dest.Ptr, src.Ptr); + + GC.KeepAlive(dest); + GC.KeepAlive(src); } } } diff --git a/sdk/dotnet/webrtc/api/video/video_buffer.cs b/sdk/dotnet/webrtc/api/video/video_buffer.cs index 7d0b3fa3df..7da25f1b96 100644 --- a/sdk/dotnet/webrtc/api/video/video_buffer.cs +++ b/sdk/dotnet/webrtc/api/video/video_buffer.cs @@ -65,7 +65,14 @@ IntPtr buffer public static DisposableVideoFrame MoveFrame( this IVideoBufferInterface buffer) { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + var frame = webrtcVideoBufferInterfaceMoveFrame(buffer.Ptr); + GC.KeepAlive(buffer); return frame == IntPtr.Zero ? null : new DisposableVideoFrame(frame); diff --git a/sdk/dotnet/webrtc/api/video/video_frame.cs b/sdk/dotnet/webrtc/api/video/video_frame.cs index 6d64bb287a..16824197c1 100644 --- a/sdk/dotnet/webrtc/api/video/video_frame.cs +++ b/sdk/dotnet/webrtc/api/video/video_frame.cs @@ -58,19 +58,41 @@ public static class ReadOnlyVideoFrameExtension private static extern int webrtcVideoFrameHeight(IntPtr ptr); public static int Width(this IReadOnlyVideoFrame frame) - { - return webrtcVideoFrameWidth(frame.Ptr); + { + if (frame == null) + { + throw new ArgumentNullException(nameof(frame)); + } + var width = webrtcVideoFrameWidth(frame.Ptr); + GC.KeepAlive(frame); + + return width; } public static int Height(this IReadOnlyVideoFrame frame) - { - return webrtcVideoFrameHeight(frame.Ptr); + { + if (frame == null) + { + throw new ArgumentNullException(nameof(frame)); + } + var height = webrtcVideoFrameHeight(frame.Ptr); + GC.KeepAlive(frame); + + return height; } public static uint Size(this IReadOnlyVideoFrame frame) - { - return (uint)webrtcVideoFrameWidth(frame.Ptr) * - (uint)webrtcVideoFrameHeight(frame.Ptr); + { + if (frame == null) + { + throw new ArgumentNullException(nameof(frame)); + } + + var width = (uint)webrtcVideoFrameWidth(frame.Ptr); + var height = (uint)webrtcVideoFrameHeight(frame.Ptr); + GC.KeepAlive(frame); + + return width * height; } } @@ -167,52 +189,100 @@ ushort id public static DisposableVideoFrame Build( this VideoFrame.IBuilder builder) - { - return new DisposableVideoFrame( - webrtcVideoFrameBuilderBuild(builder.Ptr)); + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + var frame = webrtcVideoFrameBuilderBuild(builder.Ptr); + GC.KeepAlive(builder); + + return new DisposableVideoFrame(frame); } public static void SetVideoFrameBuffer( this VideoFrame.IBuilder builder, IVideoFrameBuffer value) { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + webrtcVideoFrameBuilder_set_video_frame_buffer( builder.Ptr, value.Ptr); + GC.KeepAlive(builder); + GC.KeepAlive(value); } - public static void SetTimestampMs( + public static void SetTimestampUs( this VideoFrame.IBuilder builder, long value) { - webrtcVideoFrameBuilder_set_timestamp_ms(builder.Ptr, value); + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + webrtcVideoFrameBuilder_set_timestamp_us(builder.Ptr, value); + GC.KeepAlive(builder); } public static void SetTimstampRtp( this VideoFrame.IBuilder builder, uint value) { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } webrtcVideoFrameBuilder_set_timestamp_rtp(builder.Ptr, value); + GC.KeepAlive(builder); } public static void SetNtpTimeMs( this VideoFrame.IBuilder builder, long value) { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } webrtcVideoFrameBuilder_set_ntp_time_ms(builder.Ptr, value); + GC.KeepAlive(builder); } public static void SetRotation( this VideoFrame.IBuilder builder, VideoRotation value) { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } webrtcVideoFrameBuilder_set_rotation(builder.Ptr, value); + GC.KeepAlive(builder); } public static void SetColorSpace( this VideoFrame.IBuilder builder, IntPtr value) { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } webrtcVideoFrameBuilder_set_color_space(builder.Ptr, value); + GC.KeepAlive(builder); } public static void SetID( this VideoFrame.IBuilder builder, ushort value) { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } webrtcVideoFrameBuilder_set_id(builder.Ptr, value); + GC.KeepAlive(builder); } } } diff --git a/sdk/dotnet/webrtc/common_video.cs b/sdk/dotnet/webrtc/common_video.cs index da71398bf5..59ee2726c2 100644 --- a/sdk/dotnet/webrtc/common_video.cs +++ b/sdk/dotnet/webrtc/common_video.cs @@ -58,11 +58,20 @@ public static bool ConvertFromI420( int dstSampleSize, IntPtr dstFrame) { - return webrtcConvertFromI420( + if (srcFrame == null) + { + throw new ArgumentNullException(nameof(srcFrame)); + } + + var result = webrtcConvertFromI420( srcFrame.Ptr, dstVideoType, dstSampleSize, - dstFrame) == 0; + dstFrame); + + GC.KeepAlive(srcFrame); + + return result == 0; } public static bool ConvertToI420( @@ -75,7 +84,12 @@ public static bool ConvertToI420( int cropX, int cropY) { - return webrtcConvertToI420( + if (dstFrame == null) + { + throw new ArgumentNullException(nameof(dstFrame)); + } + + var result = webrtcConvertToI420( srcVideoType, srcSampleSize, srcWidth, @@ -83,7 +97,11 @@ public static bool ConvertToI420( srcFrame, dstFrame.Ptr, cropX, - cropY) == 0; + cropY); + + GC.KeepAlive(dstFrame); + + return result == 0; } } } diff --git a/sdk/dotnet/webrtc/media/base/adapted_video_track_source.cs b/sdk/dotnet/webrtc/media/base/adapted_video_track_source.cs index 4bf77abf67..0890875e9c 100644 --- a/sdk/dotnet/webrtc/media/base/adapted_video_track_source.cs +++ b/sdk/dotnet/webrtc/media/base/adapted_video_track_source.cs @@ -81,15 +81,24 @@ IntPtr functions [MonoPInvokeCallback(typeof(DestructionHandler))] private static void OnDestruction(IntPtr context) { - var handle = (GCHandle)context; - var source = (AdaptedVideoTrackSource)handle.Target; - - if (source._handle.IsAllocated) + try { - source._handle.Free(); + var handle = (GCHandle)context; + var source = (AdaptedVideoTrackSource)handle.Target; + if (source != null) + { + if (source._handle.IsAllocated) + { + source._handle.Free(); + } + } + + handle.Free(); } + catch + { - handle.Free(); + } } [MonoPInvokeCallback(typeof(SourceStateGetter))] @@ -152,7 +161,12 @@ private protected override void FreePtr() protected void OnFrame(IVideoFrame frame) { + if (frame == null) + { + throw new ArgumentNullException(nameof(frame)); + } rtcAdaptedVideoTrackSourceOnFrame(Ptr, frame.Ptr); + GC.KeepAlive(frame); } protected bool AdaptFrame( diff --git a/sdk/dotnet/webrtc/modules.cs b/sdk/dotnet/webrtc/modules.cs index 0c2b098567..033d333b06 100644 --- a/sdk/dotnet/webrtc/modules.cs +++ b/sdk/dotnet/webrtc/modules.cs @@ -80,6 +80,11 @@ IntPtr builder public static DisposableAudioProcessing Create( this IAudioProcessingBuilder builder) { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + return new DisposableAudioProcessing( webrtcAudioProcessingBuilderCreate(builder.Ptr) ); diff --git a/sdk/dotnet/webrtc/rtc_base.cs b/sdk/dotnet/webrtc/rtc_base.cs index 36869809bc..bdaedf478a 100644 --- a/sdk/dotnet/webrtc/rtc_base.cs +++ b/sdk/dotnet/webrtc/rtc_base.cs @@ -137,7 +137,11 @@ internal DisposablePtr() ~DisposablePtr() { - FreePtr(); + //if (_ptr == IntPtr.Zero) + //{ + // return; + //} + //FreePtr(); } public void Dispose() @@ -157,8 +161,8 @@ public virtual void ReleasePtr() { _ptr = IntPtr.Zero; GC.SuppressFinalize(this); - } - } + } + } public sealed class DisposableThread : DisposablePtr, IDisposableThread { @@ -224,7 +228,13 @@ public static class MessageQueueExtension public static void Quit(this IMessageQueue queue) { + if (queue == null) + { + throw new ArgumentNullException(nameof(queue)); + } + rtcMessageQueueQuit(queue.Ptr); + GC.KeepAlive(queue); } } @@ -238,12 +248,24 @@ public static class ThreadExtension public static void Run(this IThread thread) { + if (thread == null) + { + throw new ArgumentNullException(nameof(thread)); + } + rtcThreadRun(thread.Ptr); + GC.KeepAlive(thread); } public static void Start(this IThread thread) { + if (thread == null) + { + throw new ArgumentNullException(nameof(thread)); + } + rtcThreadStart(thread.Ptr); + GC.KeepAlive(thread); } } @@ -261,12 +283,26 @@ IntPtr ptr public static void UnwrapCurrentThread(this IThreadManager manager) { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + rtcThreadManagerUnwrapCurrentThread(manager.Ptr); + GC.KeepAlive(manager); } public static Thread WrapCurrentThread(this IThreadManager manager) { - return new Thread(rtcThreadManagerWrapCurrentThread(manager.Ptr)); + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + + var thread = rtcThreadManagerWrapCurrentThread(manager.Ptr); + GC.KeepAlive(manager); + + return new Thread(thread); } } } diff --git a/sdk/dotnet/webrtc/webrtc.csproj b/sdk/dotnet/webrtc/webrtc.csproj index 9d66ea38c5..7d07331761 100644 --- a/sdk/dotnet/webrtc/webrtc.csproj +++ b/sdk/dotnet/webrtc/webrtc.csproj @@ -11,9 +11,10 @@ WebRTC 77.0.0.0 pixiv Inc. - © pixiv Inc. 2019 + © pixiv Inc. 2019 WebRTC is a free, open project that provides browsers and mobile applications with Real-Time Communications (RTC) capabilities via simple APIs. 7.3 netstandard2.0 + false diff --git a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h index 5c643aabba..9c47f8bdb4 100644 --- a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h +++ b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h @@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN RTC_OBJC_EXPORT +NS_CLASS_AVAILABLE_IOS(2_0) @interface RTCAudioDeviceModule : NSObject - (void)deliverRecordedData:(CMSampleBufferRef)sampleBuffer; diff --git a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm index 518d75a5e3..f3275e0624 100644 --- a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm +++ b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm @@ -11,47 +11,31 @@ #include "rtc_base/ref_counted_object.h" @implementation RTCAudioDeviceModule { -#if defined(WEBRTC_IOS) rtc::scoped_refptr _nativeModule; -#endif } - (instancetype)init { -#if defined(WEBRTC_IOS) self = [super init]; _nativeModule = new rtc::RefCountedObject(); return self; -#else - return nullptr; -#endif } - (void)deliverRecordedData:(CMSampleBufferRef)sampleBuffer { -#if defined(WEBRTC_IOS) _nativeModule->OnDeliverRecordedExternalData(sampleBuffer); -#endif } - (void)setAudioUnitSubType:(OSType)audioUnitSubType { -#if defined(WEBRTC_IOS) _nativeModule->SetAudioUnitSubType(audioUnitSubType); -#endif } - (OSType)audioUnitSubType { -#if defined(WEBRTC_IOS) return _nativeModule->GetAudioUnitSubType(); -#else - return kAudioUnitSubType_VoiceProcessingIO; -#endif } #pragma mark - Private -#if defined(WEBRTC_IOS) - (rtc::scoped_refptr)nativeModule { return _nativeModule; } -#endif @end diff --git a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h index b0c545a155..5bf1622c61 100644 --- a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h +++ b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h @@ -40,7 +40,6 @@ RTC_OBJC_EXPORT - (instancetype)initWithEncoderFactory:(nullable id)encoderFactory decoderFactory:(nullable id)decoderFactory - audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule; /** Initialize an RTCAudioSource with constraints. */ - (RTCAudioSource *)audioSourceWithConstraints:(nullable RTCMediaConstraints *)constraints; diff --git a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm index c12105ab04..930888745f 100644 --- a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm +++ b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm @@ -120,6 +120,7 @@ - (instancetype)initWithEncoderFactory:(nullable id)enco mediaTransportFactory:nullptr]; } +#if defined(WEBRTC_IOS) - (instancetype)initWithEncoderFactory:(nullable id)encoderFactory decoderFactory:(nullable id)decoderFactory audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule { @@ -143,6 +144,7 @@ - (instancetype)initWithEncoderFactory:(nullable id)enco mediaTransportFactory:nullptr]; #endif } +#endif - (instancetype)initNative { if (self = [super init]) { diff --git a/tools_webrtc/unity/generate_licenses.py b/tools_webrtc/unity/generate_licenses.py index 290740f6e4..1a87d65868 100755 --- a/tools_webrtc/unity/generate_licenses.py +++ b/tools_webrtc/unity/generate_licenses.py @@ -26,7 +26,8 @@ generics.append(path.join(argv[1], { 'LinuxX64': 'linux_x64', 'MacX64': 'mac_x64', - 'WinX64': 'win_x64' + 'WinX64': 'win_x64', + 'WinX86': 'win_x86' }[arg])) builder = \