From 0314250c5e8f55bbeb8ee8d55958cd56f1199325 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Fri, 22 Dec 2023 10:06:07 +0900 Subject: [PATCH 01/67] fix: Crash on the device that does not support NvEnc API . (#1009) * check can open encode session * fix clang format * ignore missin field initializers * clang formatted * fix test compile error --- .../Codec/CreateVideoCodecFactory.cpp | 13 +++++--- .../WebRTCPlugin/Codec/NvCodec/NvCodec.cpp | 31 +++++++++++++++++-- Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.h | 2 +- .../WebRTCPluginTest/NvCodec/NvCodecTest.cpp | 5 +-- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/Plugin~/WebRTCPlugin/Codec/CreateVideoCodecFactory.cpp b/Plugin~/WebRTCPlugin/Codec/CreateVideoCodecFactory.cpp index ff6fe21841..a1598d449c 100644 --- a/Plugin~/WebRTCPlugin/Codec/CreateVideoCodecFactory.cpp +++ b/Plugin~/WebRTCPlugin/Codec/CreateVideoCodecFactory.cpp @@ -54,13 +54,16 @@ namespace webrtc if (impl == kNvCodecImpl) { #if CUDA_PLATFORM - if (gfxDevice && gfxDevice->IsCudaSupport() && NvEncoder::IsSupported()) + if (gfxDevice && gfxDevice->IsCudaSupport()) { CUcontext context = gfxDevice->GetCUcontext(); - NV_ENC_BUFFER_FORMAT format = gfxDevice->GetEncodeBufferFormat(); - std::unique_ptr factory = - std::make_unique(context, format, profiler); - return CreateSimulcastEncoderFactory(std::move(factory)); + if (NvEncoder::IsSupported(context)) + { + NV_ENC_BUFFER_FORMAT format = gfxDevice->GetEncodeBufferFormat(); + std::unique_ptr factory = + std::make_unique(context, format, profiler); + return CreateSimulcastEncoderFactory(std::move(factory)); + } } #endif } diff --git a/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.cpp b/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.cpp index f9449d00e1..3a321be535 100644 --- a/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.cpp +++ b/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.cpp @@ -146,15 +146,40 @@ namespace webrtc return std::make_unique(codec, context, memoryType, format, profiler); } - bool NvEncoder::IsSupported() + bool NvEncoder::IsSupported(CUcontext context) { uint32_t version = 0; uint32_t currentVersion = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; - NVENC_API_CALL(NvEncodeAPIGetMaxSupportedVersion(&version)); - if (currentVersion > version) + NVENCSTATUS result = NvEncodeAPIGetMaxSupportedVersion(&version); + if (result != NV_ENC_SUCCESS || currentVersion > version) { return false; } + +// Check if this device can get the function list of nvencoder API +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" + NV_ENCODE_API_FUNCTION_LIST funclist = { NV_ENCODE_API_FUNCTION_LIST_VER }; + result = NvEncodeAPICreateInstance(&funclist); + if (result != NV_ENC_SUCCESS || funclist.nvEncOpenEncodeSession == nullptr) + { + return false; + } + +// Check if this device can open encode session +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER }; + encodeSessionExParams.device = context; + encodeSessionExParams.deviceType = NV_ENC_DEVICE_TYPE_CUDA; + encodeSessionExParams.apiVersion = NVENCAPI_VERSION; + void* hEncoder = nullptr; + result = funclist.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder); + if (result != NV_ENC_SUCCESS || hEncoder == nullptr) + { + return false; + } + + funclist.nvEncDestroyEncoder(hEncoder); + hEncoder = nullptr; return true; } diff --git a/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.h b/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.h index 665be39424..617158d3d9 100644 --- a/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.h +++ b/Plugin~/WebRTCPlugin/Codec/NvCodec/NvCodec.h @@ -35,7 +35,7 @@ namespace webrtc CUmemorytype memoryType, NV_ENC_BUFFER_FORMAT format, ProfilerMarkerFactory* profiler); - static bool IsSupported(); + static bool IsSupported(CUcontext context); ~NvEncoder() override { } }; diff --git a/Plugin~/WebRTCPluginTest/NvCodec/NvCodecTest.cpp b/Plugin~/WebRTCPluginTest/NvCodec/NvCodecTest.cpp index 3accfb8d89..b9f779ee87 100644 --- a/Plugin~/WebRTCPluginTest/NvCodec/NvCodecTest.cpp +++ b/Plugin~/WebRTCPluginTest/NvCodec/NvCodecTest.cpp @@ -36,11 +36,12 @@ namespace webrtc GTEST_SKIP() << "The graphics driver is not installed on the device."; if (!device_->IsCudaSupport()) GTEST_SKIP() << "CUDA is not supported on this device."; - if (!NvEncoder::IsSupported()) - GTEST_SKIP() << "Current Driver Version does not support this NvEncodeAPI version."; context_ = device_->GetCUcontext(); + if (!NvEncoder::IsSupported(context_)) + GTEST_SKIP() << "Current Driver Version does not support this NvEncodeAPI version."; + VideoCodecTest::SetUp(); } From 20f9fcc5f7d897640916cfc8e5f9e3b63694540b Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Mon, 2 Dec 2024 16:06:38 +0900 Subject: [PATCH 02/67] Improve API documentation --- Runtime/Scripts/RTCPeerConnection.cs | 599 +++++++++++++++++++-------- 1 file changed, 433 insertions(+), 166 deletions(-) diff --git a/Runtime/Scripts/RTCPeerConnection.cs b/Runtime/Scripts/RTCPeerConnection.cs index 5b545b1798..3d313db818 100644 --- a/Runtime/Scripts/RTCPeerConnection.cs +++ b/Runtime/Scripts/RTCPeerConnection.cs @@ -5,33 +5,108 @@ namespace Unity.WebRTC { /// - /// + /// Delegate to be called when a new RTCIceCandidate is identified and added to the local peer, when all candidates for a specific generation are identified and added, and when the ICE gathering on all transports is complete. /// - /// + /// + /// This delegate is called when: + /// * An `RTCIceCandidate` is added to the local peer using `SetLocalDescription`. + /// * Every `RTCIceCandidate` correlated with a specific username/password combination are added. + /// * ICE gathering for all transports is finished. + /// + /// `RTCIceCandidate` object containing the candidate associated with the event. + /// + /// + /// { + /// } + /// ]]> + /// + /// public delegate void DelegateOnIceCandidate(RTCIceCandidate candidate); + /// - /// + /// Delegate to be called when the ICE connection state is changed. /// - /// + /// + /// This delegate is called each time the ICE connection state changes during the negotiation process. + /// + /// `RTCIceConnectionState` value. + /// + /// + /// { + /// } + /// ]]> + /// + /// public delegate void DelegateOnIceConnectionChange(RTCIceConnectionState state); + /// - /// + /// Delegate to be called after a new track has been added to an RTCRtpReceiver which is part of the connection. /// + /// + /// This delegate is called after a new track has been added to an `RTCRtpReceiver` which is part of the connection. + /// /// + /// + /// + /// { + /// } + /// ]]> + /// + /// public delegate void DelegateOnConnectionStateChange(RTCPeerConnectionState state); + /// - /// + /// Delegate to be called when the state of the ICE candidate gathering process changes. /// + /// + /// This delegate is called when the state of the ICE candidate gathering process changes. + /// /// + /// + /// + /// { + /// } + /// ]]> + /// + /// public delegate void DelegateOnIceGatheringStateChange(RTCIceGatheringState state); + /// - /// + /// Delegate to be called when negotiation of the connection through the signaling channel is required. /// + /// + /// This delegate is called when negotiation of the connection through the signaling channel is required. + /// + /// + /// + /// { + /// } + /// ]]> + /// + /// + /// public delegate void DelegateOnNegotiationNeeded(); + /// - /// + /// Delegate to be called after a new track has been added to an RTCRtpReceiver which is part of the connection. /// - /// + /// + /// This delegate is called after a new track has been added to an `RTCRtpReceiver` which is part of the connection. + /// + /// `RTCTrackEvent` object. + /// + /// + /// { + /// } + /// ]]> + /// + /// public delegate void DelegateOnTrack(RTCTrackEvent e); @@ -39,13 +114,22 @@ namespace Unity.WebRTC internal delegate void DelegateSetSessionDescFailure(RTCError error); /// - /// Represents a WebRTC connection between the local peer and remote peer. + /// Represents a WebRTC connection between the local peer and remote peer. /// - /// /// - /// + /// `RTCPeerConnection` class represents a WebRTC connection between the local computer and a remote peer. + /// It provides methods to connect to a remote peer, maintain and monitor the connection, and close the connection once it's no longer needed. /// - /// + /// + /// + /// + /// + /// + /// + /// + /// public class RTCPeerConnection : IDisposable { private IntPtr self; @@ -53,16 +137,28 @@ public class RTCPeerConnection : IDisposable private bool disposed; /// - /// + /// Finalizer for RTCPeerConnection. /// + /// + /// Ensures that resources are released by calling the `Dispose` method. + /// ~RTCPeerConnection() { this.Dispose(); } /// - /// + /// Disposes of RTCPeerConnection. /// + /// + /// `Dispose` method releases resources used by the `RTCPeerConnection`. + /// This method closes the current peer connection and disposes of all transceivers. + /// + /// + /// + /// public void Dispose() { if (this.disposed) @@ -140,19 +236,22 @@ private void DisposeAllTransceivers() public RTCSignalingState SignalingState => NativeMethods.PeerConnectionSignalingState(GetSelfOrThrow()); /// - /// + /// RTCIceGatheringState value that describes the overall ICE gathering state for the RTCPeerConnection. /// public RTCIceGatheringState GatheringState => NativeMethods.PeerConnectionIceGatheringState(GetSelfOrThrow()); /// - /// Returns array of objects each of which represents one RTP receiver. + /// Returns array of objects each of which represents one RTP receiver. /// + /// + /// `GetReceivers` method returns an array of `RTCRtpReceiver` objects, each of which represents one RTP receiver. + /// /// - /// - /// var senders = peerConnection.GetReceivers(); - /// + /// /// - /// Array of the senders + /// An array of `RTCRtpReceiver` objects, one for each track on the connection. /// /// public IEnumerable GetReceivers() @@ -162,14 +261,17 @@ public IEnumerable GetReceivers() } /// - /// Returns array of objects each of which represents one RTP sender. + /// Returns array of objects each of which represents one RTP sender. /// + /// + /// `GetSenders` method returns an array of `RTCRtpSender` objects, each of which represents the RTP sender responsible for transmitting one track's data. + /// /// - /// - /// var senders = peerConnection.GetSenders(); - /// + /// /// - /// Array of the receivers + /// An array of `RTCRtpSender` objects, one for each track on the connection. /// /// public IEnumerable GetSenders() @@ -179,14 +281,17 @@ public IEnumerable GetSenders() } /// - /// Returns array of objects each of which represents one RTP transceiver. + /// Returns array of objects each of which represents one RTP transceiver. /// + /// + /// `GetTransceivers` method returns an array of the `RTCRtpTransceiver` objects being used to send and receive data on the connection. + /// /// - /// - /// var transceivers = peerConnection.GetTransceivers(); - /// + /// /// - /// Array of the transceivers + /// An array of the `RTCRtpTransceiver` objects representing the transceivers handling sending and receiving all media on the `RTCPeerConnection`. /// /// public IEnumerable GetTransceivers() @@ -212,50 +317,47 @@ RTCRtpTransceiver CreateTransceiver(IntPtr ptr) /// - /// This property is delegate to be called when the is changed. + /// Delegate to be called when the IceConnectionState is changed. /// - /// A delegate containing . + /// A delegate containing . /// - /// - /// peerConnection.OnIceConnectionChange = iceConnectionState => - /// { - /// ... - /// }; - /// + /// + /// { + /// ... + /// }; + /// ]]> /// /// public DelegateOnIceConnectionChange OnIceConnectionChange { get; set; } /// - /// + /// Delegate to be called after a new track has been added to an RTCRtpReceiver which is part of the connection. /// public DelegateOnConnectionStateChange OnConnectionStateChange { get; set; } /// - /// + /// Delegate to be called when the state of the ICE candidate gathering process changes. /// - /// public DelegateOnIceGatheringStateChange OnIceGatheringStateChange { get; set; } /// - /// + ///  Delegate to be called when a new RTCIceCandidate is identified and added to the local peer, when all candidates for a specific generation are identified and added, and when the ICE gathering on all transports is complete. /// - /// public DelegateOnIceCandidate OnIceCandidate { get; set; } /// - /// + /// Delegate to be called when an RTCDataChannel has been added to the connection, as a result of the remote peer calling RTCPeerConnection.CreateDataChannel. /// - /// public DelegateOnDataChannel OnDataChannel { get; set; } /// - /// + /// Delegate to be called when negotiation of the connection through the signaling channel is required. /// public DelegateOnNegotiationNeeded OnNegotiationNeeded { get; set; } /// - /// + /// Delegate to be called after a new track has been added to an RTCRtpReceiver which is part of the connection. /// /// public DelegateOnTrack OnTrack { get; set; } @@ -379,20 +481,21 @@ static void PCOnRemoveTrack(IntPtr ptr, IntPtr receiverPtr) } /// - /// Returns an object which indicates the current configuration - /// of the . + /// Returns an object which indicates the current configuration of the RTCPeerConnection. /// - /// An object describing the 's - /// current configuration. + /// + /// `GetConfiguration` method returns an object which indicates the current configuration of the `RTCPeerConnection`. + /// + /// An object describing the 's current configuration. /// - /// - /// var configuration = myPeerConnection.GetConfiguration(); - /// if(configuration.urls.length == 0) - /// { - /// configuration.urls = new[] {"stun:stun.l.google.com:19302"}; - /// } - /// myPeerConnection.SetConfiguration(configuration); - /// + /// /// /// public RTCConfiguration GetConfiguration() @@ -404,34 +507,38 @@ public RTCConfiguration GetConfiguration() } /// - /// This method sets the current configuration of the - /// This lets you change the ICE servers used by the connection - /// and which transport policies to use. + /// Sets the current configuration of the RTCPeerConnection. /// - /// The changes are not additive; instead, - /// the new values completely replace the existing ones. + /// + /// `SetConfiguration` method sets the current configuration of the connection based on the values included in the specified object. + /// This lets you change the ICE servers used by the connection and which transport policies to use. + /// + /// + /// `RTCConfiguration` object which provides the options to be set. + /// The changes are not additive; instead, the new values completely replace the existing ones. + /// /// Error code. /// - /// - /// var configuration = new RTCConfiguration - /// { - /// iceServers = new[] - /// { - /// new RTCIceServer + /// + /// ]]> /// /// public RTCErrorType SetConfiguration(ref RTCConfiguration configuration) @@ -442,8 +549,16 @@ public RTCErrorType SetConfiguration(ref RTCConfiguration configuration) } /// - /// This constructor creates an instance of peer connection with a default configuration. + /// Creates an instance of peer connection with a default configuration. /// + /// + /// `RTCPeerConnection` constructor creates an instance of peer connection with a default configuration. + /// + /// + /// + /// /// public RTCPeerConnection() { @@ -458,10 +573,18 @@ public RTCPeerConnection() } /// - /// This constructor creates an instance of peer connection with a configuration provided by user. - /// An object providing options to configure the new connection. + /// Creates an instance of peer connection with a configuration provided by user. /// - /// + /// + /// `RTCPeerConnection` constructor creates an instance of peer connection with a default configuration. + /// An object providing options to configure the new connection. + /// + /// + /// + /// + /// `RTCConfiguration` object to configure the new connection. /// public RTCPeerConnection(ref RTCConfiguration configuration) { @@ -490,16 +613,34 @@ void InitCallback() } /// - /// + /// Requests that ICE candidate gathering be redone on both ends of the connection. /// + /// + /// `RestartIce` method requests that ICE candidate gathering be redone on both ends of the connection. + /// After `RestartIce` is called, the offer returned by the next call to `CreateOffer` automatically configured to trigger ICE restart on both the local and remote peers. + /// This method triggers an `OnNegotiationNeeded` event. + /// + /// + /// + /// public void RestartIce() { NativeMethods.PeerConnectionRestartIce(GetSelfOrThrow()); } /// - /// + /// Closes the current peer connection. /// + /// + /// `Close` method closes the current peer connection. + /// + /// + /// + /// /// public void Close() { @@ -507,11 +648,23 @@ public void Close() } /// - /// + /// Adds a new media track to the set of tracks which is transmitted to the other peer. /// - /// - /// - /// + /// + /// `AddTrack` method adds a new media track to the set of tracks which is transmitted to the other peer. + /// Adding a track to a connection triggers renegotiation by firing an `OnNegotiationNeeded` event. + /// + /// `MediaStreamTrack` object representing the media track to add to the peer connection. + /// + /// Local `MediaStream` object to which the track should be added. + /// If this is not specified, then the track is **streamless**. + /// + /// `RTCRtpSender` object which is used to transmit the media data. + /// + /// + /// /// public RTCRtpSender AddTrack(MediaStreamTrack track, MediaStream stream = null) { @@ -528,10 +681,19 @@ public RTCRtpSender AddTrack(MediaStreamTrack track, MediaStream stream = null) } /// - /// + /// Tells the local end of the connection to stop sending media from the specified track. /// - /// - /// + /// + /// `RemoveTrack` method tells the local end of the connection to stop sending media from the specified track, without actually removing the corresponding `RTCRtpSender` from the list of senders. + /// If the track is already stopped, or is not in the connection's senders list, this method has no effect. + /// + /// `RTCRtpSender` object specifying the sender to remove from the connection. + /// `RTCErrorType` value. + /// + /// + /// /// public RTCErrorType RemoveTrack(RTCRtpSender sender) { @@ -540,11 +702,20 @@ public RTCErrorType RemoveTrack(RTCRtpSender sender) } /// - /// + /// Creates a new RTCRtpTransceiver and adds it to the set of transceivers associated with the RTCPeerConnection. /// - /// - /// - /// + /// + /// `AddTransceiver` method creates a new `RTCRtpTransceiver` instance and adds it to the set of transceivers associated with the `RTCPeerConnection`. + /// Each transceiver represents a bidirectional stream, with both an `RTCRtpSender` and an `RTCRtpReceiver` associated with it. + /// + /// `MediaStreamTrack` object to associate with the transceiver. + /// `RTCRtpTransceiverInit` object for specifying options when creating the new transceiver. + /// `RTCRtpTransceiver` object which is used to exchange the media data. + /// + /// + /// public RTCRtpTransceiver AddTransceiver(MediaStreamTrack track, RTCRtpTransceiverInit init = null) { if (track == null) @@ -555,11 +726,20 @@ public RTCRtpTransceiver AddTransceiver(MediaStreamTrack track, RTCRtpTransceive } /// - /// + /// Creates a new RTCRtpTransceiver and adds it to the set of transceivers associated with the RTCPeerConnection. /// - /// - /// - /// + /// + /// `AddTransceiver` method creates a new `RTCRtpTransceiver` instance and adds it to the set of transceivers associated with the `RTCPeerConnection`. + /// Each transceiver represents a bidirectional stream, with both an `RTCRtpSender` and an `RTCRtpReceiver` associated with it. + /// + /// `TrackKind` value which is used as the kind of the receiver's track. + /// `RTCRtpTransceiverInit` object for specifying options when creating the new transceiver. + /// `RTCRtpTransceiver` object which is used to exchange the media data. + /// + /// + /// public RTCRtpTransceiver AddTransceiver(TrackKind kind, RTCRtpTransceiverInit init = null) { IntPtr ptr = PeerConnectionAddTransceiverWithType( @@ -568,10 +748,19 @@ public RTCRtpTransceiver AddTransceiver(TrackKind kind, RTCRtpTransceiverInit in } /// - /// + /// Adds a new remote candidate to the connection's remote description. /// - /// - /// + /// + /// `AddIceCandidate` method adds a new remote ICE candidate to the connection's remote description, which describes the current state of the remote end of the connection. + /// + /// + /// `RTCIceCandidate` object that describes the properties of the new remote candidate. + /// If the value is null, the added ICE candidate is an "end-of-candidates" indicator. + /// + /// `true` if the candidate has been successfully added to the remote peer's description by the ICE agent. + /// public bool AddIceCandidate(RTCIceCandidate candidate) { return NativeMethods.PeerConnectionAddIceCandidate( @@ -579,11 +768,19 @@ public bool AddIceCandidate(RTCIceCandidate candidate) } /// - /// Create an SDP (Session Description Protocol) offer to start a new connection - /// to a remote peer. + /// Create an SDP (Session Description Protocol) offer to start a new connection to a remote peer. /// - /// - /// + /// + /// `CreateOffer` initiates the creation of an SDP offer for the purpose of starting a new WebRTC connection to a remote peer. + /// The SDP offer contains details about `MediaStreamTrack` objects, supported codecs and options, and ICE candidates. + /// + /// `RTCOfferAnswerOptions` object providing the options requested for the offer. + /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. + /// + /// + /// /// public RTCSessionDescriptionAsyncOperation CreateOffer(ref RTCOfferAnswerOptions options) { @@ -593,9 +790,18 @@ public RTCSessionDescriptionAsyncOperation CreateOffer(ref RTCOfferAnswerOptions } /// - /// + /// Create an SDP (Session Description Protocol) offer to start a new connection to a remote peer. /// - /// + /// + /// `CreateOffer` initiates the creation of an SDP offer for the purpose of starting a new WebRTC connection to a remote peer. + /// The SDP offer contains details about `MediaStreamTrack` objects, supported codecs and options, and ICE candidates. + /// + /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. + /// + /// + /// public RTCSessionDescriptionAsyncOperation CreateOffer() { CreateSessionDescriptionObserver observer = @@ -604,11 +810,19 @@ public RTCSessionDescriptionAsyncOperation CreateOffer() } /// - /// Create an SDP (Session Description Protocol) answer to start a new connection - /// to a remote peer. + /// Create an SDP (Session Description Protocol) answer to start a new connection to a remote peer. /// - /// - /// + /// + /// `CreateAnswer` method creates an SDP answer to an offer received from a remote peer during the offer/answer negotiation of a WebRTC connection. + /// The SDP answer contains details about the session's media, supported codecs, and ICE candidates. + /// + /// `RTCOfferAnswerOptions` object providing options requested for the answer. + /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. + /// + /// + /// public RTCSessionDescriptionAsyncOperation CreateAnswer(ref RTCOfferAnswerOptions options) { CreateSessionDescriptionObserver observer = @@ -617,9 +831,18 @@ public RTCSessionDescriptionAsyncOperation CreateAnswer(ref RTCOfferAnswerOption } /// - /// + /// Create an SDP (Session Description Protocol) answer to start a new connection to a remote peer. /// - /// + /// + /// `CreateAnswer` method creates an SDP answer to an offer received from a remote peer during the offer/answer negotiation of a WebRTC connection. + /// The SDP answer contains details about the session's media, supported codecs, and ICE candidates. + /// + /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. + /// + /// + /// public RTCSessionDescriptionAsyncOperation CreateAnswer() { CreateSessionDescriptionObserver observer = @@ -628,12 +851,22 @@ public RTCSessionDescriptionAsyncOperation CreateAnswer() } /// - /// Creates a new data channel related the remote peer. + /// Creates a new data channel related the remote peer. /// - /// A string for the data channel. - /// This string may be checked by . - /// A struct provides configuration options for the data channel. - /// A new data channel. + /// + /// `CreateDataChannel` method creates a new data channel with the remote peer for transmitting any type of data. + /// + /// + /// A string for the data channel. + /// This string may be checked by . + /// + /// A struct provides configuration options for the data channel. + /// A new data channel. + /// + /// + /// public RTCDataChannel CreateDataChannel(string label, RTCDataChannelInit options = null) { RTCDataChannelInitInternal _options = @@ -646,21 +879,27 @@ public RTCDataChannel CreateDataChannel(string label, RTCDataChannelInit options } /// - /// This method changes the session description - /// of the local connection to negotiate with other connections. + /// Changes the session description of the local connection to negotiate with other connections. /// - /// + /// + /// `SetLocalDescription` method changes the local description associated with the connection, specifying the properties of the local end of the connection, including the media format. + /// + /// `RTCSessionDescription` object which specifies the configuration to be applied to the local end of the connection. /// - /// An AsyncOperation which resolves with an - /// object providing a description of the session. + /// An AsyncOperation which resolves with an object providing a description of the session. /// + /// + /// + /// /// - /// Thrown when an argument has an invalid value. - /// For example, when passed the sdp which is null or empty. + /// Thrown when an argument has an invalid value. + /// For example, when passed the sdp which is null or empty. /// /// - /// Thrown when an argument has an invalid value. - /// For example, when passed the sdp which is not be able to parse. + /// Thrown when an argument has an invalid value. + /// For example, when passed the sdp which is not be able to parse. /// /// public RTCSetSessionDescriptionAsyncOperation SetLocalDescription( @@ -675,9 +914,19 @@ public RTCSetSessionDescriptionAsyncOperation SetLocalDescription( } /// - /// + /// Changes the session description of the local connection to negotiate with other connections. /// - /// + /// + /// `SetLocalDescription` method automatically adjusts the local description associated with the connection, specifying the properties of the local end of the connection, including the media format. + /// + /// + /// An AsyncOperation which resolves with an object providing a description of the session. + /// + /// + /// + /// public RTCSetSessionDescriptionAsyncOperation SetLocalDescription() { SetSessionDescriptionObserver observer = @@ -686,21 +935,27 @@ public RTCSetSessionDescriptionAsyncOperation SetLocalDescription() } /// - /// This method changes the session description - /// of the remote connection to negotiate with local connections. + /// This method changes the session description of the remote connection to negotiate with local connections. /// - /// + /// + /// `SetRemoteDescription` method changes the specified session description as the remote peer's current offer or answer, specifying the properties of the remote end of the connection, including the media format. + /// + /// `RTCSessionDescription` object which specifies the remote peer's current offer or answer. /// - /// An AsyncOperation which resolves with an - /// object providing a description of the session. + /// An AsyncOperation which resolves with an object providing a description of the session. /// + /// + /// + /// /// - /// Thrown when an argument has an invalid value. - /// For example, when passed the sdp which is null or empty. + /// Thrown when an argument has an invalid value. + /// For example, when passed the sdp which is null or empty. /// /// - /// Thrown when an argument has an invalid value. - /// For example, when passed the sdp which is not be able to parse. + /// Thrown when an argument has an invalid value. + /// For example, when passed the sdp which is not be able to parse. /// /// public RTCSetSessionDescriptionAsyncOperation SetRemoteDescription( @@ -715,27 +970,29 @@ public RTCSetSessionDescriptionAsyncOperation SetRemoteDescription( } /// - /// Returns an AsyncOperation which resolves with data providing statistics. + /// Returns an AsyncOperation which resolves with data providing statistics. /// + /// + /// `GetStats` method returns a promise which resolves with data providing statistics about either the overall connection or about the specified `MediaStreamTrack`. + /// /// - /// An AsyncOperation which resolves with an - /// object providing connection statistics. + /// An AsyncOperation which resolves with an object providing connection statistics. /// /// - /// - /// // Already instantiated peerConnection as RTCPeerConnection. - /// var operation = peerConnection.GetStats(); - /// yield return operation; + /// + /// if (!operation.IsError) + /// { + /// var report = operation.Value; + /// foreach (var stat in report.Stats.Values) + /// { + /// Debug.Log(stat.Type.ToString()); + /// } + /// } + /// ]]> /// /// public RTCStatsReportAsyncOperation GetStats() @@ -763,6 +1020,14 @@ RTCStatsReportAsyncOperation GetStats(RTCStatsCollectorCallback callback) return new RTCStatsReportAsyncOperation(callback); } + /// + /// Boolean value that indicates whether the remote peer can accept trickled ICE candidates. + /// + /// + /// When the value is true, the remote peer can accept trickled ICE candidates. + /// When the value is false, the remote peer cannot accept trickled ICE candidates. + /// When the value is null, the remote peer has not been established. + /// public bool? CanTrickleIceCandidates { get @@ -773,7 +1038,7 @@ public bool? CanTrickleIceCandidates } /// - /// + /// RTCSessionDescription object that describes the session for the local end of the RTCPeerConnection. /// public RTCSessionDescription LocalDescription { @@ -789,7 +1054,7 @@ public RTCSessionDescription LocalDescription } /// - /// + /// RTCSessionDescription object that describes the session (which includes configuration and media information) for the remote end of the connection. /// public RTCSessionDescription RemoteDescription { @@ -805,7 +1070,8 @@ public RTCSessionDescription RemoteDescription } /// - /// + ///  RTCSessionDescription object describing the local end of the connection from the last successful negotiation with a remote peer. + /// It also includes ICE candidates generated by the ICE agent since the initial offer or answer was first created. /// public RTCSessionDescription CurrentLocalDescription { @@ -821,7 +1087,8 @@ public RTCSessionDescription CurrentLocalDescription } /// - /// + /// RTCSessionDescription object describing the remote end of the connection from the last successful negotiation with a remote peer. + /// It also includes ICE candidates generated by the ICE agent since the initial offer or answer was first created. /// public RTCSessionDescription CurrentRemoteDescription { @@ -837,7 +1104,7 @@ public RTCSessionDescription CurrentRemoteDescription } /// - /// + /// RTCSessionDescription object that describes a pending configuration change for the local end of the connection. /// public RTCSessionDescription PendingLocalDescription { @@ -853,7 +1120,7 @@ public RTCSessionDescription PendingLocalDescription } /// - /// + /// RTCSessionDescription object that describes a pending configuration change for the remote end of the connection. /// public RTCSessionDescription PendingRemoteDescription { From 675aae31b654e85bb54727d5f89355d5257d8434 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Tue, 3 Dec 2024 17:39:34 +0900 Subject: [PATCH 03/67] Improve documentation of VideoStreamTrack --- Runtime/Scripts/VideoStreamTrack.cs | 105 +++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 19 deletions(-) diff --git a/Runtime/Scripts/VideoStreamTrack.cs b/Runtime/Scripts/VideoStreamTrack.cs index 1e6702f6e9..657d6669d6 100644 --- a/Runtime/Scripts/VideoStreamTrack.cs +++ b/Runtime/Scripts/VideoStreamTrack.cs @@ -8,26 +8,53 @@ namespace Unity.WebRTC { /// - /// + /// Delegate to be called when the first frame of the video is received. /// - /// + /// + /// `OnVideoReceived` delegate is called when the first frame of the video is received. + /// + /// `Texture` object where the video stream is rendered. + /// + /// + /// { + /// } + /// ]]> + /// + /// public delegate void OnVideoReceived(Texture renderer); /// - /// + /// Delegate to be called to copy texture. /// - /// - /// + /// + /// `CopyTexture` delegate is called to copy texture when the texture is updated. + /// + /// Source `Texture` object. + /// Destination `Texture` object. public delegate void CopyTexture(Texture source, RenderTexture dest); /// - /// + /// Represents a single video track within a stream /// + /// + /// `VideoStreamTrack` is a `MediaStreamTrack` that represents a single video track within a stream. + /// + /// + /// + /// + /// + /// public class VideoStreamTrack : MediaStreamTrack { /// - /// Flip vertically received video, change it befor start receive video + /// If the value is set to true, the received video is flipped vertically. /// + /// + /// Change this property before starting to receive video. + /// public static bool NeedReceivedVideoFlipVertically { get; set; } = true; internal static ConcurrentDictionary> s_tracks = @@ -66,7 +93,8 @@ private static RenderTexture CreateRenderTexture(int width, int height) } /// - /// encoded / decoded texture + /// When the track is configured to receive a video stream, represents the `Texture` object where the video stream is rendered. + /// When the track is configured to send a video stream, represents the destination `Texture` object to send. /// public Texture Texture { @@ -79,7 +107,8 @@ public Texture Texture } /// - /// encoded / decoded texture ptr + /// When the track is configured to receive a video stream, represents the pointer to the `Texture` object where the video stream is rendered. + /// When the track is configured to send a video stream, represents the pointer to the destination `Texture` object to send. /// public IntPtr TexturePtr { @@ -91,14 +120,35 @@ public IntPtr TexturePtr } } + /// + /// Indicates that the track is configured to decode an incoming video stream. + /// public bool Decoding => m_renderer != null; + + /// + /// Indicates that the track is configured to encode and send a video stream to a remote peer. + /// public bool Encoding => m_source != null; - public IntPtr DataPtr => m_dataptr; + /// + /// Pointer to the video stream data in the native memory. + /// + public IntPtr DataPtr => m_dataptr; /// - /// + /// Event to be fired when the first frame of the video is received. /// + /// + /// `OnVideoReceived` event is fired when the first frame of the video is received. + /// + /// + /// + /// { + /// } + /// ]]> + /// + /// public event OnVideoReceived OnVideoReceived; internal void UpdateTexture() @@ -135,15 +185,24 @@ internal void UpdateTexture() } /// - /// Video Sender - /// Creates a new VideoStream object. - /// The track is created with a `source`. + /// Creates a new VideoStreamTrack object. /// - /// + /// + /// `VideoStreamTrack` constructor creates an instance of `VideoStreamTrack` with a `source`. + /// + /// + /// `Texture` object that provides the input source for the video stream and is used in creating the video track. + /// /// - /// By default, textures are copied vertically flipped, using CopyTextureHelper.VerticalFlipCopy, - /// use Graphics.Blit for copy as is, CopyTextureHelper for flip, - /// or write your own CopyTexture function + /// By default, textures are copied vertically flipped, using `CopyTextureHelper.VerticalFlipCopy`, + /// use `Graphics.Blit` for copy as is, `CopyTextureHelper` for flip, + /// or write your own `CopyTexture` function. + /// + /// + /// + /// /// public VideoStreamTrack(Texture texture, CopyTexture copyTexture = null) : base(CreateVideoTrack(texture, out var source)) @@ -180,8 +239,16 @@ internal VideoStreamTrack(IntPtr ptr) } /// - /// + /// Disposes of VideoStremTrack /// + /// + /// `Dispose` method disposes of the `VideoStreamTrack` and releases the associated resources. + /// + /// + /// + /// public override void Dispose() { if (this.disposed) From 61a2df905b4be6de0117560afdf5a9be23abe3d6 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Wed, 4 Dec 2024 11:21:01 +0900 Subject: [PATCH 04/67] Improve documentation of MediaStream --- Runtime/Scripts/MediaStream.cs | 112 ++++++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 21 deletions(-) diff --git a/Runtime/Scripts/MediaStream.cs b/Runtime/Scripts/MediaStream.cs index 4e4b2ce381..5f4784b0ae 100644 --- a/Runtime/Scripts/MediaStream.cs +++ b/Runtime/Scripts/MediaStream.cs @@ -17,8 +17,19 @@ namespace Unity.WebRTC public delegate void DelegateOnRemoveTrack(MediaStreamTrackEvent e); /// - /// + /// Represents a stream of media content. /// + /// + /// `MediaStream` represents a stream of media content. + /// A stream consists of several tracks, such as video or audio tracks. + /// Each track is specified as an instance of `MediaStreamTrack`. + /// + /// + /// + /// + /// public class MediaStream : RefCountedObject { private DelegateOnAddTrack onAddTrack; @@ -27,22 +38,33 @@ public class MediaStream : RefCountedObject private HashSet cacheTracks = new HashSet(); /// - /// + /// String containing 36 characters denoting a unique identifier for the object. /// public string Id => NativeMethods.MediaStreamGetID(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// - /// + /// Finalizer for MediaStream. /// + /// + /// Ensures that resources are released by calling the `Dispose` method. + /// ~MediaStream() { this.Dispose(); } /// - /// + /// Disposes of MediaStream. /// + /// + /// `Dispose` method disposes of the MediaStream and releases the associated resources. + /// + /// + /// + /// public override void Dispose() { if (this.disposed) @@ -58,7 +80,7 @@ public override void Dispose() } /// - /// + /// Delegate to be called when a new MediaStreamTrack object has been added. /// /// todo:(kazuki) Rename to "onAddTrack" /// todo:(kazuki) Should we change the API to use UnityEvent or Action class? @@ -72,7 +94,7 @@ public DelegateOnAddTrack OnAddTrack } /// - /// + /// Delegate to be called when a new MediaStreamTrack object has been removed. /// /// todo:(kazuki) Rename to "onAddTrack" /// todo:(kazuki) Should we change the API to use UnityEvent or Action class? @@ -86,9 +108,17 @@ public DelegateOnRemoveTrack OnRemoveTrack } /// - /// + /// Returns a list of VideoStreamTrack objects in the stream. /// - /// + /// + /// `GetVideoTracks` method returns a sequence that represents all the `VideoStreamTrack` objects in this stream's track set. + /// + /// List of `MediaStreamTrack` objects, one for each video track contained in the media stream. + /// + /// + /// public IEnumerable GetVideoTracks() { var buf = NativeMethods.MediaStreamGetVideoTracks(GetSelfOrThrow(), out ulong length); @@ -96,9 +126,17 @@ public IEnumerable GetVideoTracks() } /// - /// + /// Returns a list of AudioStreamTrack objects in the stream. /// - /// + /// + /// `GetAudioTracks` method returns a sequence that represents all the `AudioStreamTrack` objects in this stream's track set. + /// + /// List of `AudioStreamTrack` objects, one for each audio track contained in the stream. + /// + /// + /// public IEnumerable GetAudioTracks() { var buf = NativeMethods.MediaStreamGetAudioTracks(GetSelfOrThrow(), out ulong length); @@ -106,23 +144,37 @@ public IEnumerable GetAudioTracks() } /// - /// + /// Returns a list of MediaStreamTrack objects in the stream. /// - /// + /// + /// `GetTracks` method returns a sequence that represents all the `MediaStreamTrack` objects in this stream's track set. + /// + /// List of `MediaStreamTrack` objects. + /// + /// + /// public IEnumerable GetTracks() { return GetAudioTracks().Cast().Concat(GetVideoTracks()); } /// - /// Add a new track to the stream. + /// Add a new track to the stream. /// /// - /// This class keeps references of to avoid GC. - /// Please call the method when it's no longer needed. + /// `AddTrack` method adds a new track to the stream. + /// This class keeps references of to avoid GC. + /// Please call the method when it's no longer needed. /// - /// - /// + /// `MediaStreamTrack` object to add to the stream. + /// `true` if the track successfully added to the stream. + /// + /// + /// /// public bool AddTrack(MediaStreamTrack track) { @@ -131,10 +183,18 @@ public bool AddTrack(MediaStreamTrack track) } /// - /// Remove a new track to the stream. + /// Remove a track from the stream. /// - /// - /// + /// + /// `RemoveTrack` method removes a track from the stream. + /// + /// `MediaStreamTrack` object to remove from the stream. + /// `true` if the track successfully removed from the stream. + /// + /// + /// /// public bool RemoveTrack(MediaStreamTrack track) { @@ -143,8 +203,18 @@ public bool RemoveTrack(MediaStreamTrack track) } /// - /// + /// Creates a MediaStream instance. /// + /// + /// `MediaStream` constructor creates an instance of `MediaStream`, + /// which serves as a collection of media tracks, + /// each represented by a `MediaStreamTrack` object. + /// + /// + /// + /// public MediaStream() : this(WebRTC.Context.CreateMediaStream(Guid.NewGuid().ToString())) { } From ab8688dfc8a9544216dd13664e75b46a5de332a0 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Wed, 4 Dec 2024 14:12:14 +0900 Subject: [PATCH 05/67] Improve documentation of RTCConfiguration --- Runtime/Scripts/WebRTC.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index ae0e30ef33..6f20164335 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -531,30 +531,38 @@ public enum RTCIceTransportPolicy : int } /// - /// + /// Provides options to configure the new connection. /// + /// + /// `RTCConfiguration` struct provides options to configure the new connection. + /// + /// + /// + /// /// /// [Serializable] public struct RTCConfiguration { /// - /// + /// List of RTCIceServer objects, each describing one server which may be used by the ICE agent. /// public RTCIceServer[] iceServers; /// - /// + /// Represents the current ICE transport policy. /// public RTCIceTransportPolicy? iceTransportPolicy; /// - /// + /// Specifies how to handle negotiation of candidates when the remote peer is not compatible with the SDP BUNDLE standard. /// public RTCBundlePolicy? bundlePolicy; /// - /// + /// Specifies the size of the prefetched ICE candidate pool. /// public int? iceCandidatePoolSize; From 5e81a90cdb1a7ae28fe2b0d88e6f5d3d6dd6cf80 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Wed, 4 Dec 2024 12:16:26 +0900 Subject: [PATCH 06/67] Improve documentation of MediaStreamTrack --- Runtime/Scripts/MediaStreamTrack.cs | 49 ++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/Runtime/Scripts/MediaStreamTrack.cs b/Runtime/Scripts/MediaStreamTrack.cs index e633af3b2c..3752dc661c 100644 --- a/Runtime/Scripts/MediaStreamTrack.cs +++ b/Runtime/Scripts/MediaStreamTrack.cs @@ -4,13 +4,27 @@ namespace Unity.WebRTC { /// - /// + /// Represents a single media track within a stream. /// + /// + /// `MediaStreamTrack` represents a single media track within a stream. + /// Typically, these are audio or video tracks, but other track types may exist as well. + /// Each track is associated with a `MediaStream` object. + /// + /// + /// + /// + /// public class MediaStreamTrack : RefCountedObject { /// - /// + /// Boolean value that indicates whether the track is allowed to render the source stream. /// + /// + /// When the value is `true`, a track's data is output from the source to the destination; otherwise, empty frames are output. + /// public bool Enabled { get @@ -24,19 +38,19 @@ public bool Enabled } /// - /// + /// TrackState value that describes the status of the track. /// public TrackState ReadyState => NativeMethods.MediaStreamTrackGetReadyState(GetSelfOrThrow()); /// - /// + /// TrackKind value that describes the type of media for the track. /// public TrackKind Kind => NativeMethods.MediaStreamTrackGetKind(GetSelfOrThrow()); /// - /// + /// String containing a unique identifier (GUID) for the track. /// public string Id => NativeMethods.MediaStreamTrackGetID(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); @@ -47,16 +61,27 @@ internal MediaStreamTrack(IntPtr ptr) : base(ptr) } /// - /// + /// Finalizer for MediaStreamTrack. /// + /// + /// Ensures that resources are released by calling the `Dispose` method + /// ~MediaStreamTrack() { this.Dispose(); } /// - /// + /// Disposes of MediaStreamTrack. /// + /// + /// `Dispose` method disposes of the `MediaStreamTrack` and releases the associated resources. + /// + /// + /// + /// public override void Dispose() { if (this.disposed) @@ -72,8 +97,16 @@ public override void Dispose() } /// - /// Disassociate track from its source(video or audio), not for destroying the track + /// Stops the track. /// + /// + /// `Stop` method disassociates track from its source (video or audio), not for destroying the track. + /// + /// + /// + /// public void Stop() { WebRTC.Context.StopMediaStreamTrack(GetSelfOrThrow()); From 3ff2669a4d19c3b136b7554c3c97affefa784d79 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Wed, 4 Dec 2024 14:22:45 +0900 Subject: [PATCH 07/67] Improve documentation of AudioStreamTrack --- Runtime/Scripts/AudioStreamTrack.cs | 148 +++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 26 deletions(-) diff --git a/Runtime/Scripts/AudioStreamTrack.cs b/Runtime/Scripts/AudioStreamTrack.cs index ca7d8ae077..a7130c2f29 100644 --- a/Runtime/Scripts/AudioStreamTrack.cs +++ b/Runtime/Scripts/AudioStreamTrack.cs @@ -7,10 +7,22 @@ namespace Unity.WebRTC { /// - /// This event is called on audio thread. + /// Delegate to be called when received new audio data. /// - /// - /// + /// + /// `AudioReadEventHandler` is a delegate to be called when received new audio data. + /// + /// Float array containing audio data samples. + /// Number of audio channels. + /// Sample rate of the audio data + /// + /// + /// { + /// } + /// ]]> + /// + /// public delegate void AudioReadEventHandler(float[] data, int channels, int sampleRate); /// @@ -30,12 +42,22 @@ public static void SetTrack(this AudioSource source, AudioStreamTrack track) } /// - /// + /// Represents a single audio track within a stream. /// + /// + /// `AudioStreamTrack` is a `MediaStreamTrack` that represents a single audio track within a stream. + /// + /// + /// + /// + /// + /// public class AudioStreamTrack : MediaStreamTrack { /// - /// + /// AudioSource object. /// public AudioSource Source { @@ -182,17 +204,33 @@ internal void SetData(float[] data, int channels, int sampleRate) internal AudioTrackSource _trackSource; /// - /// + /// Creates a new AudioStreamTrack object. /// + /// + /// `AudioStreamTrack` constructor creates an instance of `AudioStreamTrack`. + /// + /// + /// + /// public AudioStreamTrack() : this(Guid.NewGuid().ToString(), new AudioTrackSource()) { } /// - /// + /// Creates a new AudioStreamTrack object. /// - /// + /// + /// `AudioStreamTrack` constructor creates an instance of `AudioStreamTrack` with a `source`. + /// + /// `AudioSource` object. + /// + /// + /// public AudioStreamTrack(AudioSource source) : this(Guid.NewGuid().ToString(), new AudioTrackSource()) { @@ -206,6 +244,18 @@ public AudioStreamTrack(AudioSource source) _audioCapturer.sender = true; } + /// + /// Creates a new AudioStreamTrack object. + /// + /// + /// `AudioStreamTrack` constructor creates an instance of `AudioStreamTrack` with a `listener`. + /// + /// `AudioListener` object. + /// + /// + /// public AudioStreamTrack(AudioListener listener) : this(Guid.NewGuid().ToString(), new AudioTrackSource()) { @@ -241,8 +291,16 @@ internal void RemoveSink(AudioStreamRenderer renderer) } /// - /// + /// Disposes of AudioStreamTrack. /// + /// + /// `Dispose` method disposes of the `AudioStreamTrack` and releases the associated resources. + /// + /// + /// + /// public override void Dispose() { if (this.disposed) @@ -270,11 +328,19 @@ public override void Dispose() } /// - /// + /// Provides the audio data to the track. /// - /// - /// - /// + /// + /// `SetData` method provides the audio data to the track. + /// + /// `NativeArray` containing audio data samples. + /// Number of audio channels. + /// Sample rate of the audio data + /// + /// + /// public void SetData(NativeArray.ReadOnly nativeArray, int channels, int sampleRate) { unsafe @@ -285,11 +351,19 @@ public void SetData(NativeArray.ReadOnly nativeArray, int channels, int s } /// - /// + /// Provides the audio data to the track. /// - /// - /// - /// + /// + /// `SetData` method provides the audio data to the track. + /// + /// `NativeSlice` containing audio data samples. + /// Number of audio channels. + /// Sample rate of the audio data + /// + /// + /// public void SetData(NativeSlice nativeSlice, int channels, int sampleRate) { unsafe @@ -310,11 +384,19 @@ static void ProcessAudio(AudioTrackSource source, IntPtr array, int sampleRate, } /// - /// + /// Provides the audio data to the track. /// - /// - /// - /// + /// + /// `SetData` method provides the audio data to the track. + /// + /// Float array containing audio data samples. + /// Number of audio channels. + /// Sample rate of the audio data + /// + /// + /// public void SetData(float[] array, int channels, int sampleRate) { if (array == null) @@ -332,11 +414,14 @@ public void SetData(float[] array, int channels, int sampleRate) // ReadOnlySpan is supported since .NET Standard 2.1. #if UNITY_2021_2_OR_NEWER /// - /// + /// Provides the audio data to the track. /// - /// - /// - /// + /// + /// `SetData` method provides the audio data to the track. + /// + /// `ReadOnlySpan` containing audio data samples. + /// Number of audio channels. + /// Sample rate of the audio data public void SetData(ReadOnlySpan span, int channels, int sampleRate) { unsafe @@ -350,8 +435,19 @@ public void SetData(ReadOnlySpan span, int channels, int sampleRate) #endif /// - /// + /// Event to be fired when received new audio data. /// + /// + /// `onReceived` event is fired when received new audio data. + /// + /// + /// + /// { + /// } + /// ]]> + /// + /// public event AudioReadEventHandler onReceived { add From 262489685d2a0c53c0faad86586dc6b99e04aa13 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Thu, 5 Dec 2024 10:21:40 +0900 Subject: [PATCH 08/67] Improve documentation of WebRTC --- Runtime/Scripts/WebRTC.cs | 109 ++++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 22 deletions(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index ae0e30ef33..db2197044f 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -621,8 +621,17 @@ public enum NativeLoggingSeverity }; /// - /// + /// Provides utilities and management functions for integrating WebRTC functionality. /// + /// + /// `WebRTC` class provides a set of static methods and properties to manage the WebRTC functionality. + /// + /// + /// + /// + /// public static class WebRTC { #if UNITY_IOS @@ -663,9 +672,17 @@ internal static void InitializeInternal(bool limitTextureSize = true, bool enabl } /// - /// + /// Updates the texture data for all video tracks at the end of each frame. /// - /// + /// + /// `Update` method updates the texture data for all video tracks at the end of each frame. + /// + /// `IEnumerator` to facilitate coroutine execution. + /// + /// + /// public static IEnumerator Update() { var instruction = new WaitForEndOfFrame(); @@ -705,15 +722,22 @@ public static IEnumerator Update() } /// - /// Executes any pending tasks generated asynchronously during the WebRTC runtime. + /// Executes any pending tasks generated asynchronously during the WebRTC runtime. /// + /// + /// `ExecutePendingTasks` method processes pending tasks generated during WebRTC operations, up to a specified timeout. + /// /// - /// The amount of time in milliseconds that the task queue can take before task execution will cease. + /// The amount of time in milliseconds that the task queue can take before task execution will cease. /// /// - /// true if all pending tasks were completed within milliseconds and false - /// otherwise. + /// `true` if all pending tasks were completed within milliseconds and `false` otherwise. /// + /// + /// + /// public static bool ExecutePendingTasks(int millisecondTimeout) { if (s_syncContext is ExecutableUnitySynchronizationContext executableContext) @@ -725,7 +749,7 @@ public static bool ExecutePendingTasks(int millisecondTimeout) } /// - /// + /// Controls whether texture size constraints are applied during WebRTC streaming. /// public static bool enableLimitTextureSize { @@ -734,8 +758,8 @@ public static bool enableLimitTextureSize } /// - /// Get & set the logger to use when logging debug messages inside the WebRTC package. - /// By default will use Debug.unityLogger. + /// Logger that is used for capturing debug messages within the WebRTC package. + /// Defaults to Debug.unityLogger. /// /// Throws if setting a null logger. public static ILogger Logger @@ -756,10 +780,18 @@ public static ILogger Logger } /// - /// Configure native logging settings for WebRTC. + /// Configures native logging settings for WebRTC. /// + /// + /// `ConfigureNativeLogging` method is used to enable or disable native logging and set the native logging level. + /// /// Enables or disable native logging. /// Sets the native logging level. + /// + /// + /// public static void ConfigureNativeLogging(bool enableNativeLogging, NativeLoggingSeverity nativeLoggingSeverity) { if (enableNativeLogging) @@ -860,9 +892,18 @@ internal static RTCError ValidateTextureSize(int width, int height, RuntimePlatf } /// - /// + /// Validates the specified graphics format to ensure it is supported. /// - /// + /// + /// `ValidateGraphicsFormat` method checks whether the provided `GraphicsFormat` is compatible with the current graphics device. + /// This method throws an `ArgumentException` if the format is not supported. + /// + /// `GraphicsFormat` value to be validated. + /// + /// + /// public static void ValidateGraphicsFormat(GraphicsFormat format) { // can't recognize legacy format @@ -883,10 +924,18 @@ public static void ValidateGraphicsFormat(GraphicsFormat format) } /// - /// + /// Determines the appropriate RenderTextureFormat for a given GraphicsDeviceType. /// - /// - /// + /// + /// `GetSupportedRenderTextureFormat` method determines the appropriate `RenderTextureFormat` for a given `GraphicsDeviceType`. + /// + /// `GraphicsDeviceType` for which `RenderTextureFormat` is being determined. + /// `RenderTextureFormat` value for the specified `GraphicsDeviceType`. + /// + /// + /// public static RenderTextureFormat GetSupportedRenderTextureFormat(GraphicsDeviceType type) { var graphicsFormat = GetSupportedGraphicsFormat(type); @@ -894,10 +943,18 @@ public static RenderTextureFormat GetSupportedRenderTextureFormat(GraphicsDevice } /// - /// + /// Determines the appropriate GraphicsFormat for a given GraphicsDeviceType. /// - /// - /// + /// + /// `GetSupportedGraphicsFormat` method determines the appropriate `GraphicsFormat` for a given `GraphicsDeviceType`. + /// + /// `GraphicsDeviceType` for which `GraphicsFormat` is being determined. + /// `GraphicsFormat` value for the specified `GraphicsDeviceType`. + /// + /// + /// public static GraphicsFormat GetSupportedGraphicsFormat(GraphicsDeviceType type) { if (QualitySettings.activeColorSpace == ColorSpace.Linear) @@ -937,10 +994,18 @@ public static GraphicsFormat GetSupportedGraphicsFormat(GraphicsDeviceType type) } /// - /// + /// Determines the appropriate TextureFormat for a given GraphicsDeviceType. /// - /// - /// + /// + /// `GetSupportedTextureFormat` method determines the appropriate `TextureFormat` for a given `GraphicsDeviceType`. + /// + /// `GraphicsDeviceType` for which `TextureFormat` is being determined. + /// `TextureFormat` value for the specified `GraphicsDeviceType`. + /// + /// + /// public static TextureFormat GetSupportedTextureFormat(GraphicsDeviceType type) { var graphicsFormat = GetSupportedGraphicsFormat(type); From bf79d5c9fd25108ac2c3c958aa749183a5e16b3b Mon Sep 17 00:00:00 2001 From: toshiyuki Date: Thu, 5 Dec 2024 14:03:42 +0900 Subject: [PATCH 09/67] On the way. --- Runtime/Scripts/RTCDataChannel.cs | 61 ++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 18d9a7f513..dd1cfe1b44 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -72,32 +72,57 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or } /// - /// + /// /// public delegate void DelegateOnOpen(); /// - /// + /// /// public delegate void DelegateOnClose(); /// - /// + /// /// /// public delegate void DelegateOnMessage(byte[] bytes); /// - /// + /// /// /// public delegate void DelegateOnDataChannel(RTCDataChannel channel); /// - /// + /// /// public delegate void DelegateOnError(RTCError error); /// - /// + /// Represents a network channel which can be used for bidirectional peer-to-peer transfers of arbitrary data. /// + /// + /// RTCDataChannel interface represents a network channel which can be used for bidirectional peer-to-peer transfers of arbitrary data. + /// Every data channel is associated with an RTCPeerConnection, and each peer connection can have up to a theoretical maximum of 65,534 data channels. + /// + /// To create a data channel and ask a remote peer to join you, call the RTCPeerConnection's createDataChannel() method. + /// The peer being invited to exchange data receives a datachannel event (which has type RTCDataChannelEvent) to let it know the data channel has been added to the connection. + /// + /// + /// { + /// Debug.LogFormat("received: {0}"`",${event.data}); + /// }; + /// + /// dc.OnOpen = () => { + /// Debug.Log("datachannel open"); + /// }; + /// + /// dc.OnClose = () => { + /// Debug.Log("datachannel close"); + /// }; + /// ]]> + /// /// public class RTCDataChannel : RefCountedObject { @@ -107,7 +132,7 @@ public class RTCDataChannel : RefCountedObject private DelegateOnError onError; /// - /// + /// Delegate to be called when a message has been received from the remote peer. /// public DelegateOnMessage OnMessage { @@ -116,7 +141,7 @@ public DelegateOnMessage OnMessage } /// - /// + /// Delegate to be called when the data channel's messages is opened or reopened. /// public DelegateOnOpen OnOpen { @@ -125,7 +150,7 @@ public DelegateOnOpen OnOpen } /// - /// + /// Delegate to be called when the data channel's messages is closed. /// public DelegateOnClose OnClose { @@ -134,7 +159,7 @@ public DelegateOnClose OnClose } /// - /// + /// Delegate to be called when the errors occur. /// public DelegateOnError OnError { @@ -252,7 +277,7 @@ internal RTCDataChannel(IntPtr ptr, RTCPeerConnection peerConnection) } /// - /// + /// /// ~RTCDataChannel() { @@ -260,7 +285,7 @@ internal RTCDataChannel(IntPtr ptr, RTCPeerConnection peerConnection) } /// - /// + /// /// public override void Dispose() { @@ -314,7 +339,7 @@ public void Send(byte[] msg) } /// - /// + /// /// /// /// @@ -333,7 +358,7 @@ public unsafe void Send(NativeArray msg) } /// - /// + /// /// /// /// @@ -350,7 +375,7 @@ public unsafe void Send(NativeSlice msg) #if UNITY_2020_1_OR_NEWER // ReadOnly support was introduced in 2020.1 /// - /// + /// /// /// /// @@ -366,7 +391,7 @@ public unsafe void Send(NativeArray.ReadOnly msg) #endif /// - /// + /// /// /// /// @@ -380,7 +405,7 @@ public unsafe void Send(void* msgPtr, int length) } /// - /// + /// /// /// /// @@ -397,7 +422,7 @@ public void Send(IntPtr msgPtr, int length) } /// - /// + /// /// public void Close() { From 5c4841a4a4d77086a6f5ff88f3b2918ee60b2475 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Thu, 5 Dec 2024 14:30:55 +0900 Subject: [PATCH 10/67] Improve documentation of RTCRtpTransceiver --- Runtime/Scripts/RTCRtpTransceiver.cs | 75 ++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/Runtime/Scripts/RTCRtpTransceiver.cs b/Runtime/Scripts/RTCRtpTransceiver.cs index fc60b10cac..ca1e08c768 100644 --- a/Runtime/Scripts/RTCRtpTransceiver.cs +++ b/Runtime/Scripts/RTCRtpTransceiver.cs @@ -3,8 +3,17 @@ namespace Unity.WebRTC { /// - /// + /// Describes a permanent pairing of an RTCRtpSender and an RTCRtpReceiver. /// + /// + /// `RTCRtpTransceiver` class is used to represent a permanent pairing of an `RTCRtpSender` and an `RTCRtpReceiver`, along with shared state. + /// + /// + /// + /// + /// public class RTCRtpTransceiver : RefCountedObject { private RTCPeerConnection peer; @@ -16,16 +25,27 @@ internal RTCRtpTransceiver(IntPtr ptr, RTCPeerConnection peer) : base(ptr) } /// - /// + /// Finalizer for RTCRtpTransceiver. /// + /// + /// Ensures that resources are released by calling the `Dispose` method + /// ~RTCRtpTransceiver() { this.Dispose(); } /// - /// + /// Disposes of RTCRtpTransceiver. /// + /// + /// `Dispose` method disposes of the `RTCRtpTransceiver` and releases the associated resources. + /// + /// + /// + /// public override void Dispose() { if (this.disposed) @@ -40,7 +60,7 @@ public override void Dispose() } /// - /// + /// Specifies the negotiated media ID (mid) that uniquely identifies the pairing of the sender and receiver agreed upon by local and remote peers. /// public string Mid { @@ -54,8 +74,7 @@ public string Mid } /// - /// This is used to set the transceiver's desired direction - /// and will be used in calls to CreateOffer and CreateAnswer. + /// This is used to set the transceiver's desired direction and will be used in calls to CreateOffer and CreateAnswer. /// public RTCRtpTransceiverDirection Direction { @@ -72,9 +91,9 @@ public RTCRtpTransceiverDirection Direction } /// - /// This property indicates the transceiver's current directionality, - /// or null if the transceiver is stopped or has never participated in an exchange of offers and answers. - /// To change the transceiver's directionality, set the value of the property. + /// This property indicates the transceiver's current directionality, + /// or null if the transceiver is stopped or has never participated in an exchange of offers and answers. + /// To change the transceiver's directionality, set the value of the Direction property. /// public RTCRtpTransceiverDirection? CurrentDirection { @@ -90,7 +109,7 @@ public RTCRtpTransceiverDirection? CurrentDirection } /// - /// + /// RTCRtpReceiver object that handles receiving and decoding incoming media data for the transceiver's stream. /// public RTCRtpReceiver Receiver { @@ -102,7 +121,7 @@ public RTCRtpReceiver Receiver } /// - /// + /// RTCRtpSender object that handles encoding and sending outgoing media data for the transceiver's stream. /// public RTCRtpSender Sender { @@ -114,10 +133,25 @@ public RTCRtpSender Sender } /// - /// + /// Specifies the codecs for decoding received data on this transceiver, arranged in order of decreasing preference. /// - /// - /// + /// + /// `SetCodecPreferences` method sets codec preferences for negotiating data encoding with a remote peer. + /// It requires reordering supported codecs by preference and applying them to influence the negotiation process. + /// When initiating an `RTCPeerConnection`, set codecs before calling `CreateOffer` or `CreateAnswer`. + /// Changing codecs during a session requires a new negotiation but does not automatically trigger the `OnNegotiationNeeded` event. + /// + /// + /// An array of `RTCRtpCodecCapability` objects arranged by preference to determine the codecs used for receiving and sending data streams. + /// If it is empty, the codec configurations are all reset to the defaults. + /// + /// `RTCErrorType` value. + /// + /// + /// + /// public RTCErrorType SetCodecPreferences(RTCRtpCodecCapability[] codecs) { RTCRtpCodecCapabilityInternal[] array = Array.ConvertAll(codecs, v => v.Cast()); @@ -132,9 +166,18 @@ public RTCErrorType SetCodecPreferences(RTCRtpCodecCapability[] codecs) } /// - /// + /// Stops the transceiver permanently by stopping both the associated RTCRtpSender and RTCRtpReceiver. /// - /// + /// + /// Calling `Stop` method stops the sender from sending media immediately, closes RTP streams with an RTCP "BYE" message, and the receiver stops receiving media. + /// The receiver's track ceases, and the transceiver's direction changes to `Stopped`. + /// + /// `RTCErrorType` value. + /// + /// + /// public RTCErrorType Stop() { return NativeMethods.TransceiverStop(self); From d82841c6c0c9c9e7176b7856ed0d31685540b185 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Thu, 5 Dec 2024 15:52:29 +0900 Subject: [PATCH 11/67] On the way. --- Runtime/Scripts/RTCDataChannel.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index dd1cfe1b44..0ab3c5a790 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -168,27 +168,27 @@ public DelegateOnError OnError } /// - /// + /// Returns an ID number (between 0 and 65,534) which uniquely identifies the RTCDataChannel. /// public int Id => NativeMethods.DataChannelGetID(GetSelfOrThrow()); /// - /// + /// Returns a string containing a name describing the data channel which are not required to be unique. /// public string Label => NativeMethods.DataChannelGetLabel(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// - /// + /// Returns a string containing the name of the sub protocol in use. If no protocol was specified when the data channel was created, then this property's value is the empty string (""). /// public string Protocol => NativeMethods.DataChannelGetProtocol(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// - /// + /// Returns the maximum number of times the browser should try to retransmit a message before giving up. /// public ushort MaxRetransmits => NativeMethods.DataChannelGetMaxRetransmits(GetSelfOrThrow()); /// - /// + /// Returns the amount of time, in milliseconds, the browser is allowed to take to attempt to transmit a message, as set when the data channel was created, or null. /// public ushort MaxRetransmitTime => NativeMethods.DataChannelGetMaxRetransmitTime(GetSelfOrThrow()); @@ -285,7 +285,7 @@ internal RTCDataChannel(IntPtr ptr, RTCPeerConnection peerConnection) } /// - /// + /// Release all the resources RTCDataChannel class created. /// public override void Dispose() { From 2e693250328fecd87b84084240b71605c2c423fc Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Thu, 5 Dec 2024 16:27:03 +0900 Subject: [PATCH 12/67] Improve documentation of RTCIceServer --- Runtime/Scripts/WebRTC.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index ae0e30ef33..d30e601d38 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -483,32 +483,42 @@ public enum RTCIceCredentialType } /// - /// + /// Represents a configuration for an ICE server used within WebRTC connections. /// - /// + /// + /// `RTCIceServer` struct represents a configuration for an ICE server used within WebRTC connections, + /// including authentication credentials and STUN/TURN server URLs. + /// + /// + /// + /// + /// /// [Serializable] public struct RTCIceServer { /// - /// + /// Specifies the credential for authenticating with the ICE server. /// [Tooltip("Optional: specifies the password to use when authenticating with the ICE server")] public string credential; /// - /// + /// Specifies the type of credential used. /// [Tooltip("What type of credential the `password` value")] public RTCIceCredentialType credentialType; /// - /// + /// An array containing the URLs of STUN or TURN servers to use for ICE negotiation. /// [Tooltip("Array to set URLs of your STUN/TURN servers")] public string[] urls; /// - /// + /// Specifies the user name for authenticating with the ICE server. /// [Tooltip("Optional: specifies the username to use when authenticating with the ICE server")] public string username; From cd0b433d4b32c2c7a7d44f5a5426b3b73ef215c7 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Thu, 5 Dec 2024 18:15:57 +0900 Subject: [PATCH 13/67] Refine examples --- Runtime/Scripts/RTCPeerConnection.cs | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Runtime/Scripts/RTCPeerConnection.cs b/Runtime/Scripts/RTCPeerConnection.cs index 3d313db818..707d57b3c4 100644 --- a/Runtime/Scripts/RTCPeerConnection.cs +++ b/Runtime/Scripts/RTCPeerConnection.cs @@ -779,6 +779,12 @@ public bool AddIceCandidate(RTCIceCandidate candidate) /// /// /// /// @@ -800,6 +806,12 @@ public RTCSessionDescriptionAsyncOperation CreateOffer(ref RTCOfferAnswerOptions /// /// /// public RTCSessionDescriptionAsyncOperation CreateOffer() @@ -821,6 +833,12 @@ public RTCSessionDescriptionAsyncOperation CreateOffer() /// /// /// public RTCSessionDescriptionAsyncOperation CreateAnswer(ref RTCOfferAnswerOptions options) @@ -841,6 +859,12 @@ public RTCSessionDescriptionAsyncOperation CreateAnswer(ref RTCOfferAnswerOption /// /// /// public RTCSessionDescriptionAsyncOperation CreateAnswer() @@ -891,6 +915,11 @@ public RTCDataChannel CreateDataChannel(string label, RTCDataChannelInit options /// /// /// /// @@ -925,6 +954,11 @@ public RTCSetSessionDescriptionAsyncOperation SetLocalDescription( /// /// /// public RTCSetSessionDescriptionAsyncOperation SetLocalDescription() @@ -947,6 +981,11 @@ public RTCSetSessionDescriptionAsyncOperation SetLocalDescription() /// /// /// /// From 32eca46435d2f452efb99f0cb5ae2fb702e9deb1 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Thu, 5 Dec 2024 19:02:54 +0900 Subject: [PATCH 14/67] On the way. --- Runtime/Scripts/RTCDataChannel.cs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 0ab3c5a790..7d4a51c9ae 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -6,7 +6,7 @@ namespace Unity.WebRTC { /// - /// + /// Represents argument parameters for RTCPeerConnection.CreateDataChannel() /// /// public class RTCDataChannelInit @@ -193,12 +193,12 @@ public DelegateOnError OnError public ushort MaxRetransmitTime => NativeMethods.DataChannelGetMaxRetransmitTime(GetSelfOrThrow()); /// - /// + /// Indicates whether or not the data channel guarantees in-order delivery of messages. /// public bool Ordered => NativeMethods.DataChannelGetOrdered(GetSelfOrThrow()); /// - /// + /// Returns the number of bytes of data currently queued to be sent over the data channel. /// public ulong BufferedAmount => NativeMethods.DataChannelGetBufferedAmount(GetSelfOrThrow()); @@ -208,7 +208,7 @@ public DelegateOnError OnError public bool Negotiated => NativeMethods.DataChannelGetNegotiated(GetSelfOrThrow()); /// - /// The property returns an enum of the RTCDataChannelState which shows + /// Returns an enum of the RTCDataChannelState which shows /// the state of the channel. /// /// @@ -285,7 +285,7 @@ internal RTCDataChannel(IntPtr ptr, RTCPeerConnection peerConnection) } /// - /// Release all the resources RTCDataChannel class created. + /// Release all the resources RTCDataChannel instance has allocated. /// public override void Dispose() { @@ -303,7 +303,7 @@ public override void Dispose() } /// - /// The method Sends data across the data channel to the remote peer. + /// Sends data across the data channel to the remote peer. /// /// /// The method throws InvalidOperationException when @@ -321,7 +321,7 @@ public void Send(string msg) } /// - /// The method Sends data across the data channel to the remote peer. + /// Sends data across the data channel to the remote peer. /// /// /// The method throws InvalidOperationException when @@ -339,7 +339,7 @@ public void Send(byte[] msg) } /// - /// + /// Sends data across the data channel to the remote peer. /// /// /// @@ -358,7 +358,7 @@ public unsafe void Send(NativeArray msg) } /// - /// + /// Sends data across the data channel to the remote peer. /// /// /// @@ -375,7 +375,7 @@ public unsafe void Send(NativeSlice msg) #if UNITY_2020_1_OR_NEWER // ReadOnly support was introduced in 2020.1 /// - /// + /// Sends data across the data channel to the remote peer. /// /// /// @@ -391,7 +391,7 @@ public unsafe void Send(NativeArray.ReadOnly msg) #endif /// - /// + /// Sends data across the data channel to the remote peer. /// /// /// @@ -405,7 +405,7 @@ public unsafe void Send(void* msgPtr, int length) } /// - /// + /// Sends data across the data channel to the remote peer. /// /// /// @@ -422,7 +422,7 @@ public void Send(IntPtr msgPtr, int length) } /// - /// + /// Closes the RTCDataChannel. Either peer is permitted to call this method to initiate closure of the channel. /// public void Close() { From f64a20faf2b561b9efb74b853106854d57747fb9 Mon Sep 17 00:00:00 2001 From: toshiyuki Date: Fri, 6 Dec 2024 09:55:49 +0900 Subject: [PATCH 15/67] Added summary for RTCDataChannelInit class. --- Runtime/Scripts/RTCDataChannel.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 7d4a51c9ae..f2ce0500a1 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -6,17 +6,17 @@ namespace Unity.WebRTC { /// - /// Represents argument parameters for RTCPeerConnection.CreateDataChannel() + /// Provides configuration options for the data channel. /// /// public class RTCDataChannelInit { /// - /// + /// Indicates whether or not the data channel guarantees in-order delivery of messages. /// public bool? ordered; /// - /// + /// Represents the maximum number of milliseconds that attempts to transfer a message may take in unreliable mode.. /// /// /// Cannot be set along with . @@ -24,7 +24,7 @@ public class RTCDataChannelInit /// public int? maxPacketLifeTime; /// - /// + /// Represents the maximum number of times the user agent should attempt to retransmit a message which fails the first time in unreliable mode. /// /// /// Cannot be set along with . @@ -32,15 +32,15 @@ public class RTCDataChannelInit /// public int? maxRetransmits; /// - /// + /// Provides the name of the sub-protocol being used on the RTCDataChannel. /// public string protocol; /// - /// + /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app (true) or by the WebRTC layer (false). /// public bool? negotiated; /// - /// + /// Indicates a 16-bit numeric ID for the channel. /// public int? id; } From 8f0d3ee0457e169c1e6a394a12d9215109d21b57 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 11:35:39 +0900 Subject: [PATCH 16/67] Added Exceptions doc to Send(). --- Runtime/Scripts/RTCDataChannel.cs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index f2ce0500a1..cdf677d558 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -341,6 +341,10 @@ public void Send(byte[] msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// The method throws InvalidOperationException when + /// is not Open. + /// /// /// public unsafe void Send(NativeArray msg) @@ -360,6 +364,10 @@ public unsafe void Send(NativeArray msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// The method throws InvalidOperationException when + /// is not Open. + /// /// /// public unsafe void Send(NativeSlice msg) @@ -377,7 +385,10 @@ public unsafe void Send(NativeSlice msg) /// /// Sends data across the data channel to the remote peer. /// - /// + /// + /// The method throws InvalidOperationException when + /// is not Open. + /// /// /// public unsafe void Send(NativeArray.ReadOnly msg) where T : struct @@ -393,6 +404,10 @@ public unsafe void Send(NativeArray.ReadOnly msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// The method throws InvalidOperationException when + /// is not Open. + /// /// /// public unsafe void Send(void* msgPtr, int length) @@ -407,7 +422,10 @@ public unsafe void Send(void* msgPtr, int length) /// /// Sends data across the data channel to the remote peer. /// - /// + /// + /// The method throws InvalidOperationException when + /// is not Open. + /// /// /// public void Send(IntPtr msgPtr, int length) { From ad2eb420a57dccac928bd2b5029e519cae314087 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Thu, 5 Dec 2024 16:45:15 +0900 Subject: [PATCH 17/67] Improve documentation of RTCRtpSender --- Runtime/Scripts/RTCRtpSender.cs | 144 ++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 18 deletions(-) diff --git a/Runtime/Scripts/RTCRtpSender.cs b/Runtime/Scripts/RTCRtpSender.cs index 31820e9be3..50b13619b9 100644 --- a/Runtime/Scripts/RTCRtpSender.cs +++ b/Runtime/Scripts/RTCRtpSender.cs @@ -5,8 +5,17 @@ namespace Unity.WebRTC { /// - /// + /// Provides the ability to control and access to details on encoding and sending a MediaStreamTrack to a remote peer. /// + /// + /// `RTCRtpSender` class allows customization of media encoding and transmission to a remote peer. + /// It provides access to the device's media capabilities and supports sending DTMF tones for telephony interactions. + /// + /// + /// + /// public class RTCRtpSender : RefCountedObject { private RTCPeerConnection peer; @@ -19,16 +28,27 @@ internal RTCRtpSender(IntPtr ptr, RTCPeerConnection peer) : base(ptr) } /// - /// + /// Finalizer for RTCRtpSender. /// + /// + /// Ensures that resources are released by calling the `Dispose` method + /// ~RTCRtpSender() { this.Dispose(); } /// - /// + /// Disposes of RTCRtpSender. /// + /// + /// `Dispose` method disposes of the `RTCRtpSender` and releases the associated resources. + /// + /// + /// + /// public override void Dispose() { if (this.disposed) @@ -43,10 +63,24 @@ public override void Dispose() } /// - /// + /// Provides a `RTCRtpCapabilities` object describing the codec and header extension capabilities. /// - /// - /// + /// + /// `GetCapabilities` method provides a `RTCRtpCapabilities` object that describes the codec and header extension capabilities supported by `RTCRtpSender`. + /// + /// `TrackKind` value indicating the type of media. + /// `RTCRtpCapabilities` object contains an array of `RTCRtpCodecCapability` objects. + /// + /// + /// public static RTCRtpCapabilities GetCapabilities(TrackKind kind) { WebRTC.Context.GetSenderCapabilities(kind, out IntPtr ptr); @@ -58,16 +92,37 @@ public static RTCRtpCapabilities GetCapabilities(TrackKind kind) } /// - /// + /// Asynchronously requests statistics about outgoing traffic on the RTCPeerConnection associated with the RTCRtpSender. /// - /// + /// + /// `GetStats` method asynchronously requests an `RTCStatsReport` containing statistics about the outgoing traffic for the `RTCPeerConnection` associated with the `RTCRtpSender`. + /// + /// `RTCStatsReportAsyncOperation` object containing `RTCStatsReport` object. + /// + /// + /// str + next.Key + ":" + (next.Value == null ? string.Empty : next.Value.ToString()) + "\n"); + /// Debug.Log(statsText); + /// statsReport.Dispose(); + /// } + /// ]]> + /// public RTCStatsReportAsyncOperation GetStats() { return peer.GetStats(this); } /// - /// + /// MediaStreamTrack managed by RTCRtpSender. If it is null, no transmission occurs. /// public MediaStreamTrack Track { @@ -80,6 +135,10 @@ public MediaStreamTrack Track } } + /// + /// RTCRtpScriptTransform used to insert a transform stream in a worker thread into the sender pipeline, + /// enabling transformations on encoded video and audio frames after output by a codec but before transmission. + /// public RTCRtpTransform Transform { set @@ -98,7 +157,7 @@ public RTCRtpTransform Transform } /// - /// + /// Indicates if the video track's framerate is synchronized with the application's framerate. /// public bool SyncApplicationFramerate { @@ -135,9 +194,25 @@ public bool SyncApplicationFramerate } /// - /// + /// Retrieves the current configuration of the RTCRtpSender. /// - /// + /// + /// `GetParameters` method retrieves `RTCRtpSendParameters` object describing the current configuration of the `RTCRtpSender`. + /// + /// `RTCRtpSendParameters` object containing the current configuration of the `RTCRtpSender`. + /// + /// + /// + /// public RTCRtpSendParameters GetParameters() { NativeMethods.SenderGetParameters(GetSelfOrThrow(), out var ptr); @@ -148,10 +223,30 @@ public RTCRtpSendParameters GetParameters() } /// - /// + /// Updates the configuration of the sender's track. /// - /// - /// + /// + /// `SetParameters` method updates the configuration of the sender's `MediaStreamTrack` + /// by applying changes the RTP transmission and the encoding parameters for a specific outgoing media on the connection. + /// + /// + /// A `RTCRtpSendParameters` object previously obtained by calling the sender's `GetParameters`, + /// includes desired configuration changes and potential codecs for encoding the sender's track. + /// + /// `RTCError` value. + /// + /// + /// + /// public RTCError SetParameters(RTCRtpSendParameters parameters) { if (Track is VideoStreamTrack videoTrack) @@ -182,10 +277,23 @@ public RTCError SetParameters(RTCRtpSendParameters parameters) } /// - /// + /// Replaces the current source track with a new MediaStreamTrack. /// - /// - /// + /// + /// `ReplaceTrack` method replaces the track currently being used as the sender's source with a new `MediaStreamTrack`. + /// It is often used to switch between two cameras. + /// + /// + /// A `MediaStreamTrack` to replace the current source track of the `RTCRtpSender`. + /// The new track must be the same type as the current one. + /// + /// `true` if the track has been successfully replaced. + /// + /// + /// public bool ReplaceTrack(MediaStreamTrack track) { IntPtr trackPtr = track?.GetSelfOrThrow() ?? IntPtr.Zero; From ee7ff4abc9b3051a68744a1f3c84c4d643fec1a7 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 12:36:31 +0900 Subject: [PATCH 18/67] Improved the example in RTCDataChannel. --- Runtime/Scripts/RTCDataChannel.cs | 39 +++++++++++++++++++------------ 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index cdf677d558..fe20fdc01a 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -36,7 +36,7 @@ public class RTCDataChannelInit /// public string protocol; /// - /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app (true) or by the WebRTC layer (false). + /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app or by the WebRTC layer. /// public bool? negotiated; /// @@ -72,27 +72,35 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or } /// - /// + /// Specify the delegate type for OnOpen. /// + /// public delegate void DelegateOnOpen(); + /// - /// + /// Specify the delegate type for OnClose. /// + /// public delegate void DelegateOnClose(); + /// - /// + /// Specify the delegate type for OnMessage. /// /// + /// public delegate void DelegateOnMessage(byte[] bytes); + /// - /// + /// Specify the delegate type for RTCPeerConnection.OnDataChannel. /// /// + /// public delegate void DelegateOnDataChannel(RTCDataChannel channel); /// - /// + /// Specify the delegate type for OnDataChannel. /// + /// public delegate void DelegateOnError(RTCError error); /// @@ -107,19 +115,20 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// /// /// { - /// Debug.LogFormat("received: {0}"`",${event.data}); + /// dataChennel.OnMessage = (event) => { + /// Debug.LogFormat("Received: {0}.",${event.data}); /// }; /// - /// dc.OnOpen = () => { - /// Debug.Log("datachannel open"); + /// dataChennel.OnOpen = () => { + /// Debug.Log("DataChannel opened."); /// }; /// - /// dc.OnClose = () => { - /// Debug.Log("datachannel close"); + /// dataChennel.OnClose = () => { + /// Debug.Log("DataChannel closed."); /// }; /// ]]> /// @@ -203,7 +212,7 @@ public DelegateOnError OnError public ulong BufferedAmount => NativeMethods.DataChannelGetBufferedAmount(GetSelfOrThrow()); /// - /// + /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app or by the WebRTC layer. /// public bool Negotiated => NativeMethods.DataChannelGetNegotiated(GetSelfOrThrow()); From 8bb3a9a6354ad54981bc2614a5cc0310af4125fe Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 13:37:58 +0900 Subject: [PATCH 19/67] Improve examples --- Runtime/Scripts/RTCPeerConnection.cs | 178 +++++++++++++++++++-------- 1 file changed, 127 insertions(+), 51 deletions(-) diff --git a/Runtime/Scripts/RTCPeerConnection.cs b/Runtime/Scripts/RTCPeerConnection.cs index 707d57b3c4..48071fb517 100644 --- a/Runtime/Scripts/RTCPeerConnection.cs +++ b/Runtime/Scripts/RTCPeerConnection.cs @@ -18,6 +18,7 @@ namespace Unity.WebRTC /// /// { + /// otherPeerConnection.AddIceCandidate(candidate); /// } /// ]]> /// @@ -35,6 +36,13 @@ namespace Unity.WebRTC /// /// { + /// if (state == RTCIceConnectionState.Connected || state == RTCIceConnectionState.Completed) + /// { + /// foreach (RTCRtpSender sender in peerConnection.GetSenders()) + /// { + /// sender.SyncApplicationFramerate = true; + /// } + /// } /// } /// ]]> /// @@ -52,6 +60,7 @@ namespace Unity.WebRTC /// /// { + /// Debug.Log($"Connection state changed to {state}"); /// } /// ]]> /// @@ -69,6 +78,10 @@ namespace Unity.WebRTC /// /// { + /// if (state == RTCIceGatheringState.Complete) + /// { + /// GameObject newCandidate = Instantiate(candidateElement, candidateParent); + /// } /// } /// ]]> /// @@ -85,6 +98,20 @@ namespace Unity.WebRTC /// /// { + /// StartCoroutine(NegotiationProcess()); + /// } + /// + /// IEnumerator NegotiationProcess() + /// { + /// RTCSessionDescriptionAsyncOperation asyncOperation = peerConnection.CreateOffer(); + /// yield return asyncOperation; + /// + /// if (!asyncOperation.IsError) + /// { + /// RTCSessionDescription description = asyncOperation.Desc; + /// RTCSetSessionDescriptionAsyncOperation asyncOperation = peerConnection.SetLocalDescription(ref description); + /// yield return asyncOperation; + /// } /// } /// ]]> /// @@ -101,8 +128,10 @@ namespace Unity.WebRTC /// `RTCTrackEvent` object. /// /// /// { + /// receiveStream.AddTrack(e.Track); /// } /// ]]> /// @@ -122,7 +151,7 @@ namespace Unity.WebRTC /// /// /// /// /// @@ -199,10 +228,10 @@ private void DisposeAllTransceivers() /// enum. /// /// - /// - /// var peerConnection = new RTCPeerConnection(configuration); - /// var iceConnectionState = peerConnection.IceConnectionState; - /// + /// /// /// public RTCIceConnectionState IceConnectionState => NativeMethods.PeerConnectionIceConditionState(GetSelfOrThrow()); @@ -213,10 +242,10 @@ private void DisposeAllTransceivers() /// enum. /// /// - /// - /// var peerConnection = new RTCPeerConnection(configuration); - /// var connectionState = peerConnection.ConnectionState; - /// + /// /// /// public RTCPeerConnectionState ConnectionState => NativeMethods.PeerConnectionState(GetSelfOrThrow()); @@ -227,10 +256,10 @@ private void DisposeAllTransceivers() /// enum. /// /// - /// - /// var peerConnection = new RTCPeerConnection(configuration); - /// var signalingState = peerConnection.SignalingState; - /// + /// /// /// public RTCSignalingState SignalingState => NativeMethods.PeerConnectionSignalingState(GetSelfOrThrow()); @@ -248,7 +277,7 @@ private void DisposeAllTransceivers() /// /// /// receivers = peerConnection.GetReceivers(); /// ]]> /// /// An array of `RTCRtpReceiver` objects, one for each track on the connection. @@ -268,7 +297,14 @@ public IEnumerable GetReceivers() /// /// /// /// /// An array of `RTCRtpSender` objects, one for each track on the connection. @@ -288,7 +324,13 @@ public IEnumerable GetSenders() /// /// /// /// /// An array of the `RTCRtpTransceiver` objects representing the transceivers handling sending and receiving all media on the `RTCPeerConnection`. @@ -489,7 +531,7 @@ static void PCOnRemoveTrack(IntPtr ptr, IntPtr receiverPtr) /// An object describing the 's current configuration. /// /// Error code. /// /// /// /// /// /// @@ -581,7 +623,7 @@ public RTCPeerConnection() /// /// /// /// /// `RTCConfiguration` object to configure the new connection. @@ -662,7 +704,9 @@ public void Close() /// `RTCRtpSender` object which is used to transmit the media data. /// /// /// /// @@ -691,7 +735,7 @@ public RTCRtpSender AddTrack(MediaStreamTrack track, MediaStream stream = null) /// `RTCErrorType` value. /// /// /// /// @@ -713,7 +757,9 @@ public RTCErrorType RemoveTrack(RTCRtpSender sender) /// `RTCRtpTransceiver` object which is used to exchange the media data. /// /// /// public RTCRtpTransceiver AddTransceiver(MediaStreamTrack track, RTCRtpTransceiverInit init = null) @@ -737,7 +783,9 @@ public RTCRtpTransceiver AddTransceiver(MediaStreamTrack track, RTCRtpTransceive /// `RTCRtpTransceiver` object which is used to exchange the media data. /// /// /// public RTCRtpTransceiver AddTransceiver(TrackKind kind, RTCRtpTransceiverInit init = null) @@ -758,9 +806,14 @@ public RTCRtpTransceiver AddTransceiver(TrackKind kind, RTCRtpTransceiverInit in /// If the value is null, the added ICE candidate is an "end-of-candidates" indicator. /// /// `true` if the candidate has been successfully added to the remote peer's description by the ICE agent. - /// + /// + /// + /// { + /// bool result = otherPeerConnection.AddIceCandidate(candidate); + /// } + /// ]]> + /// public bool AddIceCandidate(RTCIceCandidate candidate) { return NativeMethods.PeerConnectionAddIceCandidate( @@ -778,12 +831,14 @@ public bool AddIceCandidate(RTCIceCandidate candidate) /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. /// /// /// @@ -805,12 +860,14 @@ public RTCSessionDescriptionAsyncOperation CreateOffer(ref RTCOfferAnswerOptions /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. /// /// /// @@ -832,12 +889,14 @@ public RTCSessionDescriptionAsyncOperation CreateOffer() /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. /// /// /// @@ -858,12 +917,14 @@ public RTCSessionDescriptionAsyncOperation CreateAnswer(ref RTCOfferAnswerOption /// `RTCSessionDescriptionAsyncOperation` object containing `RTCSessionDescription` object. /// /// /// @@ -914,11 +975,14 @@ public RTCDataChannel CreateDataChannel(string label, RTCDataChannelInit options /// /// /// /// @@ -953,11 +1017,20 @@ public RTCSetSessionDescriptionAsyncOperation SetLocalDescription( /// /// /// /// { + /// StartCoroutine(NegotiationProcess()); + /// } + /// + /// IEnumerator NegotiationProcess() + /// { + /// RTCSetSessionDescriptionAsyncOperation asyncOperation = peerConnection.SetLocalDescription(ref description); + /// yield return asyncOperation; + /// + /// if (asyncOperation.IsError) + /// { + /// Debug.LogError("Failed to set local description: " + asyncOperation.Error.message); + /// } /// } /// ]]> /// @@ -980,11 +1053,14 @@ public RTCSetSessionDescriptionAsyncOperation SetLocalDescription() /// /// /// /// @@ -1020,13 +1096,13 @@ public RTCSetSessionDescriptionAsyncOperation SetRemoteDescription( /// /// Date: Fri, 6 Dec 2024 13:59:18 +0900 Subject: [PATCH 20/67] Improve examples --- Runtime/Scripts/VideoStreamTrack.cs | 30 +++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Runtime/Scripts/VideoStreamTrack.cs b/Runtime/Scripts/VideoStreamTrack.cs index 657d6669d6..251977c4a3 100644 --- a/Runtime/Scripts/VideoStreamTrack.cs +++ b/Runtime/Scripts/VideoStreamTrack.cs @@ -16,9 +16,13 @@ namespace Unity.WebRTC /// `Texture` object where the video stream is rendered. /// /// - /// { - /// } + /// [SerializeField] + /// RawImage receivedImage; + /// + /// videoStreamTrack.OnVideoReceived += (texture) => + /// { + /// receivedImage.texture = texture; + /// } /// ]]> /// /// @@ -32,6 +36,12 @@ namespace Unity.WebRTC /// /// Source `Texture` object. /// Destination `Texture` object. + /// + /// + /// + /// public delegate void CopyTexture(Texture source, RenderTexture dest); /// @@ -42,7 +52,7 @@ namespace Unity.WebRTC /// /// /// /// /// @@ -143,9 +153,13 @@ public IntPtr TexturePtr /// /// /// - /// { - /// } + /// [SerializeField] + /// RawImage receivedImage; + /// + /// videoStreamTrack.OnVideoReceived += (texture) => + /// { + /// receivedImage.texture = texture; + /// } /// ]]> /// /// @@ -200,7 +214,7 @@ internal void UpdateTexture() /// /// /// /// /// From 5fc363f922aaffdd4d3123f1fd6a96ca1703cc58 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 14:16:11 +0900 Subject: [PATCH 21/67] Added some Remarks. --- Runtime/Scripts/RTCDataChannel.cs | 101 ++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index fe20fdc01a..1b50ab0560 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -74,18 +74,27 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// /// Specify the delegate type for OnOpen. /// + /// + /// + /// /// public delegate void DelegateOnOpen(); /// /// Specify the delegate type for OnClose. /// + /// + /// + /// /// public delegate void DelegateOnClose(); /// /// Specify the delegate type for OnMessage. /// + /// + /// + /// /// /// public delegate void DelegateOnMessage(byte[] bytes); @@ -93,6 +102,9 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// /// Specify the delegate type for RTCPeerConnection.OnDataChannel. /// + /// + /// + /// /// /// public delegate void DelegateOnDataChannel(RTCDataChannel channel); @@ -100,6 +112,9 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// /// Specify the delegate type for OnDataChannel. /// + /// + /// + /// /// public delegate void DelegateOnError(RTCError error); @@ -143,6 +158,9 @@ public class RTCDataChannel : RefCountedObject /// /// Delegate to be called when a message has been received from the remote peer. /// + /// + /// + /// public DelegateOnMessage OnMessage { get => onMessage; @@ -152,6 +170,9 @@ public DelegateOnMessage OnMessage /// /// Delegate to be called when the data channel's messages is opened or reopened. /// + /// + /// + /// public DelegateOnOpen OnOpen { get => onOpen; @@ -161,6 +182,9 @@ public DelegateOnOpen OnOpen /// /// Delegate to be called when the data channel's messages is closed. /// + /// + /// + /// public DelegateOnClose OnClose { get => onClose; @@ -170,6 +194,9 @@ public DelegateOnClose OnClose /// /// Delegate to be called when the errors occur. /// + /// + /// + /// public DelegateOnError OnError { get => onError; @@ -179,41 +206,65 @@ public DelegateOnError OnError /// /// Returns an ID number (between 0 and 65,534) which uniquely identifies the RTCDataChannel. /// + /// + /// + /// public int Id => NativeMethods.DataChannelGetID(GetSelfOrThrow()); /// /// Returns a string containing a name describing the data channel which are not required to be unique. /// + /// + /// + /// public string Label => NativeMethods.DataChannelGetLabel(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// /// Returns a string containing the name of the sub protocol in use. If no protocol was specified when the data channel was created, then this property's value is the empty string (""). /// + /// + /// + /// public string Protocol => NativeMethods.DataChannelGetProtocol(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// /// Returns the maximum number of times the browser should try to retransmit a message before giving up. /// + /// + /// + /// public ushort MaxRetransmits => NativeMethods.DataChannelGetMaxRetransmits(GetSelfOrThrow()); /// /// Returns the amount of time, in milliseconds, the browser is allowed to take to attempt to transmit a message, as set when the data channel was created, or null. /// + /// + /// + /// public ushort MaxRetransmitTime => NativeMethods.DataChannelGetMaxRetransmitTime(GetSelfOrThrow()); /// /// Indicates whether or not the data channel guarantees in-order delivery of messages. /// + /// + /// + /// public bool Ordered => NativeMethods.DataChannelGetOrdered(GetSelfOrThrow()); /// /// Returns the number of bytes of data currently queued to be sent over the data channel. /// + /// + /// + /// public ulong BufferedAmount => NativeMethods.DataChannelGetBufferedAmount(GetSelfOrThrow()); /// /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app or by the WebRTC layer. /// + /// + /// + /// public bool Negotiated => NativeMethods.DataChannelGetNegotiated(GetSelfOrThrow()); /// @@ -296,6 +347,9 @@ internal RTCDataChannel(IntPtr ptr, RTCPeerConnection peerConnection) /// /// Release all the resources RTCDataChannel instance has allocated. /// + /// + /// + /// public override void Dispose() { if (this.disposed) @@ -314,6 +368,12 @@ public override void Dispose() /// /// Sends data across the data channel to the remote peer. /// + /// + /// Sends data across the data channel to the remote peer. + /// This can be done any time except during the initial process of creating the underlying transport channel. + /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), + /// and is also buffered if sent while the connection is closing or closed. + /// /// /// The method throws InvalidOperationException when /// is not Open. @@ -332,6 +392,12 @@ public void Send(string msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// Sends data across the data channel to the remote peer. + /// This can be done any time except during the initial process of creating the underlying transport channel. + /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), + /// and is also buffered if sent while the connection is closing or closed. + /// /// /// The method throws InvalidOperationException when /// is not Open. @@ -350,6 +416,12 @@ public void Send(byte[] msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// Sends data across the data channel to the remote peer. + /// This can be done any time except during the initial process of creating the underlying transport channel. + /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), + /// and is also buffered if sent while the connection is closing or closed. + /// /// /// The method throws InvalidOperationException when /// is not Open. @@ -373,6 +445,12 @@ public unsafe void Send(NativeArray msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// Sends data across the data channel to the remote peer. + /// This can be done any time except during the initial process of creating the underlying transport channel. + /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), + /// and is also buffered if sent while the connection is closing or closed. + /// /// /// The method throws InvalidOperationException when /// is not Open. @@ -394,6 +472,12 @@ public unsafe void Send(NativeSlice msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// Sends data across the data channel to the remote peer. + /// This can be done any time except during the initial process of creating the underlying transport channel. + /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), + /// and is also buffered if sent while the connection is closing or closed. + /// /// /// The method throws InvalidOperationException when /// is not Open. @@ -413,6 +497,12 @@ public unsafe void Send(NativeArray.ReadOnly msg) /// /// Sends data across the data channel to the remote peer. /// + /// + /// Sends data across the data channel to the remote peer. + /// This can be done any time except during the initial process of creating the underlying transport channel. + /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), + /// and is also buffered if sent while the connection is closing or closed. + /// /// /// The method throws InvalidOperationException when /// is not Open. @@ -431,6 +521,12 @@ public unsafe void Send(void* msgPtr, int length) /// /// Sends data across the data channel to the remote peer. /// + /// + /// Sends data across the data channel to the remote peer. + /// This can be done any time except during the initial process of creating the underlying transport channel. + /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), + /// and is also buffered if sent while the connection is closing or closed. + /// /// /// The method throws InvalidOperationException when /// is not Open. @@ -451,6 +547,11 @@ public void Send(IntPtr msgPtr, int length) /// /// Closes the RTCDataChannel. Either peer is permitted to call this method to initiate closure of the channel. /// + /// + /// Closes the RTCDataChannel. Either peer is permitted to call this method to initiate closure of the channel. + /// Closure of the data channel is not instantaneous. Most of the process of closing the connection is handled asynchronously; + /// you can detect when the channel has finished closing by watching for a close event on the data channel. + /// public void Close() { NativeMethods.DataChannelClose(GetSelfOrThrow()); From 7d4475b77fd28c45d732805a40de7c345d827fef Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 14:17:00 +0900 Subject: [PATCH 22/67] Improve examples --- Runtime/Scripts/MediaStream.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Runtime/Scripts/MediaStream.cs b/Runtime/Scripts/MediaStream.cs index 5f4784b0ae..9e9b29137c 100644 --- a/Runtime/Scripts/MediaStream.cs +++ b/Runtime/Scripts/MediaStream.cs @@ -26,7 +26,7 @@ namespace Unity.WebRTC /// /// /// /// /// @@ -116,7 +116,7 @@ public DelegateOnRemoveTrack OnRemoveTrack /// List of `MediaStreamTrack` objects, one for each video track contained in the media stream. /// /// videoTracks = mediaStream.GetVideoTracks(); /// ]]> /// public IEnumerable GetVideoTracks() @@ -134,7 +134,7 @@ public IEnumerable GetVideoTracks() /// List of `AudioStreamTrack` objects, one for each audio track contained in the stream. /// /// audioTracks = mediaStream.GetAudioTracks(); /// ]]> /// public IEnumerable GetAudioTracks() @@ -152,7 +152,7 @@ public IEnumerable GetAudioTracks() /// List of `MediaStreamTrack` objects. /// /// tracks = mediaStream.GetTracks(); /// ]]> /// public IEnumerable GetTracks() @@ -172,7 +172,11 @@ public IEnumerable GetTracks() /// `true` if the track successfully added to the stream. /// /// + /// { + /// bool result = receiveStream.AddTrack(e.Track); + /// } /// ]]> /// /// @@ -192,7 +196,7 @@ public bool AddTrack(MediaStreamTrack track) /// `true` if the track successfully removed from the stream. /// /// /// /// @@ -212,7 +216,7 @@ public bool RemoveTrack(MediaStreamTrack track) /// /// /// /// public MediaStream() : this(WebRTC.Context.CreateMediaStream(Guid.NewGuid().ToString())) From b9ce87e712f81fd7e5d3b85908c1716c5a971720 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 14:19:25 +0900 Subject: [PATCH 23/67] Improve examples --- Runtime/Scripts/MediaStreamTrack.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/MediaStreamTrack.cs b/Runtime/Scripts/MediaStreamTrack.cs index 3752dc661c..493208c2ea 100644 --- a/Runtime/Scripts/MediaStreamTrack.cs +++ b/Runtime/Scripts/MediaStreamTrack.cs @@ -13,7 +13,7 @@ namespace Unity.WebRTC /// /// /// mediaStreamTracks = mediaStream.GetTracks(); /// ]]> /// /// From 626520aea74b5116d7cdcb9c3a6e50fc7b751b1d Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 14:25:09 +0900 Subject: [PATCH 24/67] Improve examples --- Runtime/Scripts/WebRTC.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 6f20164335..a01df38151 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -538,7 +538,12 @@ public enum RTCIceTransportPolicy : int /// /// /// /// /// From 9f15add6315131702f9b6f07501c942b1264af44 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 14:43:07 +0900 Subject: [PATCH 25/67] Improve examples --- Runtime/Scripts/AudioStreamTrack.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/AudioStreamTrack.cs b/Runtime/Scripts/AudioStreamTrack.cs index a7130c2f29..8a561b2b3b 100644 --- a/Runtime/Scripts/AudioStreamTrack.cs +++ b/Runtime/Scripts/AudioStreamTrack.cs @@ -49,7 +49,7 @@ public static void SetTrack(this AudioSource source, AudioStreamTrack track) /// /// /// /// /// @@ -211,7 +211,7 @@ internal void SetData(float[] data, int channels, int sampleRate) /// /// /// /// public AudioStreamTrack() @@ -228,7 +228,7 @@ public AudioStreamTrack() /// `AudioSource` object. /// /// /// public AudioStreamTrack(AudioSource source) @@ -253,7 +253,7 @@ public AudioStreamTrack(AudioSource source) /// `AudioListener` object. /// /// /// public AudioStreamTrack(AudioListener listener) @@ -422,6 +422,11 @@ public void SetData(float[] array, int channels, int sampleRate) /// `ReadOnlySpan` containing audio data samples. /// Number of audio channels. /// Sample rate of the audio data + /// + /// + /// public void SetData(ReadOnlySpan span, int channels, int sampleRate) { unsafe From 63c9a482303511326b1cbe0ad81647d5ef71883f Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 14:50:51 +0900 Subject: [PATCH 26/67] Improve examples --- Runtime/Scripts/WebRTC.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index db2197044f..d6c6fef87b 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -628,7 +628,7 @@ public enum NativeLoggingSeverity /// /// /// /// /// @@ -933,7 +933,7 @@ public static void ValidateGraphicsFormat(GraphicsFormat format) /// `RenderTextureFormat` value for the specified `GraphicsDeviceType`. /// /// /// public static RenderTextureFormat GetSupportedRenderTextureFormat(GraphicsDeviceType type) @@ -952,7 +952,10 @@ public static RenderTextureFormat GetSupportedRenderTextureFormat(GraphicsDevice /// `GraphicsFormat` value for the specified `GraphicsDeviceType`. /// /// /// public static GraphicsFormat GetSupportedGraphicsFormat(GraphicsDeviceType type) @@ -1003,7 +1006,7 @@ public static GraphicsFormat GetSupportedGraphicsFormat(GraphicsDeviceType type) /// `TextureFormat` value for the specified `GraphicsDeviceType`. /// /// /// public static TextureFormat GetSupportedTextureFormat(GraphicsDeviceType type) From cb78380c3977a37568a540e470dd89c39a6b1603 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 14:56:48 +0900 Subject: [PATCH 27/67] Improve examples --- Runtime/Scripts/RTCRtpTransceiver.cs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Runtime/Scripts/RTCRtpTransceiver.cs b/Runtime/Scripts/RTCRtpTransceiver.cs index ca1e08c768..c42a6730e5 100644 --- a/Runtime/Scripts/RTCRtpTransceiver.cs +++ b/Runtime/Scripts/RTCRtpTransceiver.cs @@ -10,7 +10,13 @@ namespace Unity.WebRTC /// /// /// /// /// @@ -148,7 +154,13 @@ public RTCRtpSender Sender /// `RTCErrorType` value. /// /// /// /// @@ -175,7 +187,11 @@ public RTCErrorType SetCodecPreferences(RTCRtpCodecCapability[] codecs) /// `RTCErrorType` value. /// /// /// public RTCErrorType Stop() From 551f5c226519bf71f2e2289384a15f601604e8ad Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 15:07:20 +0900 Subject: [PATCH 28/67] Improve examples --- Runtime/Scripts/RTCRtpSender.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Runtime/Scripts/RTCRtpSender.cs b/Runtime/Scripts/RTCRtpSender.cs index 50b13619b9..4a47fc3ae4 100644 --- a/Runtime/Scripts/RTCRtpSender.cs +++ b/Runtime/Scripts/RTCRtpSender.cs @@ -72,9 +72,9 @@ public override void Dispose() /// `RTCRtpCapabilities` object contains an array of `RTCRtpCodecCapability` objects. /// /// `RTCStatsReportAsyncOperation` object containing `RTCStatsReport` object. /// /// /// str + next.Key + ":" + (next.Value == null ? string.Empty : next.Value.ToString()) + "\n"); @@ -202,10 +202,10 @@ public bool SyncApplicationFramerate /// `RTCRtpSendParameters` object containing the current configuration of the `RTCRtpSender`. /// /// `RTCError` value. /// /// `true` if the track has been successfully replaced. /// /// /// From ca452470d79045cad3cf3341ac324914c9dacd70 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 15:09:08 +0900 Subject: [PATCH 29/67] Adding Remarks. --- Runtime/Scripts/RTCDataChannel.cs | 60 ++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 1b50ab0560..e81888c188 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -72,48 +72,54 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or } /// - /// Specify the delegate type for OnOpen. + /// Represents type of delegate to be called when WebRTC open event is sent. /// /// - /// + /// The WebRTC open event is sent to an RTCDataChannel object's onopen event handler when the underlying transport used to send and receive the data channel's messages is opened or reopened. + /// This event is not cancelable and does not bubble. /// /// public delegate void DelegateOnOpen(); /// - /// Specify the delegate type for OnClose. + /// Represents type of delegate to be called when RTCDataChannel close event is sent. /// /// - /// + /// The close event is sent to the onclose event handler on an RTCDataChannel instance when the data transport for the data channel has closed. + /// Before any further data can be transferred using RTCDataChannel, a new 'RTCDataChannel' instance must be created. + /// This event is not cancelable and does not bubble. /// /// public delegate void DelegateOnClose(); /// - /// Specify the delegate type for OnMessage. + /// Represents type of delegate to be called when RTCDataChannel message event is sent. /// /// - /// + /// The WebRTC message event is sent to the onmessage event handler on an RTCDataChannel object when a message has been received from the remote peer. /// /// /// public delegate void DelegateOnMessage(byte[] bytes); /// - /// Specify the delegate type for RTCPeerConnection.OnDataChannel. + /// Represents type of delegate to be called when RTCPeerConnection datachannel event is sent. /// /// - /// + /// A datachannel event is sent to an RTCPeerConnection instance when an RTCDataChannel has been added to the connection, + /// as a result of the remote peer calling RTCPeerConnection.createDataChannel(). /// /// - /// + /// public delegate void DelegateOnDataChannel(RTCDataChannel channel); /// - /// Specify the delegate type for OnDataChannel. + /// Delegate to be called when RTCPeerConnection error event is sent. /// /// - /// + /// A WebRTC error event is sent to an RTCDataChannel object's onerror event handler when an error occurs on the data channel. + /// The RTCErrorEvent object provides details about the error that occurred; see that article for details. + /// This event is not cancelable and does not bubble. /// /// public delegate void DelegateOnError(RTCError error); @@ -159,7 +165,7 @@ public class RTCDataChannel : RefCountedObject /// Delegate to be called when a message has been received from the remote peer. /// /// - /// + /// The WebRTC message event is sent to the onmessage event handler on an RTCDataChannel object when a message has been received from the remote peer. /// public DelegateOnMessage OnMessage { @@ -171,7 +177,8 @@ public DelegateOnMessage OnMessage /// Delegate to be called when the data channel's messages is opened or reopened. /// /// - /// + /// The WebRTC open event is sent to an RTCDataChannel object's onopen event handler when the underlying transport used to send and receive the data channel's messages is opened or reopened. + /// This event is not cancelable and does not bubble. /// public DelegateOnOpen OnOpen { @@ -183,7 +190,9 @@ public DelegateOnOpen OnOpen /// Delegate to be called when the data channel's messages is closed. /// /// - /// + /// The close event is sent to the onclose event handler on an RTCDataChannel instance when the data transport for the data channel has closed. + /// Before any further data can be transferred using RTCDataChannel, a new 'RTCDataChannel' instance must be created. + /// This event is not cancelable and does not bubble. /// public DelegateOnClose OnClose { @@ -195,7 +204,9 @@ public DelegateOnClose OnClose /// Delegate to be called when the errors occur. /// /// - /// + /// A WebRTC error event is sent to an RTCDataChannel object's onerror event handler when an error occurs on the data channel. + /// The RTCErrorEvent object provides details about the error that occurred; see that article for details. + /// This event is not cancelable and does not bubble. /// public DelegateOnError OnError { @@ -207,7 +218,8 @@ public DelegateOnError OnError /// Returns an ID number (between 0 and 65,534) which uniquely identifies the RTCDataChannel. /// /// - /// + /// Returns an ID number (between 0 and 65,534) which uniquely identifies the RTCDataChannel. This ID is set at the time the data channel is created, either by the user agent (if RTCDataChannel.negotiated is false) or by the site or app script (if negotiated is true). + /// Each RTCPeerConnection can therefore have up to a theoretical maximum of 65,534 data channels on it. /// public int Id => NativeMethods.DataChannelGetID(GetSelfOrThrow()); @@ -220,10 +232,12 @@ public DelegateOnError OnError public string Label => NativeMethods.DataChannelGetLabel(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// - /// Returns a string containing the name of the sub protocol in use. If no protocol was specified when the data channel was created, then this property's value is the empty string (""). + /// Returns a string containing a name describing the data channel. These labels are not required to be unique. /// /// - /// + /// Returns a string containing a name describing the data channel. These labels are not required to be unique. + /// You may use the label as you wish; you could use it to identify all the channels that are being used for the same purpose, by giving them all the same name. + /// Or you could give each channel a unique label for tracking purposes. It's entirely up to the design decisions made when building your site or app. /// public string Protocol => NativeMethods.DataChannelGetProtocol(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); @@ -231,7 +245,9 @@ public DelegateOnError OnError /// Returns the maximum number of times the browser should try to retransmit a message before giving up. /// /// - /// + /// Returns the maximum number of times the browser should try to retransmit a message before giving up, + /// as set when the data channel was created, or null, which indicates that there is no maximum. + /// This can only be set when the RTCDataChannel is created by calling RTCPeerConnection.createDataChannel(), using the maxRetransmits field in the specified options. /// public ushort MaxRetransmits => NativeMethods.DataChannelGetMaxRetransmits(GetSelfOrThrow()); @@ -239,7 +255,8 @@ public DelegateOnError OnError /// Returns the amount of time, in milliseconds, the browser is allowed to take to attempt to transmit a message, as set when the data channel was created, or null. /// /// - /// + /// Returns the amount of time, in milliseconds, the browser is allowed to take to attempt to transmit a message, as set when the data channel was created, or null. + /// This limits how long the browser can continue to attempt to transmit and retransmit the message before giving up. /// public ushort MaxRetransmitTime => NativeMethods.DataChannelGetMaxRetransmitTime(GetSelfOrThrow()); @@ -247,7 +264,8 @@ public DelegateOnError OnError /// Indicates whether or not the data channel guarantees in-order delivery of messages. /// /// - /// + /// indicates whether or not the data channel guarantees in-order delivery of messages; the default is true, which indicates that the data channel is indeed ordered. + /// This is set when the RTCDataChannel is created, by setting the ordered property on the object passed as RTCPeerConnection.createDataChannel()'s options parameter. /// public bool Ordered => NativeMethods.DataChannelGetOrdered(GetSelfOrThrow()); From 76ebbf010358bf01071c4f61944a4b1280448a08 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 15:27:15 +0900 Subject: [PATCH 30/67] Added Remarks. --- Runtime/Scripts/RTCDataChannel.cs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index e81888c188..86eb098b39 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -218,7 +218,7 @@ public DelegateOnError OnError /// Returns an ID number (between 0 and 65,534) which uniquely identifies the RTCDataChannel. /// /// - /// Returns an ID number (between 0 and 65,534) which uniquely identifies the RTCDataChannel. This ID is set at the time the data channel is created, either by the user agent (if RTCDataChannel.negotiated is false) or by the site or app script (if negotiated is true). + /// This ID is set at the time the data channel is created, either by the user agent (if RTCDataChannel.negotiated is false) or by the site or app script (if negotiated is true). /// Each RTCPeerConnection can therefore have up to a theoretical maximum of 65,534 data channels on it. /// public int Id => NativeMethods.DataChannelGetID(GetSelfOrThrow()); @@ -235,7 +235,6 @@ public DelegateOnError OnError /// Returns a string containing a name describing the data channel. These labels are not required to be unique. /// /// - /// Returns a string containing a name describing the data channel. These labels are not required to be unique. /// You may use the label as you wish; you could use it to identify all the channels that are being used for the same purpose, by giving them all the same name. /// Or you could give each channel a unique label for tracking purposes. It's entirely up to the design decisions made when building your site or app. /// @@ -245,8 +244,7 @@ public DelegateOnError OnError /// Returns the maximum number of times the browser should try to retransmit a message before giving up. /// /// - /// Returns the maximum number of times the browser should try to retransmit a message before giving up, - /// as set when the data channel was created, or null, which indicates that there is no maximum. + /// As set when the data channel was created, or null, which indicates that there is no maximum. /// This can only be set when the RTCDataChannel is created by calling RTCPeerConnection.createDataChannel(), using the maxRetransmits field in the specified options. /// public ushort MaxRetransmits => NativeMethods.DataChannelGetMaxRetransmits(GetSelfOrThrow()); @@ -255,7 +253,6 @@ public DelegateOnError OnError /// Returns the amount of time, in milliseconds, the browser is allowed to take to attempt to transmit a message, as set when the data channel was created, or null. /// /// - /// Returns the amount of time, in milliseconds, the browser is allowed to take to attempt to transmit a message, as set when the data channel was created, or null. /// This limits how long the browser can continue to attempt to transmit and retransmit the message before giving up. /// public ushort MaxRetransmitTime => NativeMethods.DataChannelGetMaxRetransmitTime(GetSelfOrThrow()); @@ -264,7 +261,7 @@ public DelegateOnError OnError /// Indicates whether or not the data channel guarantees in-order delivery of messages. /// /// - /// indicates whether or not the data channel guarantees in-order delivery of messages; the default is true, which indicates that the data channel is indeed ordered. + /// The default is true, which indicates that the data channel is indeed ordered. /// This is set when the RTCDataChannel is created, by setting the ordered property on the object passed as RTCPeerConnection.createDataChannel()'s options parameter. /// public bool Ordered => NativeMethods.DataChannelGetOrdered(GetSelfOrThrow()); @@ -273,7 +270,9 @@ public DelegateOnError OnError /// Returns the number of bytes of data currently queued to be sent over the data channel. /// /// - /// + /// The queue may build up as a result of calls to the send() method. + /// This only includes data buffered by the user agent itself; + /// it doesn't include any framing overhead or buffering done by the operating system or network hardware. /// public ulong BufferedAmount => NativeMethods.DataChannelGetBufferedAmount(GetSelfOrThrow()); @@ -281,7 +280,7 @@ public DelegateOnError OnError /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app or by the WebRTC layer. /// /// - /// + /// True is for Web App and the False is for WebRTC layer. The default is false. /// public bool Negotiated => NativeMethods.DataChannelGetNegotiated(GetSelfOrThrow()); @@ -366,7 +365,13 @@ internal RTCDataChannel(IntPtr ptr, RTCPeerConnection peerConnection) /// Release all the resources RTCDataChannel instance has allocated. /// /// + /// The Dispose method leaves the RTCDataChannel in an unusable state. + /// After calling Dispose, you must release all references to the RTCDataChannel + /// so the garbage collector can reclaim the memory that the RTCDataChannel was occupying. /// + /// Note: Always call Dispose before you release your last reference to the + /// RTCDataChannel. Otherwise, the resources it is using will not be freed + /// until the garbage collector calls the Finalize method of the object. /// public override void Dispose() { @@ -387,7 +392,6 @@ public override void Dispose() /// Sends data across the data channel to the remote peer. /// /// - /// Sends data across the data channel to the remote peer. /// This can be done any time except during the initial process of creating the underlying transport channel. /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), /// and is also buffered if sent while the connection is closing or closed. @@ -411,7 +415,6 @@ public void Send(string msg) /// Sends data across the data channel to the remote peer. /// /// - /// Sends data across the data channel to the remote peer. /// This can be done any time except during the initial process of creating the underlying transport channel. /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), /// and is also buffered if sent while the connection is closing or closed. @@ -435,7 +438,6 @@ public void Send(byte[] msg) /// Sends data across the data channel to the remote peer. /// /// - /// Sends data across the data channel to the remote peer. /// This can be done any time except during the initial process of creating the underlying transport channel. /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), /// and is also buffered if sent while the connection is closing or closed. @@ -464,7 +466,6 @@ public unsafe void Send(NativeArray msg) /// Sends data across the data channel to the remote peer. /// /// - /// Sends data across the data channel to the remote peer. /// This can be done any time except during the initial process of creating the underlying transport channel. /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), /// and is also buffered if sent while the connection is closing or closed. @@ -491,7 +492,6 @@ public unsafe void Send(NativeSlice msg) /// Sends data across the data channel to the remote peer. /// /// - /// Sends data across the data channel to the remote peer. /// This can be done any time except during the initial process of creating the underlying transport channel. /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), /// and is also buffered if sent while the connection is closing or closed. @@ -516,7 +516,6 @@ public unsafe void Send(NativeArray.ReadOnly msg) /// Sends data across the data channel to the remote peer. /// /// - /// Sends data across the data channel to the remote peer. /// This can be done any time except during the initial process of creating the underlying transport channel. /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), /// and is also buffered if sent while the connection is closing or closed. @@ -540,7 +539,6 @@ public unsafe void Send(void* msgPtr, int length) /// Sends data across the data channel to the remote peer. /// /// - /// Sends data across the data channel to the remote peer. /// This can be done any time except during the initial process of creating the underlying transport channel. /// Data sent before connecting is buffered if possible (or an error occurs if it's not possible), /// and is also buffered if sent while the connection is closing or closed. @@ -566,7 +564,6 @@ public void Send(IntPtr msgPtr, int length) /// Closes the RTCDataChannel. Either peer is permitted to call this method to initiate closure of the channel. /// /// - /// Closes the RTCDataChannel. Either peer is permitted to call this method to initiate closure of the channel. /// Closure of the data channel is not instantaneous. Most of the process of closing the connection is handled asynchronously; /// you can detect when the channel has finished closing by watching for a close event on the data channel. /// From 3f49aa303e146f38275f122b4396138cba4ffc7a Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 16:03:35 +0900 Subject: [PATCH 31/67] Added sample programs. --- Runtime/Scripts/RTCDataChannel.cs | 744 +++++++++++++++++++++++++++++- 1 file changed, 740 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 86eb098b39..4d42c75550 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -167,6 +167,26 @@ public class RTCDataChannel : RefCountedObject /// /// The WebRTC message event is sent to the onmessage event handler on an RTCDataChannel object when a message has been received from the remote peer. /// + /// + /// { + /// Debug.LogFormat("Received: {0}.", e.data); + /// }; + /// } + /// } + /// ]]> + /// public DelegateOnMessage OnMessage { get => onMessage; @@ -177,9 +197,26 @@ public DelegateOnMessage OnMessage /// Delegate to be called when the data channel's messages is opened or reopened. /// /// - /// The WebRTC open event is sent to an RTCDataChannel object's onopen event handler when the underlying transport used to send and receive the data channel's messages is opened or reopened. - /// This event is not cancelable and does not bubble. - /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// }; + /// } + /// } + /// ]]> + /// public DelegateOnOpen OnOpen { get => onOpen; @@ -194,6 +231,26 @@ public DelegateOnOpen OnOpen /// Before any further data can be transferred using RTCDataChannel, a new 'RTCDataChannel' instance must be created. /// This event is not cancelable and does not bubble. /// + /// + /// { + /// Debug.Log("DataChannel closed."); + /// }; + /// } + /// } + /// ]]> + /// public DelegateOnClose OnClose { get => onClose; @@ -208,6 +265,26 @@ public DelegateOnClose OnClose /// The RTCErrorEvent object provides details about the error that occurred; see that article for details. /// This event is not cancelable and does not bubble. /// + /// + /// { + /// Debug.LogError("DataChannel error: " + e.message); + /// }; + /// } + /// } + /// ]]> + /// public DelegateOnError OnError { get => onError; @@ -221,14 +298,51 @@ public DelegateOnError OnError /// This ID is set at the time the data channel is created, either by the user agent (if RTCDataChannel.negotiated is false) or by the site or app script (if negotiated is true). /// Each RTCPeerConnection can therefore have up to a theoretical maximum of 65,534 data channels on it. /// + /// + /// + /// public int Id => NativeMethods.DataChannelGetID(GetSelfOrThrow()); /// /// Returns a string containing a name describing the data channel which are not required to be unique. /// /// - /// + /// You may use the label as you wish; you could use it to identify all the channels that are being used for the same purpose, by giving them all the same name. + /// Or you could give each channel a unique label for tracking purposes. It's entirely up to the design decisions made when building your site or app. /// + /// + /// + /// public string Label => NativeMethods.DataChannelGetLabel(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// @@ -238,6 +352,24 @@ public DelegateOnError OnError /// You may use the label as you wish; you could use it to identify all the channels that are being used for the same purpose, by giving them all the same name. /// Or you could give each channel a unique label for tracking purposes. It's entirely up to the design decisions made when building your site or app. /// + /// + /// + /// public string Protocol => NativeMethods.DataChannelGetProtocol(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// @@ -247,6 +379,27 @@ public DelegateOnError OnError /// As set when the data channel was created, or null, which indicates that there is no maximum. /// This can only be set when the RTCDataChannel is created by calling RTCPeerConnection.createDataChannel(), using the maxRetransmits field in the specified options. /// + /// + /// + /// public ushort MaxRetransmits => NativeMethods.DataChannelGetMaxRetransmits(GetSelfOrThrow()); /// @@ -255,6 +408,29 @@ public DelegateOnError OnError /// /// This limits how long the browser can continue to attempt to transmit and retransmit the message before giving up. /// + /// + /// + /// public ushort MaxRetransmitTime => NativeMethods.DataChannelGetMaxRetransmitTime(GetSelfOrThrow()); /// @@ -264,6 +440,29 @@ public DelegateOnError OnError /// The default is true, which indicates that the data channel is indeed ordered. /// This is set when the RTCDataChannel is created, by setting the ordered property on the object passed as RTCPeerConnection.createDataChannel()'s options parameter. /// + /// + /// + /// public bool Ordered => NativeMethods.DataChannelGetOrdered(GetSelfOrThrow()); /// @@ -274,6 +473,42 @@ public DelegateOnError OnError /// This only includes data buffered by the user agent itself; /// it doesn't include any framing overhead or buffering done by the operating system or network hardware. /// + /// + /// + /// public ulong BufferedAmount => NativeMethods.DataChannelGetBufferedAmount(GetSelfOrThrow()); /// @@ -282,6 +517,28 @@ public DelegateOnError OnError /// /// True is for Web App and the False is for WebRTC layer. The default is false. /// + /// + /// + /// public bool Negotiated => NativeMethods.DataChannelGetNegotiated(GetSelfOrThrow()); /// @@ -292,6 +549,45 @@ public DelegateOnError OnError /// method must be called when the state is Open. /// /// + /// + /// + /// public RTCDataChannelState ReadyState => NativeMethods.DataChannelGetReadyState(GetSelfOrThrow()); [AOT.MonoPInvokeCallback(typeof(DelegateNativeOnMessage))] @@ -373,6 +669,47 @@ internal RTCDataChannel(IntPtr ptr, RTCPeerConnection peerConnection) /// RTCDataChannel. Otherwise, the resources it is using will not be freed /// until the garbage collector calls the Finalize method of the object. /// + /// + /// + /// public override void Dispose() { if (this.disposed) @@ -402,6 +739,57 @@ public override void Dispose() /// /// /// + /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// SendMessage("Hello, WebRTC!"); + /// }; + /// + /// dataChannel.OnMessage = (e) => + /// { + /// Debug.Log("Received message: " + e.data); + /// }; + /// } + /// + /// private void SendMessage(string message) + /// { + /// if (dataChannel.ReadyState == RTCDataChannelState.Open) + /// { + /// dataChannel.Send(message); + /// Debug.Log("Sent message: " + message); + /// } + /// else + /// { + /// Debug.LogWarning("DataChannel is not open. Cannot send message."); + /// } + /// } + /// + /// private void OnDestroy() + /// { + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// dataChannel.Dispose(); + /// } + /// } + /// } + /// ]]> + /// public void Send(string msg) { if (ReadyState != RTCDataChannelState.Open) @@ -425,6 +813,64 @@ public void Send(string msg) /// /// /// + /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// SendMessage(new byte[] { 0x01, 0x02, 0x03 }); + /// }; + /// + /// dataChannel.OnMessage = (e) => + /// { + /// if (e.binary) + /// { + /// Debug.Log("Received binary message of length: " + e.data.Length); + /// } + /// else + /// { + /// Debug.Log("Received message: " + e.data); + /// } + /// }; + /// } + /// + /// public void SendMessage(byte[] message) + /// { + /// if (dataChannel.ReadyState == RTCDataChannelState.Open) + /// { + /// dataChannel.Send(message); + /// Debug.Log("Sent binary message of length: " + message.Length); + /// } + /// else + /// { + /// Debug.LogWarning("DataChannel is not open. Cannot send message."); + /// } + /// } + /// + /// private void OnDestroy() + /// { + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// dataChannel.Dispose(); + /// } + /// } + /// } + /// ]]> + /// public void Send(byte[] msg) { if (ReadyState != RTCDataChannelState.Open) @@ -448,6 +894,67 @@ public void Send(byte[] msg) /// /// /// + /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// var nativeArray = new NativeArray(new byte[] { 0x01, 0x02, 0x03 }, Allocator.Temp); + /// Send(nativeArray); + /// nativeArray.Dispose(); + /// }; + /// + /// dataChannel.OnMessage = (e) => + /// { + /// if (e.binary) + /// { + /// Debug.Log("Received binary message of length: " + e.data.Length); + /// } + /// }; + /// } + /// + /// public unsafe void Send(NativeArray msg) where T : struct + /// { + /// if (dataChannel.ReadyState == RTCDataChannelState.Open) + /// { + /// void* ptr = msg.GetUnsafePtr(); + /// byte[] bytes = new byte[msg.Length * UnsafeUtility.SizeOf()]; + /// System.Runtime.InteropServices.Marshal.Copy(new IntPtr(ptr), bytes, 0, bytes.Length); + /// dataChannel.Send(bytes); + /// Debug.Log("Sent binary message of length: " + bytes.Length); + /// } + /// else + /// { + /// Debug.LogWarning("DataChannel is not open. Cannot send message."); + /// } + /// } + /// + /// private void OnDestroy() + /// { + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// dataChannel.Dispose(); + /// } + /// } + /// } + /// ]]> + /// public unsafe void Send(NativeArray msg) where T : struct { @@ -476,6 +983,70 @@ public unsafe void Send(NativeArray msg) /// /// /// + /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// var nativeArray = new NativeArray(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }, Allocator.Temp); + /// var nativeSlice = new NativeSlice(nativeArray, 1, 3); // Slice from index 1 to 3 + /// Send(nativeSlice); + /// nativeArray.Dispose(); + /// }; + /// + /// dataChannel.OnMessage = (e) => + /// { + /// if (e.binary) + /// { + /// Debug.Log("Received binary message of length: " + e.data.Length); + /// } + /// }; + /// } + /// + /// public unsafe void Send(NativeSlice msg) where T : struct + /// { + /// if (dataChannel.ReadyState == RTCDataChannelState.Open) + /// { + /// void* ptr = msg.GetUnsafeReadOnlyPtr(); + /// byte[] bytes = new byte[msg.Length * UnsafeUtility.SizeOf()]; + /// System.Runtime.InteropServices.Marshal.Copy(new IntPtr(ptr), bytes, 0, bytes.Length); + /// dataChannel.Send(bytes); + /// Debug.Log("Sent binary message of length: " + bytes.Length); + /// } + /// else + /// { + /// Debug.LogWarning("DataChannel is not open. Cannot send message."); + /// } + /// } + /// + /// private void OnDestroy() + /// { + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// dataChannel.Dispose(); + /// } + /// } + /// } + /// ]]> + /// + public unsafe void Send(NativeSlice msg) where T : struct { @@ -501,6 +1072,69 @@ public unsafe void Send(NativeSlice msg) /// is not Open. /// /// /// + /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// var nativeArray = new NativeArray(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }, Allocator.Temp); + /// var readOnlyArray = nativeArray.AsReadOnly(); + /// Send(readOnlyArray); + /// nativeArray.Dispose(); + /// }; + /// + /// dataChannel.OnMessage = (e) => + /// { + /// if (e.binary) + /// { + /// Debug.Log("Received binary message of length: " + e.data.Length); + /// } + /// }; + /// } + /// + /// public unsafe void Send(NativeArray.ReadOnly msg) where T : struct + /// { + /// if (dataChannel.ReadyState == RTCDataChannelState.Open) + /// { + /// void* ptr = msg.GetUnsafeReadOnlyPtr(); + /// byte[] bytes = new byte[msg.Length * UnsafeUtility.SizeOf()]; + /// System.Runtime.InteropServices.Marshal.Copy(new IntPtr(ptr), bytes, 0, bytes.Length); + /// dataChannel.Send(bytes); + /// Debug.Log("Sent binary message of length: " + bytes.Length); + /// } + /// else + /// { + /// Debug.LogWarning("DataChannel is not open. Cannot send message."); + /// } + /// } + /// + /// private void OnDestroy() + /// { + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// dataChannel.Dispose(); + /// } + /// } + /// } + /// ]]> + /// public unsafe void Send(NativeArray.ReadOnly msg) where T : struct { @@ -548,6 +1182,59 @@ public unsafe void Send(void* msgPtr, int length) /// is not Open. /// /// /// + /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// + /// byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }; + /// unsafe + /// { + /// fixed (byte* dataPtr = data) + /// { + /// Send(dataPtr, data.Length); + /// } + /// } + /// }; + /// + /// dataChannel.OnMessage = (e) => + /// { + /// if (e.binary) + /// { + /// Debug.Log("Received binary message of length: " + e.data.Length); + /// } + /// }; + /// } + /// + /// + /// private void OnDestroy() + /// { + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// dataChannel.Dispose(); + /// } + /// } + /// } + /// ]]> + /// public void Send(IntPtr msgPtr, int length) { if (ReadyState != RTCDataChannelState.Open) @@ -567,6 +1254,55 @@ public void Send(IntPtr msgPtr, int length) /// Closure of the data channel is not instantaneous. Most of the process of closing the connection is handled asynchronously; /// you can detect when the channel has finished closing by watching for a close event on the data channel. /// + /// + /// + /// { + /// Debug.Log("DataChannel opened."); + /// }; + /// + /// dataChannel.OnClose = () => + /// { + /// Debug.Log("DataChannel closed."); + /// }; + /// + /// // Assume some operation has been completed and we need to close the data channel + /// Invoke("CloseDataChannel", 5.0f); // Close the channel after 5 seconds + /// } + /// + /// private void CloseDataChannel() + /// { + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// Debug.Log("DataChannel has been closed manually."); + /// } + /// } + /// + /// private void OnDestroy() + /// { + /// // Clean up the data channel when the GameObject is destroyed + /// if (dataChannel != null) + /// { + /// dataChannel.Close(); + /// } + /// } + /// } + /// ]]> + /// public void Close() { NativeMethods.DataChannelClose(GetSelfOrThrow()); From 359647be9bfa2078bc305ee3bb4380a6f54ea5c3 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 16:07:20 +0900 Subject: [PATCH 32/67] Fixed typo "chennel". --- Runtime/Scripts/RTCDataChannel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 4d42c75550..5b2dab84fc 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -138,17 +138,17 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// { + /// dataChannel.OnMessage = (event) => { /// Debug.LogFormat("Received: {0}.",${event.data}); /// }; /// - /// dataChennel.OnOpen = () => { + /// dataChannel.OnOpen = () => { /// Debug.Log("DataChannel opened."); /// }; /// - /// dataChennel.OnClose = () => { + /// dataChannel.OnClose = () => { /// Debug.Log("DataChannel closed."); /// }; /// ]]> From 6c4b2b967099d2e07c89d631aaa94957172048be Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 16:16:35 +0900 Subject: [PATCH 33/67] Imroved description for ordered. --- Runtime/Scripts/RTCDataChannel.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 5b2dab84fc..da399dd294 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -12,7 +12,7 @@ namespace Unity.WebRTC public class RTCDataChannelInit { /// - /// Indicates whether or not the data channel guarantees in-order delivery of messages. + /// Determines whether the data channel ensures the delivery of messages in the order they were sent. /// public bool? ordered; /// @@ -434,12 +434,12 @@ public DelegateOnError OnError public ushort MaxRetransmitTime => NativeMethods.DataChannelGetMaxRetransmitTime(GetSelfOrThrow()); /// - /// Indicates whether or not the data channel guarantees in-order delivery of messages. + /// Determines whether the data channel ensures the delivery of messages in the order they were sent. /// /// - /// The default is true, which indicates that the data channel is indeed ordered. - /// This is set when the RTCDataChannel is created, by setting the ordered property on the object passed as RTCPeerConnection.createDataChannel()'s options parameter. - /// + /// This property controls whether the data channel delivers messages in the sequence they were dispatched. + /// If set to true, messages will arrive in the exact order sent, ensuring consistent data flow, which can be critical for applications where order is important. + /// If false, the data channel allows out-of-order delivery to potentially enhance transmission speed but is best suited for applications where strict order isn't a concern. /// /// /// Date: Fri, 6 Dec 2024 16:33:05 +0900 Subject: [PATCH 34/67] modifying summaries. --- Runtime/Scripts/RTCDataChannel.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index da399dd294..49ad55898f 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -15,30 +15,35 @@ public class RTCDataChannelInit /// Determines whether the data channel ensures the delivery of messages in the order they were sent. /// public bool? ordered; + /// - /// Represents the maximum number of milliseconds that attempts to transfer a message may take in unreliable mode.. + /// Specifies the maximum number of transmission retries for a message when operating under non-guaranteed delivery conditions. /// /// /// Cannot be set along with . /// /// public int? maxPacketLifeTime; + /// - /// Represents the maximum number of times the user agent should attempt to retransmit a message which fails the first time in unreliable mode. + /// Specifies the maximum number of times the data channel will attempt to resend a message if initial transmission fails under unreliable conditions. /// /// /// Cannot be set along with . /// /// public int? maxRetransmits; + /// /// Provides the name of the sub-protocol being used on the RTCDataChannel. /// public string protocol; + /// /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app or by the WebRTC layer. /// public bool? negotiated; + /// /// Indicates a 16-bit numeric ID for the channel. /// @@ -197,6 +202,7 @@ public DelegateOnMessage OnMessage /// Delegate to be called when the data channel's messages is opened or reopened. /// /// + /// /// /// Date: Fri, 6 Dec 2024 16:41:04 +0900 Subject: [PATCH 35/67] Improved protocol property. --- Runtime/Scripts/RTCDataChannel.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 49ad55898f..971b674c78 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -35,7 +35,7 @@ public class RTCDataChannelInit public int? maxRetransmits; /// - /// Provides the name of the sub-protocol being used on the RTCDataChannel. + /// Specifies the sub protocol being used by the data channel to transmit and process messages. /// public string protocol; @@ -352,11 +352,12 @@ public DelegateOnError OnError public string Label => NativeMethods.DataChannelGetLabel(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// - /// Returns a string containing a name describing the data channel. These labels are not required to be unique. + /// Returns the sub protocol being used by the data channel to transmit and process messages. /// /// - /// You may use the label as you wish; you could use it to identify all the channels that are being used for the same purpose, by giving them all the same name. - /// Or you could give each channel a unique label for tracking purposes. It's entirely up to the design decisions made when building your site or app. + /// The `Protocol` property retrieves the sub protocol negotiated for this data channel, which governs the rules for message format and communication behavior between peers. + /// This property is critical for ensuring compatibility and understanding between different systems or applications using the channel, especially when custom protocols are used. + /// If no protocol was specified during the data channel's creation, this property returns an empty string, indicating that no particular sub protocol is in effect. /// /// /// Date: Fri, 6 Dec 2024 16:43:54 +0900 Subject: [PATCH 36/67] Done for RTCDataChannelInit. --- Runtime/Scripts/RTCDataChannel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 971b674c78..6e48f34665 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -40,12 +40,12 @@ public class RTCDataChannelInit public string protocol; /// - /// Indicates whether the RTCDataChannel's connection is negotiated by the Web app or by the WebRTC layer. + /// Specifies whether the data channel's connection is manually negotiated by the application or automatically handled by WebRTC. /// public bool? negotiated; /// - /// Indicates a 16-bit numeric ID for the channel. + /// Specifies a unique 16-bit identifier for the data channel, allowing explicit channel setup during manual negotiation. /// public int? id; } From 94d38f0aa7ccf75f3cc450e09c558a85687eef6f Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Fri, 6 Dec 2024 16:46:55 +0900 Subject: [PATCH 37/67] Update Runtime/Scripts/WebRTC.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/WebRTC.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index d30e601d38..95671754fd 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -486,7 +486,7 @@ public enum RTCIceCredentialType /// Represents a configuration for an ICE server used within WebRTC connections. /// /// - /// `RTCIceServer` struct represents a configuration for an ICE server used within WebRTC connections, + /// Represents a configuration for an ICE server used within WebRTC connections, /// including authentication credentials and STUN/TURN server URLs. /// /// From d93306d923ac106cbc661d3011e157768ad46b4e Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 17:00:01 +0900 Subject: [PATCH 38/67] wip. --- Runtime/Scripts/RTCDataChannel.cs | 66 +++++++++++++++++-------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 6e48f34665..04f2de317c 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -77,68 +77,70 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or } /// - /// Represents type of delegate to be called when WebRTC open event is sent. + /// Represents the method that will be invoked when the data channel is successfully opened and ready for communication. /// /// - /// The WebRTC open event is sent to an RTCDataChannel object's onopen event handler when the underlying transport used to send and receive the data channel's messages is opened or reopened. - /// This event is not cancelable and does not bubble. + /// The `DelegateOnOpen` is triggered when the data channel's transport layer becomes successfully established and ready for data transfer. + /// This indicates that messages can now be sent and received over the channel, marking the transition from connecting to an operational state. + /// Useful for initializing or signaling to the application that the channel setup is complete and ready for communication. /// /// public delegate void DelegateOnOpen(); /// - /// Represents type of delegate to be called when RTCDataChannel close event is sent. + /// Represents the method that will be invoked when the data channel has been closed and is no longer available for communication. /// /// - /// The close event is sent to the onclose event handler on an RTCDataChannel instance when the data transport for the data channel has closed. - /// Before any further data can be transferred using RTCDataChannel, a new 'RTCDataChannel' instance must be created. - /// This event is not cancelable and does not bubble. + /// The `DelegateOnClose` is triggered when the data channel's underlying transport is terminated, signaling that no further messages can be sent or received. + /// Useful for cleaning up resources or notifying the application that the data channel is no longer in use. + /// This marks a transition to a non-operational state, and to resume communication, a new data channel must be established. /// /// public delegate void DelegateOnClose(); /// - /// Represents type of delegate to be called when RTCDataChannel message event is sent. + /// Represents the method that will be invoked when a message is received from the remote peer over the data channel. /// /// - /// The WebRTC message event is sent to the onmessage event handler on an RTCDataChannel object when a message has been received from the remote peer. + /// The `DelegateOnMessage` is executed when the data channel successfully receives a message from the remote peer, providing the message content as a parameter. + /// This allows the application to process the incoming data, whether it's for updating the UI, triggering gameplay logic, or handling any response actions. + /// The method receives the message as a byte array, making it flexible for both textual and binary data. /// /// /// public delegate void DelegateOnMessage(byte[] bytes); /// - /// Represents type of delegate to be called when RTCPeerConnection datachannel event is sent. + /// Represents the method that will be invoked when a new data channel is added to the RTCPeerConnection. /// /// - /// A datachannel event is sent to an RTCPeerConnection instance when an RTCDataChannel has been added to the connection, - /// as a result of the remote peer calling RTCPeerConnection.createDataChannel(). + /// The `DelegateOnDataChannel` is triggered when a new data channel is established, typically as a result of the remote peer creating a channel. + /// This provides an opportunity to configure the new channel, such as setting message handlers or adjusting properties. + /// Ensuring the application is prepared to handle the new data channel is crucial for seamless peer-to-peer communication. /// /// /// public delegate void DelegateOnDataChannel(RTCDataChannel channel); /// - /// Delegate to be called when RTCPeerConnection error event is sent. + /// Represents the method that will be invoked when an error occurs on the data channel. /// /// - /// A WebRTC error event is sent to an RTCDataChannel object's onerror event handler when an error occurs on the data channel. - /// The RTCErrorEvent object provides details about the error that occurred; see that article for details. - /// This event is not cancelable and does not bubble. + /// The `DelegateOnError` is executed whenever an error arises within the data channel, allowing applications to handle various error scenarios gracefully. + /// It provides detailed information about the error, enabling developers to implement corrective measures or issue notifications to users. + /// Handling such errors is crucial for maintaining robust and reliable peer-to-peer communication. /// /// public delegate void DelegateOnError(RTCError error); /// - /// Represents a network channel which can be used for bidirectional peer-to-peer transfers of arbitrary data. + /// Creates a new RTCDataChannel for peer-to-peer data exchange, using the specified label and options. /// /// - /// RTCDataChannel interface represents a network channel which can be used for bidirectional peer-to-peer transfers of arbitrary data. - /// Every data channel is associated with an RTCPeerConnection, and each peer connection can have up to a theoretical maximum of 65,534 data channels. - /// - /// To create a data channel and ask a remote peer to join you, call the RTCPeerConnection's createDataChannel() method. - /// The peer being invited to exchange data receives a datachannel event (which has type RTCDataChannelEvent) to let it know the data channel has been added to the connection. - /// + /// The `CreateDataChannel` method establishes a bidirectional communication channel between peers, identified by a unique label. + /// This channel allows for the transmission of arbitrary data, such as text or binary, directly between connected peers without the need for a traditional server. + /// The optional parameters provide flexibility in controlling the behavior of the data channel, including options for reliability and ordering of messages. + /// It's essential for applications to configure these channels according to their specific communication needs. /// /// /// /// - /// The WebRTC message event is sent to the onmessage event handler on an RTCDataChannel object when a message has been received from the remote peer. + /// The `OnMessage` delegate is invoked whenever a message is received over the data channel from the remote peer. + /// This provides the application an opportunity to process the received data, which could include tasks such as updating the user interface, storing information, or triggering specific logic. + /// The message is delivered as a byte array, offering flexibility to handle both text and binary data formats. /// /// /// /// + /// The `OnOpen` delegate is triggered when the data channel successfully establishes its underlying transport mechanism. + /// This state transition indicates that the channel is ready for data transmission, providing an opportunity for the application to initialize any required states or notify the user that the channel is ready to use. + /// It is a critical event for setting up initial data exchanges between peers. /// /// /// /// - /// A WebRTC error event is sent to an RTCDataChannel object's onerror event handler when an error occurs on the data channel. - /// The RTCErrorEvent object provides details about the error that occurred; see that article for details. - /// This event is not cancelable and does not bubble. + /// The `OnClose` delegate is triggered when the data channel's transport layer is terminated, signifying the channel's transition to a closed state. + /// This event serves as a cue for the application to release resources, update the user interface, or handle any clean-up operations necessary to gracefully end the communication session. + /// Understanding this transition is vital for managing the lifecycle of data exchanges between peers. /// /// /// /// - /// This ID is set at the time the data channel is created, either by the user agent (if RTCDataChannel.negotiated is false) or by the site or app script (if negotiated is true). - /// Each RTCPeerConnection can therefore have up to a theoretical maximum of 65,534 data channels on it. + /// The `OnError` delegate is triggered whenever there is an error in the data channel, offering the application a chance to handle the error gracefully. + /// This includes logging the error, alerting the user, or attempting to rectify the issue depending on the nature and severity of the error encountered. + /// Proper error handling is crucial for maintaining robust communication and ensuring a seamless user experience. /// /// /// Date: Fri, 6 Dec 2024 16:04:01 +0900 Subject: [PATCH 39/67] Improve documentation of RTCRtpEncodingParameters --- Runtime/Scripts/RTPParameters.cs | 65 ++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/Runtime/Scripts/RTPParameters.cs b/Runtime/Scripts/RTPParameters.cs index 0c9517f8bb..f3a1ef808b 100644 --- a/Runtime/Scripts/RTPParameters.cs +++ b/Runtime/Scripts/RTPParameters.cs @@ -5,43 +5,92 @@ namespace Unity.WebRTC { /// - /// + /// Represents the parameters for a codec used to encode the track's media. /// + /// + /// Represents the configuration for encoding a media track's codec, detailing properties like + /// active state, bitrate, framerate, RTP stream ID, and video scaling, enabling precise control of media transmission. + /// + /// + /// + /// public class RTCRtpEncodingParameters { /// - /// + /// Indicates whether the encoding is sent. /// + /// + /// By default, the value is `true`. Setting it to `false` stops the transmission but does not cause the SSRC to be removed. + /// public bool active; /// - /// + /// Specifies the maximum number of bits per second allocated for encoding tracks. /// + /// + /// `maxBitrate` defines the maximum encoding bitrate using TIAS bandwidth, not including protocol overhead. + /// Factors like `maxFramerate` and network capacity can further limit this rate. + /// Video tracks may drop frames, and audio tracks might pause if bitrates are too low, balancing quality and efficiency. + /// public ulong? maxBitrate; /// - /// + /// Specifies the minimum number of bits per second allocated for encoding tracks. /// public ulong? minBitrate; /// - /// + /// Specifies the maximum number of frames per second allocated for encoding tracks. /// public uint? maxFramerate; /// - /// + /// Specifies a scaling factor for reducing the resolution of video tracks during encoding. /// + /// + /// `scaleResolutionDownBy` property scales down video resolutions for encoding. + /// With the default value 1.0 preserves the original size. + /// Values above 1.0 reduce dimensions, helping optimize bandwidth and processing needs. + /// Values below 1.0 are invalid. + /// public double? scaleResolutionDownBy; /// - /// + /// Specifies an RTP stream ID (RID) for the RID header extension. /// + /// + /// The value can be set only during transceiver creation and not modifiable with `RTCRtpSender.SetParameters`. + /// public string rid; /// - /// + /// Creates a new RTCRtpEncodingParameters object. /// + /// + /// A constructor creates an instance of `RTCRtpEncodingParameters`. + /// + /// + /// + /// public RTCRtpEncodingParameters() { } internal RTCRtpEncodingParameters(ref RTCRtpEncodingParametersInternal parameter) From e4a1f0ba7bc95aa04df20cba73530c50b60fffe1 Mon Sep 17 00:00:00 2001 From: h3idi-X Date: Fri, 6 Dec 2024 18:08:25 +0900 Subject: [PATCH 40/67] Filled the all. --- Runtime/Scripts/RTCDataChannel.cs | 45 ++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 04f2de317c..01dd8eb1b8 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -145,7 +145,7 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// { /// Debug.LogFormat("Received: {0}.",${event.data}); @@ -308,9 +308,10 @@ public DelegateOnError OnError /// Returns an ID number (between 0 and 65,534) which uniquely identifies the RTCDataChannel. /// /// - /// The `OnError` delegate is triggered whenever there is an error in the data channel, offering the application a chance to handle the error gracefully. - /// This includes logging the error, alerting the user, or attempting to rectify the issue depending on the nature and severity of the error encountered. - /// Proper error handling is crucial for maintaining robust communication and ensuring a seamless user experience. + /// The `Id` property provides a unique identifier for the data channel, typically assigned during the channel's creation. + /// This identifier is used internally to differentiate between multiple data channels associated with a single RTCPeerConnection. + /// Understanding and referencing these IDs can be crucial when managing complex peer-to-peer communication setups where multiple channels are active. + /// The ID is automatically generated unless explicitly set during manual channel negotiation. /// /// /// /// - /// You may use the label as you wish; you could use it to identify all the channels that are being used for the same purpose, by giving them all the same name. - /// Or you could give each channel a unique label for tracking purposes. It's entirely up to the design decisions made when building your site or app. + /// The `Label` property specifies a name for the data channel, which is set when the channel is created. + /// This label is useful for identifying the purpose of the data channel, such as distinguishing between channels dedicated to different types of data or tasks. + /// While labels are not required to be unique, they provide meaningful context within an application, aiding in organization and management of multiple channels. + /// Developers can utilize labels to group channels by function or to describe their role in the communication process. /// /// /// /// - /// As set when the data channel was created, or null, which indicates that there is no maximum. - /// This can only be set when the RTCDataChannel is created by calling RTCPeerConnection.createDataChannel(), using the maxRetransmits field in the specified options. - /// + /// The `MaxRetransmits` property defines the upper limit on the number of times a message will be retransmitted if initial delivery fails. + /// This setting is particularly valuable in conditions where reliable delivery is necessary, but the application is sensitive to potential delays caused by continuous retransmission attempts. + /// By specifying a limit, developers can balance the need for message reliability with the potential impact on performance and latency. + /// If no retransmit limit is set, the data channel may continue to attempt message delivery until it succeeds, which might not be suitable for all applications. /// /// /// /// - /// This limits how long the browser can continue to attempt to transmit and retransmit the message before giving up. - /// + /// The `MaxRetransmitTime` property sets the maximum duration, in milliseconds, that the data channel will attempt to retransmit a message in unreliable mode. + /// This constraint ensures that if a message cannot be delivered within the specified time frame, the channel will cease retransmission attempts. + /// It is particularly useful for applications where timing is critical, allowing developers to limit delays potentially caused by prolonged retransmission efforts. + /// By defining this timeout, applications can maintain performance efficiency while handling network fluctuations. + /// If not set, the retransmission will continue based on other reliability settings, possibly yielding variable delays. /// /// /// /// - /// This property controls whether the data channel delivers messages in the sequence they were dispatched. + /// The `Ordered` property controls whether the data channel delivers messages in the sequence they were dispatched. /// If set to true, messages will arrive in the exact order sent, ensuring consistent data flow, which can be critical for applications where order is important. /// If false, the data channel allows out-of-order delivery to potentially enhance transmission speed but is best suited for applications where strict order isn't a concern. /// /// @@ -484,9 +491,11 @@ public DelegateOnError OnError /// Returns the number of bytes of data currently queued to be sent over the data channel. /// /// - /// The queue may build up as a result of calls to the send() method. - /// This only includes data buffered by the user agent itself; - /// it doesn't include any framing overhead or buffering done by the operating system or network hardware. + /// The `BufferedAmount` property indicates the number of bytes of data currently queued to be sent over the data channel. + /// This value represents the amount of data buffered on the sender side that has not yet been transmitted to the network. + /// Monitoring this property helps developers understand and manage flow control, allowing for adjustments to data transmission rates to avoid congestion. + /// In scenarios where this value grows unexpectedly, it could indicate network congestion or slow peer processing, prompting the need to throttle data sending. + /// Proper use of this property ensures that applications can maintain efficient data flow while mitigating potential bottlenecks. /// /// /// /// - /// True is for Web App and the False is for WebRTC layer. The default is false. + /// The `Negotiated` property indicates whether the data channel's connection parameters were explicitly negotiated by the application or automatically handled by the WebRTC implementation. + /// When set to `true`, it allows developers to manually manage the channel setup including selecting the channel ID, offering greater control over communication specifics. + /// This is especially useful in advanced scenarios where integration with complex signaling servers or custom negotiation processes are needed. + /// If `false`, the WebRTC stack automatically negotiates the channel's configuration, simplifying the setup but providing less granular control. + /// Proper switching between these modes ensures the application meets its communication requirements effectively. /// /// /// Date: Fri, 6 Dec 2024 18:30:36 +0900 Subject: [PATCH 41/67] Added more samples. --- Runtime/Scripts/RTCDataChannel.cs | 131 +++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 01dd8eb1b8..27cd0b785e 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -83,8 +83,31 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// The `DelegateOnOpen` is triggered when the data channel's transport layer becomes successfully established and ready for data transfer. /// This indicates that messages can now be sent and received over the channel, marking the transition from connecting to an operational state. /// Useful for initializing or signaling to the application that the channel setup is complete and ready for communication. + /// This delegate is typically assigned to the property. /// /// + /// + /// + /// { + /// Debug.Log("DataChannel is now open and ready for communication."); + /// }; + /// } + /// } + /// ]]> + /// public delegate void DelegateOnOpen(); /// @@ -94,8 +117,31 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// The `DelegateOnClose` is triggered when the data channel's underlying transport is terminated, signaling that no further messages can be sent or received. /// Useful for cleaning up resources or notifying the application that the data channel is no longer in use. /// This marks a transition to a non-operational state, and to resume communication, a new data channel must be established. + /// This delegate is typically assigned to the property. /// /// + /// + /// + /// { + /// Debug.Log("DataChannel has been closed."); + /// }; + /// } + /// } + /// ]]> + /// public delegate void DelegateOnClose(); /// @@ -105,9 +151,33 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// The `DelegateOnMessage` is executed when the data channel successfully receives a message from the remote peer, providing the message content as a parameter. /// This allows the application to process the incoming data, whether it's for updating the UI, triggering gameplay logic, or handling any response actions. /// The method receives the message as a byte array, making it flexible for both textual and binary data. + /// This delegate is typically assigned to the property. /// - /// + /// The message received as a byte array. /// + /// + /// + /// { + /// string message = System.Text.Encoding.UTF8.GetString(bytes); + /// Debug.Log("Received message: " + message); + /// }; + /// } + /// } + /// ]]> + /// public delegate void DelegateOnMessage(byte[] bytes); /// @@ -117,9 +187,35 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// The `DelegateOnDataChannel` is triggered when a new data channel is established, typically as a result of the remote peer creating a channel. /// This provides an opportunity to configure the new channel, such as setting message handlers or adjusting properties. /// Ensuring the application is prepared to handle the new data channel is crucial for seamless peer-to-peer communication. + /// This delegate is typically assigned to the property. /// - /// + /// The RTCDataChannel that has been added to the connection. /// + /// + /// + /// { + /// Debug.Log("A new data channel has been added: " + channel.Label); + /// channel.OnMessage = (bytes) => + /// { + /// string message = System.Text.Encoding.UTF8.GetString(bytes); + /// Debug.Log("Received message on new channel: " + message); + /// }; + /// }; + /// } + /// } + /// ]]> + /// public delegate void DelegateOnDataChannel(RTCDataChannel channel); /// @@ -129,8 +225,34 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// The `DelegateOnError` is executed whenever an error arises within the data channel, allowing applications to handle various error scenarios gracefully. /// It provides detailed information about the error, enabling developers to implement corrective measures or issue notifications to users. /// Handling such errors is crucial for maintaining robust and reliable peer-to-peer communication. + /// This delegate is typically assigned to the property. /// + /// The RTCError object that contains details about the error. /// + /// + /// + /// { + /// Debug.LogError("DataChannel error occurred: " + error.message); + /// // Additional error handling logic can be implemented here + /// }; + /// } + /// } + /// ]]> + /// + public delegate void DelegateOnError(RTCError error); /// @@ -140,7 +262,8 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// The `CreateDataChannel` method establishes a bidirectional communication channel between peers, identified by a unique label. /// This channel allows for the transmission of arbitrary data, such as text or binary, directly between connected peers without the need for a traditional server. /// The optional parameters provide flexibility in controlling the behavior of the data channel, including options for reliability and ordering of messages. - /// It's essential for applications to configure these channels according to their specific communication needs. /// + /// It's essential for applications to configure these channels according to their specific communication needs. + /// /// /// Date: Fri, 6 Dec 2024 20:34:52 +0900 Subject: [PATCH 42/67] Improved exception. --- Runtime/Scripts/RTCDataChannel.cs | 43 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 27cd0b785e..ea7bd6e133 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -888,7 +888,7 @@ public override void Dispose() /// The method throws InvalidOperationException when /// is not Open. /// - /// + /// The string message to be sent to the remote peer. /// /// /// InvalidOperationException when /// is not Open. /// - /// + /// The byte array to be sent to the remote peer. /// /// /// InvalidOperationException when /// is not Open. /// - /// - /// + /// The type of elements stored in the NativeArray, which must be a value type. + /// The NativeArray containing the data to be sent to the remote peer. + /// /// /// (NativeArray msg) /// and is also buffered if sent while the connection is closing or closed. /// /// - /// The method throws InvalidOperationException when - /// is not Open. + /// Thrown when the is not Open. /// - /// - /// + /// The type of elements stored in the NativeSlice, which must be a value type. + /// The NativeSlice containing the data to be sent to the remote peer. + /// /// /// (NativeSlice msg) /// and is also buffered if sent while the connection is closing or closed. /// /// - /// The method throws InvalidOperationException when - /// is not Open. - /// /// - /// + /// Thrown when the is not Open. + /// + /// The type of elements stored in the read-only NativeArray, which must be a value type. + /// The read-only NativeArray containing the data to be sent to the remote peer. + /// /// /// (NativeArray.ReadOnly msg) /// and is also buffered if sent while the connection is closing or closed. /// /// - /// The method throws InvalidOperationException when - /// is not Open. + /// Thrown when the is not Open. /// - /// - /// + /// A pointer to the memory location containing the data to be sent. + /// The length of the data, in bytes, to be sent from the specified memory location. + /// public unsafe void Send(void* msgPtr, int length) { if (ReadyState != RTCDataChannelState.Open) @@ -1329,10 +1331,11 @@ public unsafe void Send(void* msgPtr, int length) /// and is also buffered if sent while the connection is closing or closed. /// /// - /// The method throws InvalidOperationException when - /// is not Open. - /// /// - /// + /// Thrown when the is not Open. + /// + /// A pointer to the memory location containing the data to be sent. + /// The length of the data, in bytes, to be sent from the specified memory location. + /// /// /// Date: Fri, 6 Dec 2024 21:44:20 +0900 Subject: [PATCH 43/67] fixing source code formatting errors. --- Runtime/Scripts/RTCDataChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index ea7bd6e133..526df5d10d 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -287,7 +287,7 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or public class RTCDataChannel : RefCountedObject { private DelegateOnMessage onMessage; - private DelegateOnOpen onOpen; + private DelegateOnOpen onOpen; private DelegateOnClose onClose; private DelegateOnError onError; From 7be2aa7c90095f518a71790a41ddadc020c301f5 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:45:07 +0900 Subject: [PATCH 44/67] refactor: move CameraExtension class to its own file (#1064) --- Runtime/Scripts/CameraExtension.cs | 67 ++++++++++++++++++++++++ Runtime/Scripts/CameraExtension.cs.meta | 3 ++ Runtime/Scripts/VideoStreamTrack.cs | 69 ++----------------------- 3 files changed, 74 insertions(+), 65 deletions(-) create mode 100644 Runtime/Scripts/CameraExtension.cs create mode 100644 Runtime/Scripts/CameraExtension.cs.meta diff --git a/Runtime/Scripts/CameraExtension.cs b/Runtime/Scripts/CameraExtension.cs new file mode 100644 index 0000000000..b4afafd330 --- /dev/null +++ b/Runtime/Scripts/CameraExtension.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Concurrent; +using System.ComponentModel; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Experimental.Rendering; + +namespace Unity.WebRTC +{ + public static class CameraExtension + { + /// + /// Create an instance of to stream a camera. + /// + /// + /// You should keep a reference of , created by this method. + /// This instance is collected by GC automatically if you don't own a reference. + /// + /// + /// + /// + /// + /// + public static VideoStreamTrack CaptureStreamTrack(this Camera cam, int width, int height, + RenderTextureDepth depth = RenderTextureDepth.Depth24, CopyTexture textureCopy = null) + { + switch (depth) + { + case RenderTextureDepth.Depth16: + case RenderTextureDepth.Depth24: + case RenderTextureDepth.Depth32: + break; + default: + throw new InvalidEnumArgumentException(nameof(depth), (int)depth, typeof(RenderTextureDepth)); + } + + if (width <= 0 || height <= 0) + { + throw new ArgumentException("width and height are should be greater than zero."); + } + + int depthValue = (int)depth; + var format = WebRTC.GetSupportedRenderTextureFormat(SystemInfo.graphicsDeviceType); + var rt = new UnityEngine.RenderTexture(width, height, depthValue, format); + rt.Create(); + cam.targetTexture = rt; + return new VideoStreamTrack(rt, textureCopy); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static MediaStream CaptureStream(this Camera cam, int width, int height, + RenderTextureDepth depth = RenderTextureDepth.Depth24) + { + var stream = new MediaStream(); + var track = cam.CaptureStreamTrack(width, height, depth); + stream.AddTrack(track); + return stream; + } + } +} diff --git a/Runtime/Scripts/CameraExtension.cs.meta b/Runtime/Scripts/CameraExtension.cs.meta new file mode 100644 index 0000000000..b84a4d0def --- /dev/null +++ b/Runtime/Scripts/CameraExtension.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 29348c895bc5d8f459da06bc3c17a6b4 +timeCreated: 1599008246 \ No newline at end of file diff --git a/Runtime/Scripts/VideoStreamTrack.cs b/Runtime/Scripts/VideoStreamTrack.cs index 1e6702f6e9..b2965a9384 100644 --- a/Runtime/Scripts/VideoStreamTrack.cs +++ b/Runtime/Scripts/VideoStreamTrack.cs @@ -14,7 +14,7 @@ namespace Unity.WebRTC public delegate void OnVideoReceived(Texture renderer); /// - /// + /// /// /// /// @@ -270,67 +270,6 @@ static IntPtr CreateVideoTrack(IntPtr ptr) } } - /// - /// - /// - public static class CameraExtension - { - /// - /// Create an instance of to stream a camera. - /// - /// - /// You should keep a reference of , created by this method. - /// This instance is collected by GC automatically if you don't own a reference. - /// - /// - /// - /// - /// - /// - public static VideoStreamTrack CaptureStreamTrack(this Camera cam, int width, int height, - RenderTextureDepth depth = RenderTextureDepth.Depth24, CopyTexture textureCopy = null) - { - switch (depth) - { - case RenderTextureDepth.Depth16: - case RenderTextureDepth.Depth24: - case RenderTextureDepth.Depth32: - break; - default: - throw new InvalidEnumArgumentException(nameof(depth), (int)depth, typeof(RenderTextureDepth)); - } - - if (width <= 0 || height <= 0) - { - throw new ArgumentException("width and height are should be greater than zero."); - } - - int depthValue = (int)depth; - var format = WebRTC.GetSupportedRenderTextureFormat(SystemInfo.graphicsDeviceType); - var rt = new UnityEngine.RenderTexture(width, height, depthValue, format); - rt.Create(); - cam.targetTexture = rt; - return new VideoStreamTrack(rt, textureCopy); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static MediaStream CaptureStream(this Camera cam, int width, int height, - RenderTextureDepth depth = RenderTextureDepth.Depth24) - { - var stream = new MediaStream(); - var track = cam.CaptureStreamTrack(width, height, depth); - stream.AddTrack(track); - return stream; - } - } - internal class VideoTrackSource : RefCountedObject { internal Texture sourceTexture_; @@ -484,13 +423,13 @@ static void OnVideoFrameResize(IntPtr ptrRenderer, int width, int height) public static class CopyTextureHelper { - // Blit parameter to flip vertically + // Blit parameter to flip vertically private static readonly Vector2 s_verticalScale = new Vector2(1f, -1f); private static readonly Vector2 s_verticalOffset = new Vector2(0f, 1f); - // Blit parameter to flip horizontally + // Blit parameter to flip horizontally private static readonly Vector2 s_horizontalScale = new Vector2(-1f, 1f); private static readonly Vector2 s_horixontalOffset = new Vector2(1f, 0f); - // Blit parameter to flip diagonally + // Blit parameter to flip diagonally private static readonly Vector2 s_diagonalScale = new Vector2(-1f, -1f); private static readonly Vector2 s_diagonalOffset = new Vector2(1f, 1f); From ccfb6cddae56ecfe3d60ccd94f0569f6af681f7f Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Sat, 7 Dec 2024 08:11:43 +0900 Subject: [PATCH 45/67] Update Runtime/Scripts/MediaStreamTrack.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/MediaStreamTrack.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/MediaStreamTrack.cs b/Runtime/Scripts/MediaStreamTrack.cs index 493208c2ea..3bf49047a5 100644 --- a/Runtime/Scripts/MediaStreamTrack.cs +++ b/Runtime/Scripts/MediaStreamTrack.cs @@ -100,7 +100,7 @@ public override void Dispose() /// Stops the track. /// /// - /// `Stop` method disassociates track from its source (video or audio), not for destroying the track. + /// `Stop` method disassociates the track from its source (video or audio) without destroying the track. /// /// /// Date: Sat, 7 Dec 2024 08:40:44 +0900 Subject: [PATCH 46/67] Update Runtime/Scripts/AudioStreamTrack.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/AudioStreamTrack.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/AudioStreamTrack.cs b/Runtime/Scripts/AudioStreamTrack.cs index 8a561b2b3b..4726f5519d 100644 --- a/Runtime/Scripts/AudioStreamTrack.cs +++ b/Runtime/Scripts/AudioStreamTrack.cs @@ -7,7 +7,7 @@ namespace Unity.WebRTC { /// - /// Delegate to be called when received new audio data. + /// Delegate to be called when new audio data is received. /// /// /// `AudioReadEventHandler` is a delegate to be called when received new audio data. From e8019ed44fc4dfd2aa4669c3aa4df8a0afd4f9e2 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Sat, 7 Dec 2024 08:40:57 +0900 Subject: [PATCH 47/67] Update Runtime/Scripts/AudioStreamTrack.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/AudioStreamTrack.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/AudioStreamTrack.cs b/Runtime/Scripts/AudioStreamTrack.cs index 4726f5519d..a72a8cce03 100644 --- a/Runtime/Scripts/AudioStreamTrack.cs +++ b/Runtime/Scripts/AudioStreamTrack.cs @@ -10,7 +10,7 @@ namespace Unity.WebRTC /// Delegate to be called when new audio data is received. /// /// - /// `AudioReadEventHandler` is a delegate to be called when received new audio data. + /// `AudioReadEventHandler` is a delegate to be called when new audio data is received. /// /// Float array containing audio data samples. /// Number of audio channels. From 79013018d30a1ab5356105e6c4b7280fbccb7bdd Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Sat, 7 Dec 2024 08:41:13 +0900 Subject: [PATCH 48/67] Update Runtime/Scripts/AudioStreamTrack.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/AudioStreamTrack.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/AudioStreamTrack.cs b/Runtime/Scripts/AudioStreamTrack.cs index a72a8cce03..0e5517e746 100644 --- a/Runtime/Scripts/AudioStreamTrack.cs +++ b/Runtime/Scripts/AudioStreamTrack.cs @@ -440,7 +440,7 @@ public void SetData(ReadOnlySpan span, int channels, int sampleRate) #endif /// - /// Event to be fired when received new audio data. + /// Event to be fired when new audio data is received. /// /// /// `onReceived` event is fired when received new audio data. From f9cfb8acd7159a25b3eb9e6cde32885679de48d1 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Sat, 7 Dec 2024 08:41:21 +0900 Subject: [PATCH 49/67] Update Runtime/Scripts/AudioStreamTrack.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/AudioStreamTrack.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/AudioStreamTrack.cs b/Runtime/Scripts/AudioStreamTrack.cs index 0e5517e746..77c5fca3b6 100644 --- a/Runtime/Scripts/AudioStreamTrack.cs +++ b/Runtime/Scripts/AudioStreamTrack.cs @@ -443,7 +443,7 @@ public void SetData(ReadOnlySpan span, int channels, int sampleRate) /// Event to be fired when new audio data is received. /// /// - /// `onReceived` event is fired when received new audio data. + /// `onReceived` event is fired when new audio data is received. /// /// /// Date: Sat, 7 Dec 2024 11:55:18 +0900 Subject: [PATCH 50/67] Update Runtime/Scripts/WebRTC.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/WebRTC.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index d6c6fef87b..576f30937a 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -725,7 +725,7 @@ public static IEnumerator Update() /// Executes any pending tasks generated asynchronously during the WebRTC runtime. /// /// - /// `ExecutePendingTasks` method processes pending tasks generated during WebRTC operations, up to a specified timeout. + /// `ExecutePendingTasks` method processes pending tasks generated during WebRTC operations until reaching the specified timeout. /// /// /// The amount of time in milliseconds that the task queue can take before task execution will cease. From e788e47b5fe15ae616ae0ac488bad5b61581fcdf Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Sat, 7 Dec 2024 11:55:48 +0900 Subject: [PATCH 51/67] Update Runtime/Scripts/WebRTC.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/WebRTC.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 576f30937a..1ef168b9b5 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -728,7 +728,7 @@ public static IEnumerator Update() /// `ExecutePendingTasks` method processes pending tasks generated during WebRTC operations until reaching the specified timeout. /// /// - /// The amount of time in milliseconds that the task queue can take before task execution will cease. + /// The maximum time in milliseconds for which to process the task queue before task execution stops. /// /// /// `true` if all pending tasks were completed within milliseconds and `false` otherwise. From e0d48e3be2d33de1c124c418113ef286ee2dc307 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Sat, 7 Dec 2024 11:55:59 +0900 Subject: [PATCH 52/67] Update Runtime/Scripts/WebRTC.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/WebRTC.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 1ef168b9b5..0cfbf8198b 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -758,7 +758,7 @@ public static bool enableLimitTextureSize } /// - /// Logger that is used for capturing debug messages within the WebRTC package. + /// Logger used for capturing debug messages within the WebRTC package. /// Defaults to Debug.unityLogger. /// /// Throws if setting a null logger. From ecff9a5806c9e136c349f1003dd0d89a0a66aeb7 Mon Sep 17 00:00:00 2001 From: Masayuki Iwai Date: Sat, 7 Dec 2024 11:56:11 +0900 Subject: [PATCH 53/67] Update Runtime/Scripts/WebRTC.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/WebRTC.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 0cfbf8198b..29a644c8f8 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -892,7 +892,7 @@ internal static RTCError ValidateTextureSize(int width, int height, RuntimePlatf } /// - /// Validates the specified graphics format to ensure it is supported. + /// Checks whether the specified graphics format is supported. /// /// /// `ValidateGraphicsFormat` method checks whether the provided `GraphicsFormat` is compatible with the current graphics device. From dbddf28793b5b78fcd2a346086dd2fb67a6fb056 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:05:46 +0900 Subject: [PATCH 54/67] gitignore: ignore UserSettings and Logs (#1068) --- .gitignore | 2 ++ WebRTC~/UserSettings/EditorUserSettings.asset | 21 ------------------- 2 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 WebRTC~/UserSettings/EditorUserSettings.asset diff --git a/.gitignore b/.gitignore index 5f015ee8cb..5956a9e5c4 100644 --- a/.gitignore +++ b/.gitignore @@ -218,6 +218,7 @@ upm-ci~/ Plugin~/webrtc/* WebRTC~/CodeCoverage WebRTC~/TestResults-* +WebRTC~/Logs *.dbmdl *.dbproj.schemaview *.jfm @@ -359,6 +360,7 @@ Plugin~/WebRTCPluginTest/webrtc-test.xcodeproj/* [Tt]emp/ [Oo]bj/ [Bb]uild/ +[Uu]serSettings/ # Exclude Documentation folder !Documentation~ diff --git a/WebRTC~/UserSettings/EditorUserSettings.asset b/WebRTC~/UserSettings/EditorUserSettings.asset deleted file mode 100644 index d2ce6fb40e..0000000000 --- a/WebRTC~/UserSettings/EditorUserSettings.asset +++ /dev/null @@ -1,21 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!162 &1 -EditorUserSettings: - m_ObjectHideFlags: 0 - serializedVersion: 4 - m_ConfigSettings: - vcSharedLogLevel: - value: 0d5e400f0650 - flags: 0 - m_VCAutomaticAdd: 1 - m_VCDebugCom: 0 - m_VCDebugCmd: 0 - m_VCDebugOut: 0 - m_SemanticMergeMode: 2 - m_VCShowFailedCheckout: 1 - m_VCOverwriteFailedCheckoutAssets: 1 - m_VCProjectOverlayIcons: 1 - m_VCHierarchyOverlayIcons: 1 - m_VCOtherOverlayIcons: 1 - m_VCAllowAsyncUpdate: 1 From d04d871cdd57880b63d48517d059a4e64694708e Mon Sep 17 00:00:00 2001 From: H3idi-X Date: Mon, 9 Dec 2024 17:54:56 +0900 Subject: [PATCH 55/67] Update Runtime/Scripts/RTCDataChannel.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/RTCDataChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 526df5d10d..ac9c56845a 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -486,7 +486,7 @@ public DelegateOnError OnError public string Label => NativeMethods.DataChannelGetLabel(GetSelfOrThrow()).AsAnsiStringWithFreeMem(); /// - /// Returns the sub protocol being used by the data channel to transmit and process messages. + /// Returns the subprotocol being used by the data channel to transmit and process messages. /// /// /// The `Protocol` property retrieves the sub protocol negotiated for this data channel, which governs the rules for message format and communication behavior between peers. From cc15a51954ddff3676b74121dcedc4d47822c07d Mon Sep 17 00:00:00 2001 From: H3idi-X Date: Mon, 9 Dec 2024 17:55:07 +0900 Subject: [PATCH 56/67] Update Runtime/Scripts/RTCDataChannel.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/RTCDataChannel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index ac9c56845a..b209981732 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -489,9 +489,9 @@ public DelegateOnError OnError /// Returns the subprotocol being used by the data channel to transmit and process messages. /// /// - /// The `Protocol` property retrieves the sub protocol negotiated for this data channel, which governs the rules for message format and communication behavior between peers. + /// The `Protocol` property retrieves the subprotocol negotiated for this data channel, which governs the rules for message format and communication behavior between peers. /// This property is critical for ensuring compatibility and understanding between different systems or applications using the channel, especially when custom protocols are used. - /// If no protocol was specified during the data channel's creation, this property returns an empty string, indicating that no particular sub protocol is in effect. + /// If no protocol was specified during the data channel's creation, this property returns an empty string, indicating that no particular subprotocol is in effect. /// /// /// Date: Mon, 9 Dec 2024 17:55:19 +0900 Subject: [PATCH 57/67] Update Runtime/Scripts/RTCDataChannel.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/RTCDataChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index b209981732..8ba25ebf39 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -457,7 +457,7 @@ public DelegateOnError OnError public int Id => NativeMethods.DataChannelGetID(GetSelfOrThrow()); /// - /// Returns a string containing a name describing the data channel which are not required to be unique. + /// Returns a string description of the data channel, which is not required to be unique. /// /// /// The `Label` property specifies a name for the data channel, which is set when the channel is created. From 74972949da27efac0de181f248cb53c74d63171b Mon Sep 17 00:00:00 2001 From: H3idi-X Date: Mon, 9 Dec 2024 17:55:33 +0900 Subject: [PATCH 58/67] Update Runtime/Scripts/RTCDataChannel.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/RTCDataChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 8ba25ebf39..7118c4542b 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -394,7 +394,7 @@ public DelegateOnClose OnClose } /// - /// Delegate to be called when the errors occur. + /// Delegate to be called when errors occur. /// /// /// The `OnClose` delegate is triggered when the data channel's transport layer is terminated, signifying the channel's transition to a closed state. From f0b2db323a92b5c2ccfd1d991b1a0468ac314837 Mon Sep 17 00:00:00 2001 From: H3idi-X Date: Mon, 9 Dec 2024 17:55:51 +0900 Subject: [PATCH 59/67] Update Runtime/Scripts/RTCDataChannel.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/RTCDataChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 7118c4542b..975c544a93 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -326,7 +326,7 @@ public DelegateOnMessage OnMessage } /// - /// Delegate to be called when the data channel's messages is opened or reopened. + /// Delegate to be called when the data channel's message transport mechanism is opened or reopened. /// /// /// The `OnOpen` delegate is triggered when the data channel successfully establishes its underlying transport mechanism. From ed7b2df3f9bceaceaa3f0dbe516f0685917df045 Mon Sep 17 00:00:00 2001 From: H3idi-X Date: Mon, 9 Dec 2024 17:56:02 +0900 Subject: [PATCH 60/67] Update Runtime/Scripts/RTCDataChannel.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- Runtime/Scripts/RTCDataChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 975c544a93..0f6a7370ef 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -360,7 +360,7 @@ public DelegateOnOpen OnOpen } /// - /// Delegate to be called when the data channel's messages is closed. + /// Delegate to be called when the data channel's message transport mechanism is closed. /// /// /// The close event is sent to the onclose event handler on an RTCDataChannel instance when the data transport for the data channel has closed. From 77204e07f0560bcc61f28a016f01e6f295698597 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:17:07 +0900 Subject: [PATCH 61/67] doc: improve the API doc of RTCSessionDescription (#1067) * initial improvement * rewrite * add sample code --- Runtime/Scripts/WebRTC.cs | 78 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index f1f53b59fc..1f8b239d97 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -421,18 +421,90 @@ public enum RTCDataChannelState Closed } + /// - /// + /// The RTCSessionDescription interface represents the setup of one side of a connection or a proposed connection. + /// It contains a description type that identifies the negotiation stage it pertains to, along with the session's SDP (Session Description Protocol) + /// details. /// + /// + /// Establishing a connection between two parties involves swapping RTCSessionDescription objects, + /// with each one proposing a set of connection setup options that the sender can accommodate. + /// The connection setup is finalized when both parties agree on a particular configuration. + /// + /// + /// pc1Senders; + /// private MediaStream videoStream; + /// private MediaStreamTrack track; + /// private DelegateOnNegotiationNeeded pc1OnNegotiationNeeded; + /// private bool videoUpdateStarted; + /// + /// private void Start() + /// { + /// pc1Senders = new List(); + /// pc1OnNegotiationNeeded = () => { StartCoroutine(PcOnNegotiationNeeded(_pc1)); }; + /// Call(); + /// } + /// + /// IEnumerator PcOnNegotiationNeeded(RTCPeerConnection pc) + /// { + /// var op = pc.CreateOffer(); + /// yield return op; + /// if (!op.IsError) + /// { + /// yield return StartCoroutine(OnCreateOfferSuccess(pc, op.Desc)); + /// } + /// } + /// + /// private void Call() + /// { + /// RTCConfiguration configuration = default; + /// configuration.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; + /// _pc1 = new RTCPeerConnection(ref configuration); + /// _pc1.OnNegotiationNeeded = pc1OnNegotiationNeeded; + /// + /// videoStream = Camera.main.CaptureStream(1280, 720); + /// track = videoStream.GetTracks().First(); + /// + /// pc1Senders.Add(_pc1.AddTrack(track)); + /// if (!videoUpdateStarted) + /// { + /// StartCoroutine(WebRTC.Update()); + /// videoUpdateStarted = true; + /// } + /// } + /// + /// private IEnumerator OnCreateOfferSuccess(RTCPeerConnection pc, RTCSessionDescription desc) + /// { + /// Debug.Log($"Offer created. SDP is: \n{desc.sdp}"); + /// var op = pc.SetLocalDescription(ref desc); + /// yield return op; + /// } + /// } + /// + /// ]]> + /// + /// + /// public struct RTCSessionDescription { /// - /// + /// An enum that specifies the type of the session description. Refer to . /// public RTCSdpType type; /// - /// + /// A string that holds the session's SDP information. /// [MarshalAs(UnmanagedType.LPStr)] public string sdp; From 789f7206ba812cf134e5a68cc2b72c3e4abd3c6c Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:19:58 +0900 Subject: [PATCH 62/67] project: add a symbolic link to the Documentation folder (#1070) --- WebRTC~/Packages/com.unity.webrtc/Documentation~ | 1 + 1 file changed, 1 insertion(+) create mode 120000 WebRTC~/Packages/com.unity.webrtc/Documentation~ diff --git a/WebRTC~/Packages/com.unity.webrtc/Documentation~ b/WebRTC~/Packages/com.unity.webrtc/Documentation~ new file mode 120000 index 0000000000..996d7bc39b --- /dev/null +++ b/WebRTC~/Packages/com.unity.webrtc/Documentation~ @@ -0,0 +1 @@ +../../../Documentation~ \ No newline at end of file From d0059ad97922fe617b59798e66a12744f896122e Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:53:35 +0900 Subject: [PATCH 63/67] doc: improve the API doc of CameraExtension (#1073) * add documentation * add class summary * simplify code * update example --- Runtime/Scripts/CameraExtension.cs | 72 ++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/Runtime/Scripts/CameraExtension.cs b/Runtime/Scripts/CameraExtension.cs index b4afafd330..b58a327979 100644 --- a/Runtime/Scripts/CameraExtension.cs +++ b/Runtime/Scripts/CameraExtension.cs @@ -1,26 +1,46 @@ using System; -using System.Collections.Concurrent; using System.ComponentModel; -using System.Runtime.InteropServices; using UnityEngine; -using UnityEngine.Experimental.Rendering; namespace Unity.WebRTC { + /// + /// Provides extension methods for objects to facilitate video streaming functionalities. + /// public static class CameraExtension { /// - /// Create an instance of to stream a camera. + /// Creates an instance of for streaming video from a object. /// /// - /// You should keep a reference of , created by this method. - /// This instance is collected by GC automatically if you don't own a reference. + /// It is recommended to maintain a reference to the instance created by this method. + /// Without a reference, the instance may be collected by the garbage collector automatically. /// - /// - /// - /// - /// - /// + /// The camera from which to capture video frames + /// The desired width of the video stream, in pixels. Must be greater than zero + /// The desired height of the video stream, in pixels. Must be greater than zero + /// The depth buffer format for the render texture. Default is + /// An optional to facilitate texture copying. Default is null + /// A instance that can be used to stream video. + /// + /// Creates a GameObject with a Camera component and a VideoStreamTrack capturing video from the camera. + /// (); + /// RawImage newSource = new GameObject("SourceImage").AddComponent(); + /// try + /// { + /// videoStreamTrackList.Add(newCam.CaptureStreamTrack(WebRTCSettings.StreamSize.x, WebRTCSettings.StreamSize.y)); + /// newSource.texture = newCam.targetTexture; + /// } + /// catch (Exception e) + /// { + /// Debug.LogError(e.Message); + /// } + /// } + /// ]]> + /// public static VideoStreamTrack CaptureStreamTrack(this Camera cam, int width, int height, RenderTextureDepth depth = RenderTextureDepth.Depth24, CopyTexture textureCopy = null) { @@ -48,15 +68,29 @@ public static VideoStreamTrack CaptureStreamTrack(this Camera cam, int width, in } /// - /// + /// Creates an instance of capturing video from a object. /// - /// - /// - /// - /// - /// - public static MediaStream CaptureStream(this Camera cam, int width, int height, - RenderTextureDepth depth = RenderTextureDepth.Depth24) + /// + /// It is recommended to maintain a reference to the instance created by this method. + /// Without a reference, the instance may be collected by the garbage collector automatically. + /// + /// The camera from which to capture video frames + /// The desired width of the video stream, in pixels. Must be greater than zero + /// The desired height of the video stream, in pixels. Must be greater than zero + /// The depth buffer format for the render texture. Default is + /// A containing the video track captured from the camera. + /// + /// Creates a MediaStream with a VideoStreamTrack capturing video from the camera. + /// + /// + + public static MediaStream CaptureStream(this Camera cam, int width, int height, RenderTextureDepth depth = RenderTextureDepth.Depth24) { var stream = new MediaStream(); var track = cam.CaptureStreamTrack(width, height, depth); From f113e247fe268be3b6cdd5db848888a885449db5 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:07:58 +0900 Subject: [PATCH 64/67] readme: update Unity badges (#1074) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9862981ac..ddf6a44230 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ unity 2020.3 unity 2021.3 -unity 2022.3 -unity 2023.1 +unity 2022.3 +unity 2023.1 **WebRTC for Unity** is a package that allows [WebRTC](https://webrtc.org) to be used in Unity. From 5f873f2ac17f3514ffe8a8514e94bff5e93e3982 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:17:38 +0900 Subject: [PATCH 65/67] release: 3.0.0-pre.8 (#1075) * update package.json * update changelog * update readme * update ValidationExceptions.json * update install.md * update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + CHANGELOG.md | 19 ++++++++++ Documentation~/install.md | 2 +- README.md | 53 ++++++++++++++------------- ValidationExceptions.json | 2 +- package.json | 2 +- 6 files changed, 50 insertions(+), 29 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 7a036d9752..7103c47882 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -27,6 +27,7 @@ body: What version of the package are you using? You can check the Unity version in Package Manager Window. See [manual](https://docs.unity3d.com/Manual/upm-ui.html). options: + - 3.0.0-pre.8 - 3.0.0-pre.7 - 3.0.0-pre.6 - 3.0.0-pre.5 diff --git a/CHANGELOG.md b/CHANGELOG.md index a6032abbf9..bd240d2712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to the webrtc package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [3.0.0-pre.8] - 2024-12-12 + +### Changed +- doc: improve the API doc of CameraExtension +- doc: improve the API doc of RTCSessionDescription +- doc: improve the API doc of RTCDataChannel +- doc: Improve the API doc of RTCConfiguration +- doc: Improve the API doc of RTCRtpEncodingParameters +- doc: Improve the API doc of RTCRtpSender +- doc: Improve the API doc of RTCRtpTransceiver +- doc: Improve the API doc of WebRTC +- doc: Improve the API doc of AudioStreamTrack +- doc: Improve the API doc of VideoStreamTrack +- doc: Improve the API doc of MediaStreamTrack +- doc: Improve the API doc of MediaStream +- doc: Improve the API doc of RTCPeerConnection +- doc: Improve the API doc of RTCIceServer + + ## [3.0.0-pre.7] - 2023-10-20 ### Added diff --git a/Documentation~/install.md b/Documentation~/install.md index 15f0b5cf17..58e2506f1f 100644 --- a/Documentation~/install.md +++ b/Documentation~/install.md @@ -11,7 +11,7 @@ Check Package Manager window, Click `+` button and select `Add package from git Input the string below to the input field. ``` -com.unity.webrtc@3.0.0-pre.7 +com.unity.webrtc@3.0.0-pre.8 ``` The list of version string is [here](https://github.com/Unity-Technologies/com.unity.webrtc/tags). In most cases, the latest version is recommended to use. diff --git a/README.md b/README.md index ddf6a44230..cec035486a 100644 --- a/README.md +++ b/README.md @@ -32,32 +32,33 @@ Please read this if you have an interest to customize native code in this projec ## Roadmap -| Version | libwebrtc version | Focus | When | -| ------- | ----------------- | ----- | ---- | -| `1.0.0-preview` | [M72](https://groups.google.com/d/msg/discuss-webrtc/3h4y0fimHwg) | - First release | Sep 2019 | -| `1.1.0-preview` | [M72](https://groups.google.com/d/msg/discuss-webrtc/3h4y0fimHwg) | - IL2CPP Support
- Linux platform Support
- Add software encoder | Feb 2020 | -| `2.0.0-preview` | [M79](https://groups.google.com/d/msg/discuss-webrtc/Ozvbd0p7Q1Y) | - Multi camera
- DirectX12 (DXR) Support | Apr 2020 | -| `2.1.0-preview` | [M84](https://groups.google.com/g/discuss-webrtc/c/MRAV4jgHYV0) | - Profiler tool
- Bitrate control | Aug 2020 | -| `2.2.0-preview` | [M85](https://groups.google.com/g/discuss-webrtc/c/Qq3nsR2w2HU) | - Video decoder (VP8, VP9 only)
- Vulkan HW encoder support
- MacOS HW encoder support | Oct 2020 | -| `2.3.0-preview` | [M85](https://groups.google.com/g/discuss-webrtc/c/Qq3nsR2w2HU) | - iOS platform support | Dec 2020 | -| `2.4.0-exp.1` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Android platform support | Apr 2021 | -| `2.4.0-exp.2` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Fix bugs | May 2021 | -| `2.4.0-exp.3` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Fix bugs | Jun 2021 | -| `2.4.0-exp.4` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Audio renderer support
- Apple Silicon support | Aug 2021 | -| `2.4.0-exp.5` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix audio streaming issues | Feb 2022 | -| `2.4.0-exp.6` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Feb 2022 | -| `2.4.0-exp.7` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix video streaming issues | May 2022 | -| `2.4.0-exp.8` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix bugs | Jul 2022 | -| `2.4.0-exp.9` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Aug 2022 | -| `2.4.0-exp.10` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Aug 2022 | -| `2.4.0-exp.11` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Sep 2022 | -| `3.0.0-pre.1` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix bugs | Nov 2022 | -| `3.0.0-pre.2` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Update libwebrtc M107 | Dec 2022 | -| `3.0.0-pre.3` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Fix bugs | Dec 2022 | -| `3.0.0-pre.4` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Fix bugs | Jan 2023 | -| `3.0.0-pre.5` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Encoded Transform API | Apr 2023 | -| `3.0.0-pre.6` | [M112](https://groups.google.com/g/discuss-webrtc/c/V-XFau9W9gY) | - Fix bugs | Jul 2023 | -| `3.0.0-pre.7` | [M116](https://groups.google.com/g/discuss-webrtc/c/bEsO8Lz7psE) | - Update libwebrtc M116
- Simulcast for NVIDIA H.264 | Oct 2023 | +| Version | libwebrtc version | Focus | When | +|------------------| ----------------- | ----- | ---- | +| `1.0.0-preview` | [M72](https://groups.google.com/d/msg/discuss-webrtc/3h4y0fimHwg) | - First release | Sep 2019 | +| `1.1.0-preview` | [M72](https://groups.google.com/d/msg/discuss-webrtc/3h4y0fimHwg) | - IL2CPP Support
- Linux platform Support
- Add software encoder | Feb 2020 | +| `2.0.0-preview` | [M79](https://groups.google.com/d/msg/discuss-webrtc/Ozvbd0p7Q1Y) | - Multi camera
- DirectX12 (DXR) Support | Apr 2020 | +| `2.1.0-preview` | [M84](https://groups.google.com/g/discuss-webrtc/c/MRAV4jgHYV0) | - Profiler tool
- Bitrate control | Aug 2020 | +| `2.2.0-preview` | [M85](https://groups.google.com/g/discuss-webrtc/c/Qq3nsR2w2HU) | - Video decoder (VP8, VP9 only)
- Vulkan HW encoder support
- MacOS HW encoder support | Oct 2020 | +| `2.3.0-preview` | [M85](https://groups.google.com/g/discuss-webrtc/c/Qq3nsR2w2HU) | - iOS platform support | Dec 2020 | +| `2.4.0-exp.1` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Android platform support | Apr 2021 | +| `2.4.0-exp.2` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Fix bugs | May 2021 | +| `2.4.0-exp.3` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Fix bugs | Jun 2021 | +| `2.4.0-exp.4` | [M89](https://groups.google.com/g/discuss-webrtc/c/Zrsn2hi8FV0) | - Audio renderer support
- Apple Silicon support | Aug 2021 | +| `2.4.0-exp.5` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix audio streaming issues | Feb 2022 | +| `2.4.0-exp.6` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Feb 2022 | +| `2.4.0-exp.7` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix video streaming issues | May 2022 | +| `2.4.0-exp.8` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix bugs | Jul 2022 | +| `2.4.0-exp.9` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Aug 2022 | +| `2.4.0-exp.10` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Aug 2022 | +| `2.4.0-exp.11` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Hotfix | Sep 2022 | +| `3.0.0-pre.1` | [M92](https://groups.google.com/g/discuss-webrtc/c/hks5zneZJbo) | - Fix bugs | Nov 2022 | +| `3.0.0-pre.2` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Update libwebrtc M107 | Dec 2022 | +| `3.0.0-pre.3` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Fix bugs | Dec 2022 | +| `3.0.0-pre.4` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Fix bugs | Jan 2023 | +| `3.0.0-pre.5` | [M107](https://groups.google.com/g/discuss-webrtc/c/StVFkKuSRc8) | - Encoded Transform API | Apr 2023 | +| `3.0.0-pre.6` | [M112](https://groups.google.com/g/discuss-webrtc/c/V-XFau9W9gY) | - Fix bugs | Jul 2023 | +| `3.0.0-pre.7` | [M116](https://groups.google.com/g/discuss-webrtc/c/bEsO8Lz7psE) | - Update libwebrtc M116
- Simulcast for NVIDIA H.264 | Oct 2023 | +| `3.0.0-pre.8` | [M116](https://groups.google.com/g/discuss-webrtc/c/bEsO8Lz7psE) | | | ## Licenses diff --git a/ValidationExceptions.json b/ValidationExceptions.json index 2c5d358312..467a15623c 100644 --- a/ValidationExceptions.json +++ b/ValidationExceptions.json @@ -5,7 +5,7 @@ { "ValidationTest": "Restricted File Type Validation", "ExceptionMessage": "/Runtime/Plugins/x86_64/webrtc.dll should not be included in packages unless absolutely necessary. Please confirm that its inclusion is deliberate and intentional.", - "PackageVersion": "3.0.0-pre.7" + "PackageVersion": "3.0.0-pre.8" } ] } \ No newline at end of file diff --git a/package.json b/package.json index e20af15a6c..576d13e521 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.webrtc", "displayName": "WebRTC", - "version": "3.0.0-pre.7", + "version": "3.0.0-pre.8", "unity": "2020.3", "description": "The WebRTC package provides browsers and mobile applications with Real-Time Communications (RTC) capabilities.", "keywords": [ From c376696a233dd4f5b394bb7498b12d4a2e87c8e6 Mon Sep 17 00:00:00 2001 From: Thaina Date: Mon, 18 Dec 2023 03:08:48 +0700 Subject: [PATCH 66/67] experimental: Experimental WebGL implementation (#478) * webgl initial plugin * merge with @gtk2k prototype (part 1) * Add newtonsoft json parser in package dependencies * merge with @gtk2k prototype (part 2) * webgl lib compile fixes * WebGL build can be done * bugfix : remove useless condition that brake linux compatibility * Initial WebGL webrtc plugins * Merge branch 'feature/webgl-support' of https://github.com/antoined73/com.unity.webrtc into antoined73-feature/webgl-support * Added OnTextMessage on RTCDataChannel Renamed VideoStreamTrack * Remove duplicate method RegisterRenderingWebRTCPlugin in WebTC.cs * Merge branch 'feature/webgl-support' of https://github.com/antoined73/com.unity.webrtc into antoined73-feature/webgl-support * Signaling is not part of WebRTC specification * Browser crash on DataChannel options using newtonsoft * Added missing ContextUnRegisterMediaStreamObserver, not implemented yet * Removed audio/video offer options * Added missing ReceiverGetStreams, not implemented yet * RTCIceCandidates on WebGL * Moved RTCDataChannel onmessage listeren to OnDataChannel (receiver) from createDataChannel (sender) * Added VideoTrack with MediaStream created by WebGL's getUserMedia. Requires the RTCPeerConnection to have a MediaStream to attach to. * Fixed CreateAudioTrack * Fixed adding getUserMedia stream tracks to unity MediaStream * Enabled open/close/message on both creation and ondatachannel objects * Negotiation workaround Often the candidate message is handled earlier than the description (probably due coroutines when setting the description) For now the icecandidate handling waits 1 second, which is sufficient. But a fix would be to store an ICECandidate queue on the peer connection and handle the queue when the description is set * Workaround to parse datachannel options, jsonutility/newtonsoft have trouble with the Optional fields * Samples: working DataChannels and PeerConnection - Fixed MediaStream getTracks with deserializer - Added adapter.js, should improve browser compatibility - Using objects instead of 'this' in jslib for peer events * Added TextMessage event handling for DataChannels sample * Fix for UpdateRendererTexture 'no video' issue * Temp to show video tracks created by peer.ontrack * getTracks using WebRTC.Deserialize, prevents creation of existing tracks * Using Deserializer for RTP objects * Firefox requires a string, instead of int for credentialType * Fixed optional fields serialization in RTCConfiguration * Disabled context check. Context is already removed when disposing peer/data/media objects * Improved UWManaged array. C# objects can be disposed but still accessed at a later stage * Delete transceiver * Fixed DeleteMediaStreamTrack parameters * Registering stream for videotrack is not needed * Removing video tracks from local/remote video list object on DeleteMediaStreamTrack * Added RegisterMediaStreamObserver, not tested * DataChannel options parsing done in jslib file * Moved parsing of RTCConfiguration * Updated webrtc.dll * Wip: create audiotrack * Corrected RTCConfiguration parsing with new optional fields using enums * Fixed webcam stream from getUserMedia Improved creation of HTMLMediaElements, local and remote tracks are created on AddTrack and ontrack respectively, which enables the receiver to play the received audio stream. The local video track is currently only needed to display the local webcam stream, but is created for every videotrack (todo) * Added a check for RTCDataChannels onclose/open. onclose is fired when already disposed in unity. * Fixed icecandidate struct * Missing import and removed peer label (for now) * Fixed some enum values (including ice/peer connection state) * RTCStats. Not fully checked for every enum/statstype. Note: Requires "jillejr.newtonsoft.json-for-unity" * Don't show video elements * Use strToPtr * RTPParameters * RTPParameters * RTPParameters: removed debug log and added todo * setCodec support, only supported in chrome * Datachannel config parsing to support firefox * Removed debug log * Removed debug logs * Removed dual initializers (sourcetree bug?) * Moved OnRemoveTrack * Added guard for sender track * Moved #!UNITY_WEBGL inside WebRTC.Sync * Reducing the number of #if !UNITY_WEBL directives * Using RTCOfferOptions object * Added todo's * Removed commented lines * Removed comments lines in RTCStats * Removed/optimized some #if webgl directives * Added WebGLSample * Default flip, so the correct image is transported over webrtc * Updated local render method with flip * Updated samples to allow webgl builds * Some comments/codestyle changes * Added getUserMedia TODO * Reverted Android meta change * fix test-case * revert asmdef * fixed compile error * fix compile error (cherry picked from commit 8eb9e4bcc1d425b54119f1e5315d468a298dbfb4) * add webgl platform to CI test (cherry picked from commit a478886561f9a014c520fab08053a9c1a101ea7e) * fix yaml (cherry picked from commit 00939df785d1b5da64d7e3ab0795600e4f17d1a1) * fix yaml * fix * fix bug * fix yaml * fix permission * add job to test for webgl platform * WIP test runner and jslib fixes - Runs [Test] methods - Parameterized tests are disabled [TestCase] - Added webgl platform exclude to [UnityTest] - VideoReceiver disabled - AudioStream disabled - (todo) NativeAPI - Fixed several native method bugs * wip NativeAPI test enabled most test cases needs json dll * Passing DataChannelTest * Fixed several tests - Transceiver direction - Ice candidate component (added todo/comments) - MediaStreamAddTrack return value - ContextCreate methods with labels - Dummy TrackSource methods - AudioTrack id (added custom guid field) - MediaStream id (added custom guid field) - Fixed NativeAPITest for WebGL MediaStream tracks length * More test fixes - Replaced return with array [errortype,object] - AddTrack retursn both sender and errortype - SetDescription returns errortype and message. TODO: make async/await to actually return the error(type) * Fixed test cases - Applied OptionalFields for PeerConnection config - Fixed IceTransportPolicy enum - Fixed Set Description of PeerConnection - Enabled VideoReceiveTest (but are commented out) - Added TODOs * fix yaml for package test * Add Preserve attribute for avoiding code stripping * Fixed Transceiver SetCodecsPreferences * Fixed incorrect assert in SenderGetParameter test * Excluded failing tests + added todos * Fixed SetLocalDescriptionWithoutDescription * Added WebGL sample to build settings * add edtitor version for auto-testing on CI * Replaced Pointer_Stringify with UTF8ToString to support 2021.2 * fix: undefined `side` variable * fix: undefined `err` variable * fix: pass byt length to `DataChannelNativeOnMessage` callback * ops... Co-authored-by: Antoine Dezarnaud Co-authored-by: Antoine Dezarnaud Co-authored-by: kazuki Co-authored-by: Muka Schultze Co-authored-by: gtk2k --- .yamato/upm-ci-webrtc-packages.yml | 86 + Runtime/Plugins/WebGL.meta | 8 + Runtime/Plugins/WebGL/AudioStreamTrack.jslib | 19 + .../Plugins/WebGL/AudioStreamTrack.jslib.meta | 44 + Runtime/Plugins/WebGL/Common.jslib | 211 + Runtime/Plugins/WebGL/Common.jslib.meta | 44 + Runtime/Plugins/WebGL/Context.jslib | 286 ++ Runtime/Plugins/WebGL/Context.jslib.meta | 44 + Runtime/Plugins/WebGL/Enum.jslib | 261 ++ Runtime/Plugins/WebGL/Enum.jslib.meta | 44 + Runtime/Plugins/WebGL/MediaStream.jslib | 149 + Runtime/Plugins/WebGL/MediaStream.jslib.meta | 44 + Runtime/Plugins/WebGL/MediaStreamTrack.jslib | 34 + .../Plugins/WebGL/MediaStreamTrack.jslib.meta | 44 + Runtime/Plugins/WebGL/RTCDataChannel.jslib | 163 + .../Plugins/WebGL/RTCDataChannel.jslib.meta | 44 + Runtime/Plugins/WebGL/RTCIceCandidate.jslib | 57 + .../Plugins/WebGL/RTCIceCandidate.jslib.meta | 44 + Runtime/Plugins/WebGL/RTCPeerConnection.jslib | 695 ++++ .../WebGL/RTCPeerConnection.jslib.meta | 44 + Runtime/Plugins/WebGL/RTCRtpReceiver.jslib | 18 + .../Plugins/WebGL/RTCRtpReceiver.jslib.meta | 44 + Runtime/Plugins/WebGL/RTCRtpSender.jslib | 43 + Runtime/Plugins/WebGL/RTCRtpSender.jslib.meta | 44 + Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib | 70 + .../WebGL/RTCRtpTransceiver.jslib.meta | 44 + Runtime/Plugins/WebGL/VideoRenderer.jslib | 72 + .../Plugins/WebGL/VideoRenderer.jslib.meta | 44 + Runtime/Plugins/WebGL/VideoStreamTrack.jslib | 164 + .../Plugins/WebGL/VideoStreamTrack.jslib.meta | 44 + Runtime/Plugins/WebGL/adapter.jspre | 3423 +++++++++++++++++ Runtime/Plugins/WebGL/adapter.jspre.meta | 76 + Runtime/Scripts/Context.cs | 145 +- .../CreateSessionDescriptionObserver.cs | 2 +- Runtime/Scripts/MediaStream.cs | 35 +- Runtime/Scripts/MediaStreamTrack.cs | 34 + Runtime/Scripts/RTCDataChannel.cs | 33 + Runtime/Scripts/RTCIceCandidate.cs | 92 +- Runtime/Scripts/RTCPeerConnection.cs | 116 +- Runtime/Scripts/RTCRtpReceiver.cs | 14 +- Runtime/Scripts/RTCRtpSender.cs | 30 + Runtime/Scripts/RTCRtpTransceiver.cs | 21 + Runtime/Scripts/RTPParameters.cs | 1 - Runtime/Scripts/VideoStreamTrack.cs | 4 + Runtime/Scripts/WebRTC.cs | 276 +- Runtime/Unity.WebRTC.Runtime.asmdef | 2 + Samples~/Audio/AudioSample.cs | 3 +- Samples~/DataChannel/DataChannelSample.cs | 9 + Samples~/Menu/Menu.unity | 79 + Samples~/Scripts/SceneSelectUI.cs | 7 + Samples~/WebGL.meta | 8 + Samples~/WebGL/WebGL.unity | 2386 ++++++++++++ Samples~/WebGL/WebGL.unity.meta | 7 + Samples~/WebGL/WebGLSample.cs | 424 ++ Samples~/WebGL/WebGLSample.cs.meta | 3 + Tests/Editor/PluginTest.cs | 8 +- Tests/Runtime/AudioStreamTrackTest.cs | 6 +- Tests/Runtime/ContextTest.cs | 6 + Tests/Runtime/DataChannelTest.cs | 4 +- Tests/Runtime/IceCandidateTest.cs | 8 + Tests/Runtime/MediaStreamTest.cs | 11 +- Tests/Runtime/MediaStreamTrackTest.cs | 20 +- Tests/Runtime/NativeAPITest.cs | 92 +- Tests/Runtime/PeerConnectionTest.cs | 45 +- Tests/Runtime/StatsReportTest.cs | 2 +- Tests/Runtime/TransceiverTest.cs | 1 + .../Runtime/Unity.WebRTC.RuntimeTests.asmdef | 4 +- Tests/Runtime/VideoReceiveTest.cs | 26 + Tests/Runtime/WebRTCTest.cs | 7 +- .../ProjectSettings/EditorBuildSettings.asset | 3 + package.json | 3 +- 71 files changed, 10342 insertions(+), 86 deletions(-) create mode 100644 Runtime/Plugins/WebGL.meta create mode 100644 Runtime/Plugins/WebGL/AudioStreamTrack.jslib create mode 100644 Runtime/Plugins/WebGL/AudioStreamTrack.jslib.meta create mode 100644 Runtime/Plugins/WebGL/Common.jslib create mode 100644 Runtime/Plugins/WebGL/Common.jslib.meta create mode 100644 Runtime/Plugins/WebGL/Context.jslib create mode 100644 Runtime/Plugins/WebGL/Context.jslib.meta create mode 100644 Runtime/Plugins/WebGL/Enum.jslib create mode 100644 Runtime/Plugins/WebGL/Enum.jslib.meta create mode 100644 Runtime/Plugins/WebGL/MediaStream.jslib create mode 100644 Runtime/Plugins/WebGL/MediaStream.jslib.meta create mode 100644 Runtime/Plugins/WebGL/MediaStreamTrack.jslib create mode 100644 Runtime/Plugins/WebGL/MediaStreamTrack.jslib.meta create mode 100644 Runtime/Plugins/WebGL/RTCDataChannel.jslib create mode 100644 Runtime/Plugins/WebGL/RTCDataChannel.jslib.meta create mode 100644 Runtime/Plugins/WebGL/RTCIceCandidate.jslib create mode 100644 Runtime/Plugins/WebGL/RTCIceCandidate.jslib.meta create mode 100644 Runtime/Plugins/WebGL/RTCPeerConnection.jslib create mode 100644 Runtime/Plugins/WebGL/RTCPeerConnection.jslib.meta create mode 100644 Runtime/Plugins/WebGL/RTCRtpReceiver.jslib create mode 100644 Runtime/Plugins/WebGL/RTCRtpReceiver.jslib.meta create mode 100644 Runtime/Plugins/WebGL/RTCRtpSender.jslib create mode 100644 Runtime/Plugins/WebGL/RTCRtpSender.jslib.meta create mode 100644 Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib create mode 100644 Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib.meta create mode 100644 Runtime/Plugins/WebGL/VideoRenderer.jslib create mode 100644 Runtime/Plugins/WebGL/VideoRenderer.jslib.meta create mode 100644 Runtime/Plugins/WebGL/VideoStreamTrack.jslib create mode 100644 Runtime/Plugins/WebGL/VideoStreamTrack.jslib.meta create mode 100644 Runtime/Plugins/WebGL/adapter.jspre create mode 100644 Runtime/Plugins/WebGL/adapter.jspre.meta create mode 100644 Samples~/WebGL.meta create mode 100644 Samples~/WebGL/WebGL.unity create mode 100644 Samples~/WebGL/WebGL.unity.meta create mode 100644 Samples~/WebGL/WebGLSample.cs create mode 100644 Samples~/WebGL/WebGLSample.cs.meta diff --git a/.yamato/upm-ci-webrtc-packages.yml b/.yamato/upm-ci-webrtc-packages.yml index dca7b2ae25..eae176a889 100644 --- a/.yamato/upm-ci-webrtc-packages.yml +++ b/.yamato/upm-ci-webrtc-packages.yml @@ -1,4 +1,59 @@ {% metadata_file .yamato/package.metafile %} +{% metadata_file .yamato/meta/environments.yml %} + +editors: + - version: 2019.4 + - version: 2020.3 + - version: 2021.2 + +platforms: + - name: win + type: Unity::VM + gpu_type: Unity::VM::GPU + image: renderstreaming/win10:v0.3.7-728388 + gpu_image: renderstreaming/win10:v0.3.7-728285 + flavor: b1.large + model: rtx2080 + build_command: BuildScripts~/build_plugin_win.cmd + test_command: BuildScripts~/test_plugin_win.cmd + plugin_path: Runtime/Plugins/x86_64/webrtc.dll + - name: linux + type: Unity::VM + gpu_type: Unity::VM::GPU + image: renderstreaming/ubuntu-18.04:latest + gpu_image: renderstreaming/ubuntu-18.04:latest + flavor: b1.large + model: rtx2080 + build_command: BuildScripts~/build_plugin_linux.sh + test_command: BuildScripts~/test_plugin_linux.sh + plugin_path: Runtime/Plugins/x86_64/libwebrtc.so + - name: macos + type: Unity::metal::macmini + gpu_type: Unity::metal::macmini + image: package-ci/mac:latest + gpu_image: package-ci/mac:latest + flavor: m1.mac + build_command: BuildScripts~/build_plugin_mac.sh + test_command: BuildScripts~/test_plugin_mac.sh + plugin_path: Runtime/Plugins/macOS/webrtc.bundle/** + - name: ios + type: Unity::metal::macmini + gpu_type: Unity::metal::macmini + image: package-ci/mac:latest + gpu_image: package-ci/mac:latest + flavor: m1.mac + build_command: BuildScripts~/build_plugin_ios.sh + test_command: BuildScripts~/test_plugin_ios.sh + plugin_path: Runtime/Plugins/iOS/webrtc.framework/** + - name: android + type: Unity::VM + gpu_type: Unity::VM + image: renderstreaming/android-linux-build:latest + gpu_image: renderstreaming/android-linux-build:latest + flavor: b1.large + build_command: BuildScripts~/build_plugin_android.sh + test_command: BuildScripts~/test_plugin_android.sh + plugin_path: Runtime/Plugins/Android/libwebrtc.aar test_targets: - name: win @@ -316,6 +371,37 @@ test_{{ package.name }}_{{ editor.version }}_android_{{ target.name }}: paths: - "build/test-results/**" {% endfor %} + +test_{{ package.name }}_{{ editor.version }}_webgl_win: + name: Test {{ package.packagename }} with {{ editor.version }} on WebGL Windows + agent: + type: Unity::VM::GPU + image: package-ci/win10:stable + flavor: b1.large + dependencies: + - .yamato/upm-ci-{{ package.name }}-packages.yml#pack_{{ package.name }} + commands: + - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple + - unity-downloader-cli -u {{ editor.version }} -c WebGL -c Editor -c StandaloneSupport-IL2CPP --fast -w + - curl -s https://artifactory.prd.it.unity3d.com/artifactory/unity-tools/utr-standalone/utr.bat --output utr.bat + - ./utr --suite=playmode --platform=WebGL --editor-location=.Editor --testproject=WebRTC~ --artifacts_path=build/test-results --timeout=5400 + +test_{{ package.name }}_{{ editor.version }}_webgl_macos: + name: Test {{ package.packagename }} with {{ editor.version }} on WebGL macOS + agent: + type: Unity::VM::osx + image: package-ci/mac:latest + flavor: m1.mac + dependencies: + - .yamato/upm-ci-{{ package.name }}-packages.yml#pack_{{ package.name }} + commands: + - find upm-ci~/packages/ -name "*.tgz" | xargs -I file tar xvf file -C upm-ci~ + - cp -rf upm-ci~/package/Runtime/Plugins Runtime/ + - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple + - unity-downloader-cli -u {{ editor.version }} -c WebGL -c Editor -c StandaloneSupport-IL2CPP --fast -w + - curl -s https://artifactory.prd.it.unity3d.com/artifactory/unity-tools/utr-standalone/utr --output utr + - chmod +x ./utr + - ./utr --suite=playmode --platform=WebGL --editor-location=.Editor --testproject=WebRTC~ --artifacts_path=build/test-results --timeout=5400 {% endfor %} {% for target in test_targets %} diff --git a/Runtime/Plugins/WebGL.meta b/Runtime/Plugins/WebGL.meta new file mode 100644 index 0000000000..ff2accf012 --- /dev/null +++ b/Runtime/Plugins/WebGL.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2f07c78bad0e2744cb2802a3f74e4f63 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/AudioStreamTrack.jslib b/Runtime/Plugins/WebGL/AudioStreamTrack.jslib new file mode 100644 index 0000000000..039b840099 --- /dev/null +++ b/Runtime/Plugins/WebGL/AudioStreamTrack.jslib @@ -0,0 +1,19 @@ +var UnityWebRTCAudioStreamTrack = { + + // To be implemented + CreateAudioTrack: function (labelPtr, sourcePtr) { + if (!uwcom_audioContext) { + uwcom_audioContext = new AudioContext; + } + var dest = uwcom_audioContext.createMediaStreamDestination(); + var audioTrack = dest.stream.getAudioTracks()[0]; + uwcom_addManageObj(audioTrack); + audioTrack.guid = UTF8ToString(labelPtr); + return audioTrack.managePtr; + }, + + ProcessAudio: function (data, size) { + // TODO + } +}; +mergeInto(LibraryManager.library, UnityWebRTCAudioStreamTrack); diff --git a/Runtime/Plugins/WebGL/AudioStreamTrack.jslib.meta b/Runtime/Plugins/WebGL/AudioStreamTrack.jslib.meta new file mode 100644 index 0000000000..0849e3ddd5 --- /dev/null +++ b/Runtime/Plugins/WebGL/AudioStreamTrack.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 5594afffd631a0149986ea6854cb8379 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/Common.jslib b/Runtime/Plugins/WebGL/Common.jslib new file mode 100644 index 0000000000..348c1ff70e --- /dev/null +++ b/Runtime/Plugins/WebGL/Common.jslib @@ -0,0 +1,211 @@ +var UnityWebRTCCommon = { + $uwcom_logLevel: 0, + $uwcom_managePtr: 0, + $uwcom_localAudioTracks: {}, + $uwcom_localVideoTracks: {}, + $uwcom_remoteAudioTracks: {}, + $uwcom_remoteVideoTracks: {}, + $uwcom_audioContext: null, + + $uwcom_addManageObj: function (obj) { + if (!obj.managePtr) { + uwcom_managePtr++; + obj.managePtr = uwcom_managePtr; + UWManaged[obj.managePtr] = obj; + } + else if(!UWManaged[obj.managePtr]){ + UWManaged[obj.managePtr] = obj; + } + }, + $uwcom_strToPtr: function (str) { + var len = lengthBytesUTF8(str) + 1; + var ptr = _malloc(len); + stringToUTF8(str, ptr, len); + return ptr; + }, + $uwcom_arrayToReturnPtr: function (arr, type) { + var buf = (new type(arr)).buffer; + var ui8a = new Uint8Array(buf); + var ptr = _malloc(ui8a.byteLength + 4); + HEAP32.set([arr.length], ptr >> 2); + HEAPU8.set(ui8a, ptr + 4); + setTimeout(function () { + _free(ptr); + }, 0); + return ptr; + }, + $uwcom_errorNo: function (err) { + var errNo = UWRTCErrorType.indexOf(err.name); + if (errNo === -1) + errNo = 0; + return errNo; + }, + $uwcom_fixStatEnumValue: function (stat) { + if (stat.type === 'codec') { + if (stat.codecType) { + stat.codecType = UWRTCCodecType.indexOf(stat.codecType); + if (stat.codecType === -1) return false; + } + } + if (stat.type === 'outbound-rtp') { + if (stat.qualityLimitationReason) { + stat.qualityLimitationReason = UWRTCQualityLimitationReason.indexOf(stat.qualityLimitationReason); + if (stat.qualityLimitationReason === -1) return false; + } + if (stat.priority) { + stat.priority = UWRTCPriorityType.indexOf(stat.priority); + if (stat.priority === -1) return false; + } + } + if (stat.type === 'media-source') { + if (stat.kind) { + stat.kind = UWMediaStreamTrackKind.indexOf(stat.kind); + if (stat.kind === -1) return false; + } + } + if (stat.type === 'data-channel') { + if (stat.state) { + stat.state = UWRTCDataChannelState.indexOf(stat.state); + if (stat.state === -1) return false; + } + } + if (stat.type === 'transport') { + if (stat.iceRole) { + stat.iceRole = UWRTCIceRole.indexOf(stat.iceRole); + if (stat.iceRole === -1) return false; + } + if (stat.dtlsState) { + stat.dtlsState = UWRTCDtlsTransportState.indexOf(stat.dtlsState); + if (stat.dtlsState === -1) return false; + } + if (stat.iceState) { + stat.iceState = UWRTCIceTransportState.indexOf(stat.iceState); + if (stat.iceState === -1) return false; + } + } + if (stat.type === 'local-candidate' + || stat.type === 'remote-candidate') { + if (stat.candidateType) { + stat.candidateType = UWRTCIceCandidateType.indexOf(stat.candidateType); + if (stat.candidateType === -1) return false; + } + } + if (stat.type === 'candidate-pair') { + if (stat.state) { + stat.state = UWRTCStatsIceCandidatePairState.indexOf(stat.state); + if (stat.state === -1) return false; + } + } + stat.type = UWRTCStatsType.indexOf(stat.type); + return true; + }, + $uwcom_statsSerialize: function (stats) { + var statsJsons = []; + stats.forEach((function(stat) { + if (uwcom_fixStatEnumValue(stat)) statsJsons.push(stat); + })); + var statsDataJson = JSON.stringify(statsJsons); + var statsDataJsonPtr = uwcom_strToPtr(statsDataJson); + return statsDataJsonPtr; + }, + $uwcom_existsCheck: function (ptr, funcName, typeName) { + var obj = UWManaged[ptr]; + if (obj) return true; + console.error("[jslib] " + funcName + ": Unmanaged " + typeName + ". Ptr: " + ptr); + return false; + }, + $uwcom_getIdx: function (enum_, val) { + enum_.indexOf() + }, + $uwcom_debugLog: function (level, fileName, member, msg) { + if (!level) return; + var logLevels = ['', '', '', '', '', '', '', 'error', 'warning', 'log', 'verbose']; + var levelNo = logLevels.indexOf(level); + if (levelNo === -1) return; + if ((uwcom_logLevel > 0 && uwcom_logLevel <= 3 && levelNo > 0 && (levelNo - 6) <= uwcom_logLevel) || + (uwcom_logLevel > 6 && uwcom_logLevel <= 9 && levelNo > 6 && levelNo <= uwcom_logLevel)) { + msg = '[JSLIB] ' + fileName + ' : ' + member + ' : ' + msg; + var msgPtr = uwcom_strToPtr(msg); + // shift level number so 9(log) => 1(NativeLoggingSeverity.Info) + Module.dynCall_vii(uwevt_DebugLog, msgPtr, Math.min(logLevels.indexOf('log') - levelNo + 1,4)); + _free(msgPtr); + } + }, + + $UWManaged: {}, + + $uwevt_DebugLog: null, + $uwevt_PCOnIceCandidate: null, + $uwevt_PCOnIceConnectionChange: null, + $uwevt_PCOnConnectionStateChange: null, + $uwevt_PCOnIceGatheringChange: null, + $uwevt_PCOnNegotiationNeeded: null, + $uwevt_PCOnDataChannel: null, + $uwevt_PCOnTrack: null, + $uwevt_PCOnRemoveTrack: null, + $uwevt_MSOnAddTrack: null, + $uwevt_MSOnRemoveTrack: null, + $uwevt_DCOnTextMessage: null, + $uwevt_DCOnBinaryMessage: null, + $uwevt_DCOnOpen: null, + $uwevt_DCOnClose: null, + $uwevt_DCOnError: null, + $uwevt_OnSetSessionDescSuccess: null, + $uwevt_OnSetSessionDescFailure: null, + $uwevt_OnSuccessCreateSessionDesc: null, + $uwevt_OnFailureCreateSessionDesc: null, + $uwevt_OnStatsDeliveredCallback: null, + + RegisterDebugLog: function (debugLogPtr,enableNativeLog,nativeLoggingSeverity) { + var logLevels = ['', '', '', '', '', '', '', 'error', 'warning', 'log', 'verbose']; + // shift level number so 1(NativeLoggingSeverity.Info) => 9(log) + uwcom_logLevel = logLevels.indexOf('log') - nativeLoggingSeverity + 1; + uwevt_DebugLog = debugLogPtr; + }, + + StatsGetJson: function (statsPtr) { + throw new Error("Not implemented"); + }, + +}; +autoAddDeps(UnityWebRTCCommon, '$uwcom_logLevel'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_debugLog'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_managePtr'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_localAudioTracks'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_localVideoTracks'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_remoteAudioTracks'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_remoteVideoTracks'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_audioContext'); +autoAddDeps(UnityWebRTCCommon, '$UWManaged'); + +autoAddDeps(UnityWebRTCCommon, '$uwevt_DebugLog'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnIceCandidate'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnIceConnectionChange'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnConnectionStateChange'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnIceGatheringChange'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnNegotiationNeeded'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnDataChannel'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnTrack'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_PCOnRemoveTrack'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_MSOnAddTrack'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_MSOnRemoveTrack'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_DCOnTextMessage'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_DCOnBinaryMessage'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_DCOnOpen'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_DCOnClose'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_DCOnError'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_OnSetSessionDescSuccess'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_OnSetSessionDescFailure'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_OnSuccessCreateSessionDesc'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_OnFailureCreateSessionDesc'); +autoAddDeps(UnityWebRTCCommon, '$uwevt_OnStatsDeliveredCallback'); + +autoAddDeps(UnityWebRTCCommon, '$uwcom_addManageObj'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_strToPtr'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_arrayToReturnPtr'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_errorNo'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_fixStatEnumValue'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_statsSerialize'); +autoAddDeps(UnityWebRTCCommon, '$uwcom_existsCheck'); + +mergeInto(LibraryManager.library, UnityWebRTCCommon); diff --git a/Runtime/Plugins/WebGL/Common.jslib.meta b/Runtime/Plugins/WebGL/Common.jslib.meta new file mode 100644 index 0000000000..9ab0de82d9 --- /dev/null +++ b/Runtime/Plugins/WebGL/Common.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 03acbb047b5b38f498dd4b9435f8a9b2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/Context.jslib b/Runtime/Plugins/WebGL/Context.jslib new file mode 100644 index 0000000000..7621733e62 --- /dev/null +++ b/Runtime/Plugins/WebGL/Context.jslib @@ -0,0 +1,286 @@ +var UnityWebRTCContext = { + GetHardwareEncoderSupport: function () { + return true; + }, + + ContextCreate__deps: ['$UWEncoderType'], + ContextCreate: function (uid, encodeType) { + var context = { + id: uid, + encodeType: UWEncoderType[encodeType] + }; + uwcom_addManageObj(context); + return context.managePtr; + }, + + ContextDestroy: function (uid) { + var contextPtrs = Object.keys(UWManaged).filter(function (contextPtr) { + if ('id' in UWManaged[contextPtr]) { + return UWManaged[contextPtr].id === uid; + } else + return false; + }); + if (contextPtrs.length > 1) { + console.error('ContextDestroy: multiple Contexts with the same id'); + } else if (!contextPtrs.length) { + console.error('ContextDestroy: There is no context with id = ' + uid.toString()); + } + contextPtrs.forEach(function (contextPtr) { + delete UWManaged[contextPtr]; + }); + }, + + SetCurrentContext: function (contextPtr) { + throw new Error("Not implemented"); + }, + + // TODO + ContextAddRefPtr: function (ptr){ + throw new Error("Not implemented"); + }, + + // TODO + ContextDeleteRefPtr: function(ptr){ + throw new Error("Not implemented"); + }, + + // TODO + ContextCreateAudioTrackSource: function(contextPtr){ + if (!uwcom_existsCheck(contextPtr, "ContextCreateAudioTrackSource", "context")) return; + const audioTrackSource = {}; + uwcom_addManageObj(audioTrackSource); + return audioTrackSource.managePtr; + }, + + // TODO + ContextCreateVideoTrackSource: function(contextPtr){ + if (!uwcom_existsCheck(contextPtr, "ContextCreateVideoTrackSource", "context")) return; + const videoTrackSource = {}; + uwcom_addManageObj(videoTrackSource); + return videoTrackSource.managePtr; + }, + + ContextGetEncoderType: function (contextPtr) { + if (!uwcom_existsCheck(contextPtr, 'ContextGetEncoderType', 'context')) return; + var context = UWManaged[contextPtr]; + var encodeTypeIdx = UWEncoderType.indexOf(context.encodeType); + return encodeTypeIdx; + }, + + ContextCreatePeerConnection: function (contextPtr, conf) { + if (!uwcom_existsCheck(contextPtr, 'ContextCreatePeerConnection', 'context')) return; + return _CreatePeerConnection(conf); + }, + + ContextCreatePeerConnectionWithConfig: function (contextPtr, confPtr) { + if (!uwcom_existsCheck(contextPtr, 'ContextCreatePeerConnectionWithConfig', 'context')) return; + return _CreatePeerConnectionWithConfig(confPtr); + }, + + ContextDeletePeerConnection: function (contextPtr, peerPtr) { + //if (!uwcom_existsCheck(contextPtr, 'ContextDeletePeerConnection', 'context')) return; + if (!uwcom_existsCheck(peerPtr, 'ContextDeletePeerConnection', 'peer')) return; + var peer = UWManaged[peerPtr]; + if (peer.readyState !== 'closed' || peer.signalingState !== 'closed') + peer.close(); + delete UWManaged[peerPtr]; + }, + + PeerConnectionSetLocalDescription: function (contextPtr, peerPtr, typeIdx, sdpPtr) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionSetLocalDescription', 'context')) return 11; // OperationErrorWithData + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSetLocalDescription', 'peer')) return 11; // OperationErrorWithData + return _PeerConnectionSetDescription(peerPtr, typeIdx, sdpPtr, 'Local'); + }, + + PeerConnectionSetRemoteDescription: function (contextPtr, peerPtr, typeIdx, sdpPtr) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionSetRemoteDescription', 'context')) return 11; // OperationErrorWithData + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSetRemoteDescription', 'peer')) return 11; // OperationErrorWithData + return _PeerConnectionSetDescription(peerPtr, typeIdx, sdpPtr, 'Remote'); + }, + + PeerConnectionSetLocalDescriptionWithoutDescription: function (contextPtr, peerPtr) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionSetLocalDescriptionWithoutDescription', 'context')) return 11; // OperationErrorWithData + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSetLocalDescriptionWithoutDescription', 'peer')) return 11; // OperationErrorWithData + return _PeerConnectionSetDescriptionWithoutDescription(peerPtr); + }, + + PeerConnectionRegisterOnSetSessionDescSuccess: function (contextPtr, peerPtr, OnSetSessionDescSuccess) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionRegisterOnSetSessionDescSuccess', 'context')) return; + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterOnSetSessionDescSuccess', 'peer')) return; + uwevt_OnSetSessionDescSuccess = OnSetSessionDescSuccess; + }, + + PeerConnectionRegisterOnSetSessionDescFailure: function (contextPtr, peerPtr, OnSetSessionDescFailure) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionRegisterOnSetSessionDescFailure', 'context')) return; + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterOnSetSessionDescFailure', 'peer')) return; + uwevt_OnSetSessionDescFailure = OnSetSessionDescFailure; + }, + + ContextCreateDataChannel: function (contextPtr, peerPtr, labelPtr, optionsJsonPtr) { + if (!uwcom_existsCheck(contextPtr, 'ContextCreateDataChannel', 'context')) return; + if (!uwcom_existsCheck(peerPtr, 'ContextCreateDataChannel', 'peer')) return; + return _CreateDataChannel(peerPtr, labelPtr, optionsJsonPtr); + }, + + ContextDeleteDataChannel: function (contextPtr, dataChannelPtr) { + //if (!uwcom_existsCheck(contextPtr, 'ContextDeleteDataChannel', 'context')) return; + if (!uwcom_existsCheck(dataChannelPtr, 'ContextDeleteDataChannel', 'dataChannel')) return; + delete UWManaged[dataChannelPtr]; + }, + + ContextCreateMediaStream: function (contextPtr, labelPtr) { + if (!uwcom_existsCheck(contextPtr, 'ContextCreateMediaStream', 'context')) return; + return _CreateMediaStream(labelPtr); + }, + + ContextDeleteMediaStream: function (contextPtr, streamPtr) { + //if (!uwcom_existsCheck(contextPtr, 'ContextDeleteMediaStream', 'context')) return; + if (!uwcom_existsCheck(streamPtr, 'ContextDeleteMediaStream', 'stream')) return; + _DeleteMediaStream(streamPtr); + }, + + ContextRegisterMediaStreamObserver: function (contextPtr, streamPtr) { + if (!uwcom_existsCheck(contextPtr, 'MediaStreamRegisterOnAddTrack', 'context')) return; + if (!uwcom_existsCheck(streamPtr, 'MediaStreamRegisterOnAddTrack', 'stream')) return; + + var stream = UWManaged[streamPtr]; + stream.onaddtrack = (function(evt) { + uwcom_addManageObj(evt.track); + Module.dynCall_vii(uwevt_MSOnAddTrack, stream.managePtr, evt.track.managePtr); + }); + stream.onremovetrack = (function(evt) { + if (!uwcom_existsCheck(evt.track.managePtr, "stream.onremovetrack", "track")) return; + Module.dynCall_vii(uwevt_MSOnRemoveTrack, stream.managePtr, evt.track.managePtr); + }); + }, + + ContextUnRegisterMediaStreamObserver: function (contextPtr, streamPtr) { + + }, + + MediaStreamRegisterOnAddTrack: function (contextPtr, streamPtr, MediaStreamOnAddTrack) { + if (!uwcom_existsCheck(contextPtr, 'MediaStreamRegisterOnAddTrack', 'context')) return; + if (!uwcom_existsCheck(streamPtr, 'MediaStreamRegisterOnAddTrack', 'stream')) return; + uwevt_MSOnAddTrack = MediaStreamOnAddTrack; + }, + + MediaStreamRegisterOnRemoveTrack: function (contextPtr, streamPtr, MediaStreamOnRemoveTrack) { + if (!uwcom_existsCheck(contextPtr, 'MediaStreamRegisterOnRemoveTrack', 'context')) return; + if (!uwcom_existsCheck(streamPtr, 'MediaStreamRegisterOnRemoveTrack', 'stream')) return; + uwevt_MSOnRemoveTrack = MediaStreamOnRemoveTrack; + }, + + GetBatchUpdateEventFunc: function (contextPtr) { + throw new Error("Not implemented"); + return null; + }, + + GetBatchUpdateEventID: function () { + throw new Error("Not implemented"); + return -1; + }, + + AudioTrackAddSink: function(track, sink) { + throw new Error("Not implemented"); + }, + + AudioTrackRemoveSink: function(track, sink) { + throw new Error("Not implemented"); + }, + + AudioTrackSinkProcessAudio: function(sink, data, length, channels, sampleRate) { + throw new Error("Not implemented"); + }, + + GetUpdateTextureFunc: function (contextPtr) { + throw new Error("Not implemented"); + }, + + ContextCreateAudioTrack: function (contextPtr, labelPtr, sourcePtr) { + if (!uwcom_existsCheck(contextPtr, 'ContextCreateAudioTrack', 'context')) return; + return _CreateAudioTrack(labelPtr, sourcePtr); + }, + + ContextCreateVideoTrack: function (contextPtr, srcTexturePtr, dstTexturePtr, width, height) { + if (!uwcom_existsCheck(contextPtr, 'ContextCreateVideoTrack', 'context')) return; + return _CreateVideoTrack(srcTexturePtr, dstTexturePtr, width, height); + }, + + ContextStopMediaStreamTrack: function (contextPtr, trackPtr) { + if (!uwcom_existsCheck(contextPtr, 'ContextStopMediaStreamTrack', 'context')) return; + if (!uwcom_existsCheck(trackPtr, 'ContextStopMediaStreamTrack', 'track')) return; + var track = UWManaged[trackPtr]; + track.stop(); + }, + + ContextDeleteMediaStreamTrack: function (contextPtr, trackPtr) { + if (!uwcom_existsCheck(trackPtr, 'ContextDeleteMediaStreamTrack', 'track')) return; + var track = UWManaged[trackPtr]; + + // Not sure how js garbage collection works, remove/disable/stop all attributes inside the track object? + if(track.kind === "video"){ + if(uwcom_localVideoTracks[trackPtr]){ + delete uwcom_localVideoTracks[trackPtr]; + } + + if(uwcom_remoteVideoTracks[trackPtr]){ + uwcom_remoteVideoTracks[trackPtr].video.remove(); + uwcom_remoteVideoTracks[trackPtr].track.stop(); + delete uwcom_remoteVideoTracks[trackPtr]; + } + } + delete UWManaged[trackPtr]; + }, + + ContextRegisterAudioReceiveCallback: function (contextPtr, trackPtr, AudioTrackOnReceive){ + + }, + + ContextUnregisterAudioReceiveCallback: function (contextPtr, trackPtr){ + + }, + + // CreateVideoRenderer: function(contextPtr) { + + // }, + + // DeleteVideoRenderer: function(contextPtr, sinkPtr) { + + // }, + + ContextDeleteStatsReport: function (contextPtr, reportPtr) { + //if (!uwcom_existsCheck(contextPtr, 'ContextDeleteStatsReport', 'context')) return; + if (!uwcom_existsCheck(reportPtr, 'ContextDeleteStatsReport', 'report')) return; + delete UWManaged[reportPtr]; + }, + + // ContextSetVideoEncoderParameter: function(trackPtr, width, height, format, texturePtr) { + + // }, + + // GetInitializationResult: function(contextPtr, trackPtr) { + + // }, + + $UWContextGetCapabilities: function (senderReceiver, kindIdx) { + var kind = UWMediaStreamTrackKind[kindIdx]; + var capabilities = {codecs:[], headerExtensions: []}; + const supportsSetCodecPreferences = window.RTCRtpTransceiver && 'setCodecPreferences' in window.RTCRtpTransceiver.prototype; + if(supportsSetCodecPreferences) capabilities = senderReceiver.getCapabilities(kind); + var capabilitiesJson = JSON.stringify(capabilities); + var capabilitiesJsonPtr = uwcom_strToPtr(capabilitiesJson); + return capabilitiesJsonPtr; + }, + + ContextGetSenderCapabilities: function (contextPtr, kindIdx) { + if (!uwcom_existsCheck(contextPtr, 'ContextGetSenderCapabilities', 'context')) return; + return UWContextGetCapabilities(RTCRtpSender, kindIdx); + }, + + ContextGetReceiverCapabilities: function (contextPtr, kindIdx) { + if (!uwcom_existsCheck(contextPtr, 'ContextGetReceiverCapabilities', 'context')) return; + return UWContextGetCapabilities(RTCRtpReceiver, kindIdx); + } +}; +autoAddDeps(UnityWebRTCContext, '$UWContextGetCapabilities'); +mergeInto(LibraryManager.library, UnityWebRTCContext); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/Context.jslib.meta b/Runtime/Plugins/WebGL/Context.jslib.meta new file mode 100644 index 0000000000..3c1c80389d --- /dev/null +++ b/Runtime/Plugins/WebGL/Context.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: c48860db3eea72b4ea1296b43edd93dc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/Enum.jslib b/Runtime/Plugins/WebGL/Enum.jslib new file mode 100644 index 0000000000..d385dbab03 --- /dev/null +++ b/Runtime/Plugins/WebGL/Enum.jslib @@ -0,0 +1,261 @@ +var UnityWebRTCEnum = { + // WebRTC + $UWRTCSdpSemantics: [ // Not defined in the API + 'plan-b', + 'unified-plan' + ], + $UWRTCIceCredentialType: [ + 'password', + 'oauth' + ], + $UWRTCIceTransportPolicy: [ + '', + 'relay', + '', + 'all' + ], + $UWRTCBundlePolicy: [ + 'balanced', + 'max-compat', + 'max-bundle' + ], + $UWRTCRtcpMuxPolicy: [ + 'require' + ], + $UWRTCSignalingState: [ + 'stable', + 'have-local-offer', + 'have-local-pranswer', + 'have-remote-offer', + 'have-remote-pranswer', + 'closed' + ], + $UWRTCIceGatheringState: [ + 'new', + 'gathering', + 'complete' + ], + $UWRTCPeerConnectionState: [ + 'new', + 'connecting', + 'connected', + 'disconnected', + 'failed', + 'closed' + ], + $UWRTCIceConnectionState: [ + 'new', + 'checking', + 'connected', + 'completed', + 'failed', + 'disconnected', + 'closed', + 'max' + ], + $UWRTCSdpType: [ + 'offer', + 'pranswer', + 'answer', + 'rollback' + ], + $UWRTCIceProtocol: [ + 'udp', + 'tcp' + ], + $UWRTCIceTcpCandidateType: [ + 'active', + 'passive', + 'so' + ], + $UWRTCIceCandidateType: [ + 'host', + 'srflx', + 'prflx', + 'relay' + ], + $UWRTCRtpTransceiverDirection: [ + 'sendrecv', + 'sendonly', + 'recvonly', + 'inactive', + 'stopped' + ], + $UWRTCDtlsTransportState: [ + 'new', + 'connecting', + 'connected', + 'closed', + 'failed' + ], + $UWRTCIceGathererState: [ + 'new', + 'gathering', + 'complete' + ], + $UWRTCIceTransportState: [ + 'new', + 'checking', + 'connected', + 'completed', + 'disconnected', + 'failed', + 'closed' + ], + $UWRTCIceRole: [ + 'unknown', + 'controlling', + 'controlled' + ], + $UWRTCIceComponent: [ + 'none', + 'rtp', + 'rtcp' + ], + $UWRTCSctpTransportState: [ + 'connecting', + 'connected', + 'closed' + ], + $UWRTCDataChannelState: [ + 'connecting', + 'open', + 'closing', + 'closed' + ], + $UWRTCErrorDetailType: [ + 'data-channel-failure', + 'dtls-failure', + 'fingerprint-failure', + 'hardware-encoder-error', + 'hardware-encoder-not-available', + 'sdp-syntax-error', + 'sctp-failure' + ], + // Media Capture and Streams + $UWMediaStreamTrackState: [ + 'live', + 'ended' + ], + $UWVideoFacingModeEnum: [ + 'user', + 'environment', + 'left', + 'right' + ], + $UWVideoResizeModeEnum: [ + 'none', + 'crop-and-scale' + ], + $UWMediaStreamTrackKind: [ // Not defined in the API + 'audio', + 'video' + ], + $UWMediaDeviceKind: [ + 'audioinput', + 'audiooutput', + 'videoinput' + ], + // Identifiers for WebRTC's Statistics + $UWRTCStatsType: [ + 'codec', + 'inbound-rtp', + 'outbound-rtp', + 'remote-inbound-rtp', + 'remote-outbound-rtp', + 'media-source', + 'csrc', + 'peer-connection', + 'data-channel', + 'stream', + 'track', + 'transceiver', + 'sender', + 'receiver', + 'transport', + 'sctp-transport', + 'candidate-pair', + 'local-candidate', + 'remote-candidate', + 'certificate', + 'ice-server' + ], + $UWRTCCodecType: [ + 'encode', + 'decode' + ], + $UWRTCQualityLimitationReason: [ + 'none', + 'cpu', + 'bandwidth', + 'other' + ], + $UWRTCStatsIceCandidatePairState: [ + 'frozen', + 'waiting', + 'in-progress', + 'failed', + 'succeeded' + ], + // WebRTC Priority Control + $UWRTCPriorityType: [ + 'very-low', + 'low', + 'medium', + 'high' + ], + // Unity WebRTC + $UWEncoderType: [ + 'software', + 'hardware' + ], + $UWRTCErrorType: [ + 'None', + 'UnsupportedOperation', + 'UnsupportedParameter', + 'InvalidParameter', + 'InvalidRange', + 'SyntaxError', + 'InvalidState', + 'InvalidModification', + 'NetworkError', + 'ResourceExhausted', + 'InternalError', + 'OperationErrorWithData' + ] +} +autoAddDeps(UnityWebRTCEnum, '$UWRTCSdpSemantics'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceCredentialType'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceTransportPolicy'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCBundlePolicy'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCRtcpMuxPolicy'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCSignalingState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceGatheringState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCPeerConnectionState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceConnectionState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCSdpType'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceProtocol'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceTcpCandidateType'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceCandidateType'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCRtpTransceiverDirection'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCDtlsTransportState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceGathererState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceTransportState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceRole'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCIceComponent'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCSctpTransportState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCDataChannelState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCErrorDetailType'); +autoAddDeps(UnityWebRTCEnum, '$UWMediaStreamTrackState'); +autoAddDeps(UnityWebRTCEnum, '$UWVideoFacingModeEnum'); +autoAddDeps(UnityWebRTCEnum, '$UWVideoResizeModeEnum'); +autoAddDeps(UnityWebRTCEnum, '$UWMediaStreamTrackKind'); +autoAddDeps(UnityWebRTCEnum, '$UWMediaDeviceKind'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCStatsType'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCCodecType'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCQualityLimitationReason'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCStatsIceCandidatePairState'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCPriorityType'); +autoAddDeps(UnityWebRTCEnum, '$UWEncoderType'); +autoAddDeps(UnityWebRTCEnum, '$UWRTCErrorType'); +mergeInto(LibraryManager.library, UnityWebRTCEnum); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/Enum.jslib.meta b/Runtime/Plugins/WebGL/Enum.jslib.meta new file mode 100644 index 0000000000..ac44328133 --- /dev/null +++ b/Runtime/Plugins/WebGL/Enum.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 07f342ccd20fa214c9e2477f5dc681c3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/MediaStream.jslib b/Runtime/Plugins/WebGL/MediaStream.jslib new file mode 100644 index 0000000000..0b44e153a3 --- /dev/null +++ b/Runtime/Plugins/WebGL/MediaStream.jslib @@ -0,0 +1,149 @@ +var UnityWebRTCMediaStream = { + + // Note: MediaStream creates a read-only id field, so we cannot set it. + // Using custom 'guid' field instead. + CreateMediaStream: function (labelPtr) { + var label = UTF8ToString(labelPtr); + var stream = new MediaStream(); + stream.guid = label; + stream.onaddtrack = function (evt) { + uwcom_addManageObj(evt.track); + console.log('stream.ontrack' + evt.track.managePtr); + Module.dynCall_vii(uwevt_MSOnAddTrack, this.managePtr, evt.track.managePtr); + }; + stream.onremovetrack = function (evt) { + if (!evt.track.managePtr) { + console.warn('track does not own managePtr'); + return; + } + if (!uwcom_existsCheck(evt.track.managePtr, 'stream.onremovetrack', 'track')) return; + Module.dynCall_vii(uwevt_MSOnRemoveTrack, this.managePtr, evt.track.managePtr); + }; + + uwcom_addManageObj(stream); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'CreateMediaStream', stream.managePtr); + return stream.managePtr; + }, + + MediaStreamAddUserMedia: function (streamPtr, constraints){ + if (!uwcom_existsCheck(streamPtr, 'MediaStreamAddUserMedia', 'stream')) return; + uwcom_debugLog('log', 'MediaStream.jslib', 'AddUserMedia', streamPtr); + + var stream = UWManaged[streamPtr]; + var optionsJson = UTF8ToString(constraints); + var options = JSON.parse(optionsJson); + + navigator.mediaDevices.getUserMedia(options) + .then(function(usermedia){ + usermedia.getTracks().forEach(function(track){ + uwcom_addManageObj(track); + _MediaStreamAddTrack(stream.managePtr, track.managePtr); + }) + }) + .catch(function(err) { + console.error(err); + }); + }, + + DeleteMediaStream: function(streamPtr) { + var stream = UWManaged[streamPtr]; + stream.getTracks().forEach(function(track) { + track.stop(); + stream.removeTrack(track); + track = null; + }); + stream = null; + delete UWManaged[streamPtr]; + }, + + MediaStreamGetID: function (streamPtr) { + if (!uwcom_existsCheck(streamPtr, 'MediaStreamGetID', 'stream')) return; + var stream = UWManaged[streamPtr]; + var id = stream.guid || stream.id; + var streamIdPtr = uwcom_strToPtr(id); + return streamIdPtr; + }, + + MediaStreamGetVideoTracks: function (streamPtr) { + if (!uwcom_existsCheck(streamPtr, 'MediaStreamGetVideoTracks', 'stream')) return; + var stream = UWManaged[streamPtr]; + var tracks = stream.getVideoTracks(); + var ptrs = []; + tracks.forEach(function (track) { + uwcom_addManageObj(track); + ptrs.push(track.managePtr); + }); + var ptr = uwcom_arrayToReturnPtr(ptrs, Int32Array); + return ptr; + }, + + MediaStreamGetAudioTracks: function (streamPtr) { + if (!uwcom_existsCheck(streamPtr, 'MediaStreamGetAudioTracks', 'stream')) return; + var stream = UWManaged[streamPtr]; + var tracks = stream.getAudioTracks(); + var ptrs = []; + tracks.forEach(function (track) { + uwcom_addManageObj(track); + ptrs.push(track.managePtr); + }); + var ptr = uwcom_arrayToReturnPtr(ptrs, Int32Array); + return ptr; + }, + + MediaStreamAddTrack: function (streamPtr, trackPtr) { + if (!uwcom_existsCheck(streamPtr, 'MediaStreamAddTrack', 'stream')) return; + if (!uwcom_existsCheck(trackPtr, 'MediaStreamAddTrack', 'track')) return; + var stream = UWManaged[streamPtr]; + var track = UWManaged[trackPtr]; + try { + stream.addTrack(track); + Module.dynCall_vii(uwevt_MSOnAddTrack, stream.managePtr, track.managePtr); + return true; + } catch(err){ + return false; + } + // try { + // console.log('MediaStreamAddTrack:' + streamPtr + ':' + trackPtr); + // stream.addTrack(track); + // var video = document.createElement('video'); + // video.id = 'video_' + track.managePtr.toString(); + // video.muted = true; + // //video.style.display = 'none'; + // video.srcObject = stream; + // document.body.appendChild(video); + // video.style.width = '300px'; + // video.style.height = '200px'; + // video.style.position = 'absolute'; + // video.style.left = video.style.top = 0; + // uwcom_remoteVideoTracks[track.managePtr] = { + // track: track, + // video: video, + // playing: false + // }; + // video.onplaying = function(){ + // uwcom_remoteVideoTracks[track.managePtr].playing = true; + // } + // video.play(); + // Module.dynCall_vii(uwevt_MSOnAddTrack, stream.managePtr, track.managePtr); + // return true; + // } catch (err) { + // console.log('MediaStreamAddTrack: ' + err.message); + // return false; + // } + }, + + MediaStreamRemoveTrack: function (streamPtr, trackPtr) { + if (!uwcom_existsCheck(streamPtr, 'MediaStreamRemoveTrack', 'stream')) return; + if (!uwcom_existsCheck(trackPtr, 'MediaStreamRemoveTrack', 'track')) return; + var stream = UWManaged[streamPtr]; + var track = UWManaged[trackPtr]; + try { + stream.removeTrack(track); + return true; + } catch (err) { + console.log('MediaStreamRemoveTrack: ' + err.message); + return false; + } + } +}; +mergeInto(LibraryManager.library, UnityWebRTCMediaStream); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/MediaStream.jslib.meta b/Runtime/Plugins/WebGL/MediaStream.jslib.meta new file mode 100644 index 0000000000..71357073a7 --- /dev/null +++ b/Runtime/Plugins/WebGL/MediaStream.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 257addfc1fbef704184dfac8b8d18ad9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/MediaStreamTrack.jslib b/Runtime/Plugins/WebGL/MediaStreamTrack.jslib new file mode 100644 index 0000000000..112608696c --- /dev/null +++ b/Runtime/Plugins/WebGL/MediaStreamTrack.jslib @@ -0,0 +1,34 @@ +var UnityWebRTCMediaStreamTrack = { + MediaStreamTrackGetEnabled: function (trackPtr) { + if (!uwcom_existsCheck(trackPtr, 'MediaStreamTrackGetEnabled', 'track')) return; + var track = UWManaged[trackPtr]; + return track.enabled; + }, + + MediaStreamTrackSetEnabled: function (trackPtr, enabled) { + if (!uwcom_existsCheck(trackPtr, 'MediaStreamTrackSetEnabled', 'track')) return; + var track = UWManaged[trackPtr]; + track.enabled = !!enabled; + }, + + MediaStreamTrackGetReadyState: function (trackPtr) { + if (!uwcom_existsCheck(trackPtr, 'MediaStreamTrackGetReadyState', 'track')) return; + var track = UWManaged[trackPtr]; + return UWMediaStreamTrackState.indexOf(track.readyState); + }, + + MediaStreamTrackGetKind: function (trackPtr) { + if (!uwcom_existsCheck(trackPtr, 'MediaStreamTrackGetKind', 'track')) return; + var track = UWManaged[trackPtr]; + return UWMediaStreamTrackKind.indexOf(track.kind); + }, + + MediaStreamTrackGetID: function (trackPtr) { + if (!uwcom_existsCheck(trackPtr, 'MediaStreamTrackGetID', 'track')) return; + var track = UWManaged[trackPtr]; + var id = track.guid || track.id; + var idPtr = uwcom_strToPtr(id); + return idPtr; + } +}; +mergeInto(LibraryManager.library, UnityWebRTCMediaStreamTrack); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/MediaStreamTrack.jslib.meta b/Runtime/Plugins/WebGL/MediaStreamTrack.jslib.meta new file mode 100644 index 0000000000..843b040fdc --- /dev/null +++ b/Runtime/Plugins/WebGL/MediaStreamTrack.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 00b85dc7de0a51d44b9388fb29b6cdca +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/RTCDataChannel.jslib b/Runtime/Plugins/WebGL/RTCDataChannel.jslib new file mode 100644 index 0000000000..a61f8a6a3c --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCDataChannel.jslib @@ -0,0 +1,163 @@ +var UnityWebRTCDataChannel = { + + CreateDataChannel: function(peerPtr, labelPtr, optionsJsonPtr) { + /** @type {RTCPeerConnection} */ + var peer = UWManaged[peerPtr]; + var label = UTF8ToString(labelPtr); + var optionsJson = UTF8ToString(optionsJsonPtr); + var options = JSON.parse(optionsJson); + + // Firefox doesn't like null values, so remove them. + if(options.ordered.hasValue) options.ordered = options.ordered.value; + else delete options.ordered; + if(options.maxRetransmits.hasValue) options.maxRetransmits = options.maxRetransmits.value; + else delete options.maxRetransmits; + if(options.maxRetransmitTime.hasValue) options.maxRetransmitTime = options.maxRetransmitTime.value; + else delete options.maxRetransmitTime; + if(options.negotiated.hasValue) options.negotiated = options.negotiated.value; + else delete options.negotiated; + if(options.id.hasValue) options.id = options.id.value; + else delete options.id; + + // Chrome (incorrectly?) accept maxRetransmits and maxRetransmitTime being set + if (options.maxRetransmits && options.maxRetransmitTime) return 0; + + try { + var dataChannel = peer.createDataChannel(label, options); + } + catch(err){ + console.log(err); + return 0; + } + + dataChannel.onmessage = function (evt) { + if (typeof evt.data === 'string') { + var msgPtr = uwcom_strToPtr(evt.data); + Module.dynCall_vii(uwevt_DCOnTextMessage, this.managePtr, msgPtr); + } else { + var msgPtr = uwcom_arrayToReturnPtr(evt.data, Uint8Array); + Module.dynCall_viii(uwevt_DCOnBinaryMessage, this.managePtr, msgPtr + 4, evt.data.byteLength); + } + }; + dataChannel.onopen = function (evt) { + if (!uwcom_existsCheck(this.managePtr, "onopen", "dataChannel")) return; + Module.dynCall_vi(uwevt_DCOnOpen, this.managePtr); + }; + dataChannel.onclose = function (evt) { + if (!uwcom_existsCheck(this.managePtr, "onclose", "dataChannel")) return; + Module.dynCall_vi(uwevt_DCOnClose, this.managePtr); + }; + dataChannel.onerror = function (evt) { + if (!uwcom_existsCheck(this.managePtr, "onerror", "dataChannel")) return; + Module.dynCall_vi(uwevt_DCOnError, this.managePtr); + }; + uwcom_addManageObj(dataChannel); + return dataChannel.managePtr; + }, + + DataChannelGetID: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetID', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + if(dataChannel.id === null) return -1; + return dataChannel.id; + }, + + DataChannelGetLabel: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetLabel', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + var labelPtr = uwcom_strToPtr(dataChannel.label); + return labelPtr; + }, + + DataChannelGetProtocol: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetProtocol', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + var protocolPtr = uwcom_strToPtr(dataChannel.protocol); + return protocolPtr; + }, + + DataChannelGetMaxRetransmits: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetMaxRetransmits', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + if(dataChannel.maxRetransmits === null) return -1; + return dataChannel.maxRetransmits; + }, + + DataChannelGetMaxRetransmitTime: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetMaxRetransmitTime', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + if(dataChannel.maxPacketLifeTime === null) return -1; + return dataChannel.maxPacketLifeTime; + }, + + DataChannelGetOrdered: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetOrdered', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + return dataChannel.ordered; + }, + + DataChannelGetBufferedAmount: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetBufferedAmount', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + return dataChannel.bufferedAmount; + }, + + DataChannelGetNegotiated: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetNegotiated', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + return dataChannel.negotiated; + }, + + DataChannelGetReadyState: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelGetReadyState', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + var readyStateIdx = UWRTCDataChannelState.indexOf(dataChannel.readyState); + return readyStateIdx; + }, + + DataChannelRegisterOnMessage: function (contextPtr, dataChannelPtr, DataChannelNativeOnMessage) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelRegisterOnMessage', 'dataChannel')) return; + uwevt_DCOnBinaryMessage = DataChannelNativeOnMessage; + }, + + DataChannelRegisterOnTextMessage: function (contextPtr, dataChannelPtr, DataChannelNativeOnTextMessage) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelRegisterOnTextMessage', 'dataChannel')) return; + uwevt_DCOnTextMessage = DataChannelNativeOnTextMessage; + }, + + DataChannelRegisterOnError: function (contextPtr, dataChannelPtr, DataChannelNativeOnClose) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelRegisterOnError', 'dataChannel')) return; + uwevt_DCOnError = DataChannelNativeOnClose; + }, + + DataChannelRegisterOnOpen: function (contextPtr, dataChannelPtr, DataChannelNativeOnOpen) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelRegisterOnOpen', 'dataChannel')) return; + uwevt_DCOnOpen = DataChannelNativeOnOpen; + }, + + DataChannelRegisterOnClose: function (contextPtr, dataChannelPtr, DataChannelNativeOnClose) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelRegisterOnClose', 'dataChannel')) return; + uwevt_DCOnClose = DataChannelNativeOnClose; + }, + + DataChannelSend: function (dataChannelPtr, textMsgPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelSend', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + var textMsg = UTF8ToString(textMsgPtr); + dataChannel.send(textMsg); + }, + + DataChannelSendBinary: function (dataChannelPtr, binaryMsgPtr, size) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelSendBinary', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + var binaryMsg = HEAPU8.subarray(binaryMsgPtr, binaryMsgPtr + size); + dataChannel.send(binaryMsg); + }, + + DataChannelClose: function (dataChannelPtr) { + if (!uwcom_existsCheck(dataChannelPtr, 'DataChannelClose', 'dataChannel')) return; + var dataChannel = UWManaged[dataChannelPtr]; + dataChannel.close(); + } +}; +mergeInto(LibraryManager.library, UnityWebRTCDataChannel); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/RTCDataChannel.jslib.meta b/Runtime/Plugins/WebGL/RTCDataChannel.jslib.meta new file mode 100644 index 0000000000..2e1edfc67f --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCDataChannel.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 32308ed992b14bf449be4e88101b706e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/RTCIceCandidate.jslib b/Runtime/Plugins/WebGL/RTCIceCandidate.jslib new file mode 100644 index 0000000000..dbea81411d --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCIceCandidate.jslib @@ -0,0 +1,57 @@ +var UnityWebRTCIceCandidate = { + CreateNativeRTCIceCandidate: function (candPtr, sdpMidPtr, sdpMLineIndex) { + var cand = UTF8ToString(candPtr); + var sdpMid = UTF8ToString(sdpMidPtr); + var candidate = new RTCIceCandidate({ + candidate: cand, + sdpMid: sdpMid, + sdpMLineIndex: sdpMLineIndex + }); + uwcom_addManageObj(candidate); + return candidate.managePtr; + }, + + IceCandidateGetCandidate: function (candPtr){ + if (!uwcom_existsCheck(candPtr, "IceCandidateGetCandidate", "iceCandidate")) return; + var candidate = UWManaged[candPtr]; + var ret = {}; + ret.candidate = candidate.candidate; + ret.component = UWRTCIceComponent.indexOf(candidate.component); + ret.foundation = candidate.foundation; + ret.ip = candidate.ip; + ret.port = candidate.port; + ret.priority = candidate.priority; + ret.address = candidate.address; + ret.protocol = candidate.protocol; + ret.relatedAddress = candidate.relatedAddress; // TODO: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/relatedAddress - Is null for host candidates + ret.sdpMid = candidate.sdpMid; + ret.sdpMLineIndex = candidate.sdpMLineIndex; + ret.tcpType = candidate.tcpType; + ret.type = candidate.type; + ret.usernameFragment = candidate.usernameFragment; //TODO: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/usernameFragment - This can be null? + var json = JSON.stringify(ret); + return uwcom_strToPtr(json); + }, + + IceCandidateGetSdp: function(candidatePtr){ + if (!uwcom_existsCheck(candidatePtr, "IceCandidateGetSdp", "iceCandidate")) return; + candidate = UWManaged[candidatePtr]; + return uwcom_strToPtr(candidate.candidate); + }, + + IceCandidateGetSdpMid: function(candidatePtr){ + if (!uwcom_existsCheck(candidatePtr, "IceCandidateGetSdpMid", "iceCandidate")) return; + return uwcom_strToPtr(candidate.sdpMid); + }, + + IceCandidateGetSdpLineIndex: function(candidatePtr){ + if (!uwcom_existsCheck(candidatePtr, "IceCandidateGetSdpMid", "iceCandidate")) return; + return candidate.sdpMLineIndex; + }, + + DeleteIceCandidate: function (candidatePtr) { + if (!uwcom_existsCheck(candidatePtr, 'DeleteIceCandidate', 'iceCandidate')) return; + delete UWManaged[candidatePtr]; + } +}; +mergeInto(LibraryManager.library, UnityWebRTCIceCandidate); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/RTCIceCandidate.jslib.meta b/Runtime/Plugins/WebGL/RTCIceCandidate.jslib.meta new file mode 100644 index 0000000000..c2006b4cde --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCIceCandidate.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: f295fbc53955655439b7db3ed0018597 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/RTCPeerConnection.jslib b/Runtime/Plugins/WebGL/RTCPeerConnection.jslib new file mode 100644 index 0000000000..658884a60a --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCPeerConnection.jslib @@ -0,0 +1,695 @@ +var UnityWebRTCPeerConnection = { + CreatePeerConnection: function (conf) { + var label = ''; + if (conf) { + label = conf.label; + delete conf.label; + } + + //debugger; + conf = conf || {}; + + try { + var peer = new RTCPeerConnection(conf); + } catch (err) { + return 0; + } + peer.label = label; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'CreatePeerConnection', 'create peer: ' + peer.label); + peer.onicecandidate = function (evt) { + var cnd = evt.candidate; + // TODO evt.candidate === null (candidate collect end) + if (cnd) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'onicecandidate', JSON.stringify(evt.candidate.toJSON())); + uwcom_addManageObj(cnd); + var candidatePtr = uwcom_strToPtr(cnd.candidate); + var sdpMidPtr = uwcom_strToPtr(cnd.sdpMid); + Module.dynCall_viiiii(uwevt_PCOnIceCandidate, peer.managePtr, cnd.managePtr, candidatePtr, sdpMidPtr, cnd.sdpMLineIndex); + } + }; + peer.oniceconnectionstatechange = function (evt) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'oniceconnectionstatechange', this.label + ':' + this.iceConnectionState); + var idx = UWRTCIceConnectionState.indexOf(this.iceConnectionState); + if (idx === -1) { + console.error('unknown iceConnectionState: "' + this.iceConnectionState + '"'); + } + Module.dynCall_vii(uwevt_PCOnIceConnectionChange, peer.managePtr, idx); + }; + peer.onconnectionstatechange = function (evt) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'onconnectionstatechange', this.label + ':' + this.connectionState); + var idx = UWRTCPeerConnectionState.indexOf(this.connectionState); + Module.dynCall_vii(uwevt_PCOnConnectionStateChange, peer.managePtr, idx); + }; + peer.onicegatheringstatechange = function (evt) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'onicegatheringstatechange', this.label + ':' + this.iceGatheringState); + var idx = UWRTCIceGatheringState.indexOf(this.iceGatheringState); + if (idx === -1) { + console.error('unknown iceGatheringState: "' + this.iceGatheringState + '"'); + } + Module.dynCall_vii(uwevt_PCOnIceGatheringChange, peer.managePtr, idx); + }; + peer.onnegotiationneeded = function (evt) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'onnegotiationneeded', this.label); + Module.dynCall_vi(uwevt_PCOnNegotiationNeeded, peer.managePtr); + }; + peer.ondatachannel = function (evt) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'ondatachannel', this.label + ':' + evt.channel.label); + var channel = evt.channel; + channel.onmessage = (function (evt) { + if (typeof evt.data === "string") { + var msgPtr = uwcom_strToPtr(evt.data); + Module.dynCall_vii(uwevt_DCOnTextMessage, channel.managePtr, msgPtr); + } else { + var msgPtr = uwcom_arrayToReturnPtr(evt.data, Uint8Array); + Module.dynCall_viii(uwevt_DCOnBinaryMessage, channel.managePtr, msgPtr + 4, evt.data.byteLength); + } + }); + channel.onopen = function (evt) { + if (!uwcom_existsCheck(channel.managePtr, "onopen", "dataChannel")) return; + Module.dynCall_vi(uwevt_DCOnOpen, channel.managePtr); + }; + channel.onclose = function (evt) { + if (!uwcom_existsCheck(channel.managePtr, "onclose", "dataChannel")) return; + Module.dynCall_vi(uwevt_DCOnClose, channel.managePtr); + }; + uwcom_addManageObj(channel); + Module.dynCall_vii(uwevt_PCOnDataChannel, peer.managePtr, channel.managePtr); + }; + peer.ontrack = function (evt) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'ontrack', this.label + ':' + evt.track.kind); + var receiver = evt.receiver; + var transceiver = evt.transceiver; + var track = evt.track; + if (evt.streams[0]) + evt.streams[0].removeTrack(track); + uwcom_addManageObj(receiver); + uwcom_addManageObj(transceiver); + uwcom_addManageObj(track); + + var stream = new MediaStream(); + stream.addTrack(track); + if (track.kind === "audio") { + var audio = document.createElement('audio'); + audio.id = "audio_remote_" + track.managePtr.toString(); + audio.style.display = "none"; + audio.srcObject = stream; + //document.body.appendChild(audio); + audio.play(); + uwcom_remoteAudioTracks[track.managePtr] = { + track: track, + audio: audio + }; + Module.dynCall_vii(uwevt_PCOnTrack, peer.managePtr, transceiver.managePtr); + } else if (track.kind === "video") { + var video = document.createElement("video"); + video.id = "video_receive_" + track.managePtr.toString(); + //document.body.appendChild(video); + video.muted = true; + video.srcObject = stream; + video.style.width = "300px"; + video.style.height = "200px"; + video.style.position = "absolute"; + video.style.left = video.style.top = 0; + uwcom_remoteVideoTracks[track.managePtr] = { + track: track, + video: video + }; + video.play(); + video.onloadedmetadata = function (evt) { + Module.dynCall_vii(uwevt_PCOnTrack, peer.managePtr, transceiver.managePtr); + } + } + }; + uwcom_addManageObj(peer); + return peer.managePtr; + }, + + CreatePeerConnectionWithConfig: function (confPtr) { + var confJson = UTF8ToString(confPtr); + var conf = JSON.parse(confJson); + // conf.bundlePolicy = 'bundlePolicy' in conf ? conf.bundlePolicy : 0; + // conf.bundlePolicy = UWRTCBundlePolicy[conf.bundlePolicy]; + // conf.iceTransportPolicy = 'iceTransportPolicy' in conf ? conf.iceTransportPolicy : 3; + // conf.iceTransportPolicy = UWRTCIceTransportPolicy.indexOf[conf.iceTransportPolicy]; + + var iceIdx = 0; + for (var iceIdx = 0; iceIdx < conf.iceServers.length; iceIdx++) { + var idx = conf.iceServers[iceIdx].credentialType; + conf.iceServers[iceIdx].credentialType = UWRTCIceCredentialType[idx]; + } + + if (conf.iceTransportPolicy) { + if(conf.iceTransportPolicy.hasValye) conf.iceTransportPolicy = UWRTCIceTransportPolicy[conf.iceTransportPolicy.value]; + else delete conf.iceTransportPolicy; + } + if (conf.iceCandidatePoolSize) { + if (conf.iceCandidatePoolSize.hasValue) conf.iceCandidatePoolSize = conf.iceCandidatePoolSize.value; + else delete conf.iceCandidatePoolSize; + } + if (conf.bundlePolicy) + { + if(conf.bundlePolicy.hasValue) conf.bundlePolicy = UWRTCBundlePolicy[conf.bundlePolicy.value]; + else delete conf.bundlePolicy; + } + if (conf.enableDtlsSrtp) { + if(conf.enableDtlsSrtp.hasValue) conf.enableDtlsSrtp = conf.enableDtlsSrtp.value; + else delete conf.enableDtlsSrtp; + } + + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'CreatePeerConnectionWithConfig', JSON.stringify(conf)); + var ptr = _CreatePeerConnection(conf); + return ptr; + }, + + PeerConnectionSetDescriptionWithoutDescription: function (peerPtr) { + var peer = UWManaged[peerPtr]; + peer.setLocalDescription() + .then(function () { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionSetDescription', peer.label); + Module.dynCall_vi(uwevt_OnSetSessionDescSuccess, peer.managePtr); + }).catch(function (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionSetDescription', peer.label + ':' + err.message); + var errorNo = uwcom_errorNo(err); + var errMsgPtr = uwcom_strToPtr(err.message); + Module.dynCall_viii(uwevt_OnSetSessionDescFailure, peer.managePtr, errorNo, errMsgPtr); + }); + + // TODO: Use promises to wait for resolve/reject and return RTCErrorType + return uwcom_arrayToReturnPtr([UWRTCErrorType.indexOf("None"), uwcom_strToPtr("no error") ], Int32Array); + }, + + PeerConnectionSetDescription: function (peerPtr, typeIdx, sdpPtr, side) { + var peer = UWManaged[peerPtr]; + var type = UWRTCSdpType[typeIdx]; + var sdp = UTF8ToString(sdpPtr); + peer['set' + side + 'Description']({type: type, sdp: sdp}) + .then(function () { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionSetDescription', peer.label + ':' + side + ':' + type + ':' /*+ sdp*/); + Module.dynCall_vi(uwevt_OnSetSessionDescSuccess, peer.managePtr); + }).catch(function (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionSetDescription', peer.label + ':' + side + ':' + err.message); + var errorNo = uwcom_errorNo(err); + var errMsgPtr = uwcom_strToPtr(err.message); + Module.dynCall_viii(uwevt_OnSetSessionDescFailure, peer.managePtr, errorNo, errMsgPtr); + }); + + // TODO: Use promises to wait for resolve/reject and return RTCErrorType + return uwcom_arrayToReturnPtr([UWRTCErrorType.indexOf("None"), uwcom_strToPtr("no error") ], Int32Array); + }, + + PeerConnectionIceConditionState: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionIceConditionState', 'peer')) return; + var peer = UWManaged[peerPtr]; + var idx = UWRTCIceConnectionState.indexOf(peer.iceConnectionState); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionIceConditionState', peer.label + ':' + peer.iceConnectionState); + return idx; + }, + + PeerConnectionState: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionState', 'peer')) return; + var peer = UWManaged[peerPtr]; + var idx = UWRTCPeerConnectionState.indexOf(peer.connectionState); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionState', peer.label + ':' + peer.connectionState); + return idx; + }, + + PeerConnectionSignalingState: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSignalingState', 'peer')) return; + var peer = UWManaged[peerPtr]; + var idx = UWRTCSignalingState.indexOf(peer.signalingState); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionSignalingState', peer.label + ':' + peer.signalingState); + return idx; + }, + + PeerConnectionIceGatheringState: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionIceGatheringState', 'peer')) return; + var peer = UWManaged[peerPtr]; + var idx = UWRTCIceGathererState.indexOf(peer.iceGatheringState); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionIceGatheringState', peer.label + ':' + peer.iceGatheringState); + return idx; + }, + + PeerConnectionGetReceivers: function (contextPtr, peerPtr) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionGetReceivers', 'context')) return; + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetReceivers', 'peer')) return; + var peer = UWManaged[peerPtr]; + var receivers = peer.getReceivers(); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetReceivers', peer.label + ': receivers=' + receivers.length); + var ptrs = []; + receivers.forEach(function (receiver) { + uwcom_addManageObj(receiver); + ptrs.push(receiver.managePtr); + }); + var ptr = uwcom_arrayToReturnPtr(ptrs, Int32Array); + return ptr; + }, + + PeerConnectionGetSenders: function (contextPtr, peerPtr) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionGetSenders', 'context')) return; + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetSenders', 'peer')) return; + var peer = UWManaged[peerPtr]; + var senders = peer.getSenders(); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetSenders', peer.label + ': senders=' + senders.length); + var ptrs = []; + senders.forEach(function (sender) { + uwcom_addManageObj(sender); + ptrs.push(sender.managePtr); + }); + var ptr = uwcom_arrayToReturnPtr(ptrs, Int32Array); + return ptr; + }, + + PeerConnectionGetTransceivers: function (contextPtr, peerPtr) { + if (!uwcom_existsCheck(contextPtr, 'PeerConnectionGetTransceivers', 'context')) return; + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetTransceivers', 'peer')) return; + var peer = UWManaged[peerPtr]; + var transceivers = peer.getTransceivers(); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetTransceivers', peer.label + ': transceivers=' + transceivers.length); + var ptrs = []; + transceivers.forEach(function (transceiver) { + uwcom_addManageObj(transceiver); + ptrs.push(transceiver.managePtr); + }); + var ptr = uwcom_arrayToReturnPtr(ptrs, Int32Array); + return ptr; + }, + + PeerConnectionGetConfiguration: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetConfiguration', 'peer')) return; + var peer = UWManaged[peerPtr]; + var conf = peer.getConfiguration(); + + // TODO: Clean + if(conf.iceTransportPolicy) + conf.iceTransportPolicy = { + hasValue: true, + value: UWRTCIceTransportPolicy.indexOf(conf.iceTransportPolicy) + }; + else + conf.iceTransportPolicy = { + hasValue: false, + value: UWRTCIceTransportPolicy.indexOf(conf.iceTransportPolicy) + }; + if(conf.bundlePolicy) + conf.bundlePolicy = { + hasValue: true, + value: UWRTCBundlePolicy.indexOf(conf.bundlePolicy) + }; + else + conf.bundlePolicy = { + hasValue: false, + value: UWRTCBundlePolicy.indexOf(conf.bundlePolicy) + }; + + if(conf.iceCandidatePoolSize !== undefined) + conf.iceCandidatePoolSize = { + hasValue: true, + value: conf.iceCandidatePoolSize + }; + else + conf.iceCandidatePoolSize = { + hasValue: false, + value: 0 + }; + if(conf.enableDtlsSrtp) + conf.enableDtlsSrtp = { + hasValue: false, + value: null + }; + else + conf.enableDtlsSrtp = { + hasValue: false, + value: null + }; + + + confJson = JSON.stringify(conf); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetConfiguration', peer.label + ':' + confJson); + var ptr = uwcom_strToPtr(confJson); + return ptr; + }, + + PeerConnectionSetConfiguration: function (peerPtr, confPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSetConfiguration', 'peer')) return 11; // OperationErrorWithData + try { + var peer = UWManaged[peerPtr]; + var confJson = UTF8ToString(confPtr); + var conf = JSON.parse(confJson); + // conf.bundlePolicy = 'bundlePolicy' in conf ? conf.bundlePolicy : 0; + // conf.bundlePolicy = UWRTCBundlePolicy[conf.bundlePolicy]; + // conf.iceTransportPolicy = 'iceTransportPolicy' in conf ? conf.iceTransportPolicy : 3; + // conf.iceTransportPolicy = UWRTCIceTransportPolicy[conf.iceTransportPolicy]; + // uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionSetConfiguration', peer.label + ':' + JSON.stringify(conf)); + + var iceIdx = 0; + for (var iceIdx = 0; iceIdx < conf.iceServers.length; iceIdx++) { + var idx = conf.iceServers[iceIdx].credentialType; + conf.iceServers[iceIdx].credentialType = UWRTCIceCredentialType[idx]; + } + if (conf.iceTransportPolicy) conf.iceTransportPolicy = conf.iceTransportPolicy.value; + if (conf.iceCandidatePoolSize) conf.iceCandidatePoolSize = conf.iceCandidatePoolSize.value; + if (conf.bundlePolicy) conf.bundlePolicy = conf.bundlePolicy.value; + if (conf.enableDtlsSrtp) conf.enableDtlsSrtp = conf.enableDtlsSrtp.value; + + peer.setConfiguration(conf); + return 0; + } catch (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionSetConfiguration', peer.label + ':' + err.message); + return uwcom_errorNo(err); + } + }, + + PeerConnectionRegisterCallbackCreateSD: function (peerPtr, OnSuccessCreateSessionDesc, OnFailureCreateSessionDesc) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterCallbackCreateSD', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterCallbackCreateSD', peer.label); + uwevt_OnSuccessCreateSessionDesc = OnSuccessCreateSessionDesc; + uwevt_OnFailureCreateSessionDesc = OnFailureCreateSessionDesc; + }, + + PeerConnectionRegisterCallbackCollectStats: function (peerPtr, OnStatsDeliveredCallback) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterCallbackCollectStats', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterCallbackCollectStats', peer.label); + uwevt_OnStatsDeliveredCallback = OnStatsDeliveredCallback; + }, + + PeerConnectionRegisterIceConnectionChange: function (peerPtr, PCOnIceConnectionChange) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterIceConnectionChange', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterIceConnectionChange', peer.label); + uwevt_PCOnIceConnectionChange = PCOnIceConnectionChange; + }, + + PeerConnectionRegisterConnectionStateChange: function (peerPtr, PCOnConnectionStateChange) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterConnectionStateChange', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterConnectionStateChange', peer.label); + uwevt_PCOnConnectionStateChange = PCOnConnectionStateChange; + }, + + PeerConnectionRegisterIceGatheringChange: function (peerPtr, PCOnIceGatheringChange) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterIceGatheringChange', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterIceGatheringChange', peer.label); + uwevt_PCOnIceGatheringChange = PCOnIceGatheringChange; + }, + + PeerConnectionRegisterOnIceCandidate: function (peerPtr, PCOnIceCandidate) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterOnIceCandidate', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterOnIceCandidate', peer.label); + uwevt_PCOnIceCandidate = PCOnIceCandidate; + }, + + PeerConnectionRegisterOnDataChannel: function (peerPtr, PCOnDataChannel) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterOnDataChannel', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterOnDataChannel', peer.label); + uwevt_PCOnDataChannel = PCOnDataChannel; + }, + + PeerConnectionRegisterOnRenegotiationNeeded: function (peerPtr, PCOnNegotiationNeeded) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterOnRenegotiationNeeded', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterOnRenegotiationNeeded', peer.label); + uwevt_PCOnNegotiationNeeded = PCOnNegotiationNeeded; + }, + + PeerConnectionRegisterOnTrack: function (peerPtr, PCOnTrack) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterOnTrack', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterOnTrack', peer.label); + uwevt_PCOnTrack = PCOnTrack; + }, + + PeerConnectionRegisterOnRemoveTrack: function (peerPtr, PCOnRemoveTrack) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRegisterOnRemoveTrack', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRegisterOnRemoveTrack', peer.label); + uwevt_PCOnRemoveTrack = PCOnRemoveTrack; + }, + + PeerConnectionClose: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionClose', 'peer')) return; + var peer = UWManaged[peerPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionClose', peer.label); + peer.close(); + }, + + //TODO + PeerConnectionRestartIce: function (peerPtr) { + + }, + + PeerConnectionAddTrack: function (peerPtr, trackPtr, streamPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionAddTrack', 'peer')) return; + if (!uwcom_existsCheck(trackPtr, 'PeerConnectionAddTrack', 'track')) return; + var peer = UWManaged[peerPtr]; + var track = UWManaged[trackPtr]; + var stream = null; + if (streamPtr === 0) { + stream = new MediaStream(); + uwcom_addManageObj(stream); + } else { + if (!uwcom_existsCheck(streamPtr, 'PeerConnectionAddTrack', 'stream')) return; + stream = UWManaged[streamPtr]; + } + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionAddTrack', peer.label + ':' + track.kind); + + // TODO: Only add video element for local webcam display + if (track.kind == "video") { + var video = document.createElement("video"); + video.id = "video_send_" + track.managePtr.toString(); + //document.body.appendChild(video); + video.muted = true; + video.srcObject = stream; + video.style.width = "300px"; + video.style.height = "200px"; + video.style.position = "absolute"; + video.style.left = video.style.top = 0; + uwcom_remoteVideoTracks[track.managePtr] = { + track: track, + video: video + }; + video.play(); + } + + var error = 0; + var sender = 0; + try { + sender = peer.addTrack(track); + uwcom_addManageObj(sender); + } + catch (err){ + error = UWRTCErrorType.indexOf("InvalidState"); + } + + var ptrs = [error, sender.managePtr]; + return uwcom_arrayToReturnPtr(ptrs, Int32Array); + }, + + PeerConnectionRemoveTrack: function (peerPtr, senderPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionRemoveTrack', 'peer')) return; + if (!uwcom_existsCheck(senderPtr, 'PeerConnectionRemoveTrack', 'sender')) return; + var peer = UWManaged[peerPtr]; + var sender = UWManaged[senderPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionRemoveTrack', peer.label + ':' + sender.track.kind); + peer.removeTrack(sender); + }, + + PeerConnectionAddTransceiver: function (peerPtr, trackPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionAddTransceiver', 'peer')) return; + if (!uwcom_existsCheck(trackPtr, 'PeerConnectionAddTransceiver', 'track')) return; + var peer = UWManaged[peerPtr]; + var track = UWManaged[trackPtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionAddTransceiver', peer.label + ':' + track.kind); + var transceiver = peer.addTransceiver(track); + uwcom_addManageObj(transceiver); + return transceiver.managePtr; + }, + + PeerConnectionAddTransceiverWithType: function (peerPtr, kindIdx) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionAddTransceiverWithType', 'peer')) return; + var peer = UWManaged[peerPtr]; + var kind = UWMediaStreamTrackKind[kindIdx]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionAddTransceiverWithType', peer.label + ':' + kind); + var transceiver = peer.addTransceiver(kind); + uwcom_addManageObj(transceiver); + return transceiver.managePtr; + }, + + PeerConnectionAddIceCandidate: function (peerPtr, candidatePtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionAddIceCandidate', 'peer')) return; + if (!uwcom_existsCheck(candidatePtr, 'PeerConnectionAddIceCandidate', 'candidate')) return; + var peer = UWManaged[peerPtr]; + var candidate = UWManaged[candidatePtr]; + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionAddIceCandidate', peer.label + ':' + JSON.stringify(candidate)); + + // TEMP: Use timeout so we have a higher chance that the description is set before the first candidate arrives + // Timing bug: Often we icecandidate is received earlier than the description, and it's not possible to set an ice candiate without a description on the peer server + // Possible solution: Put icecandidates on a queue if the description is not set, and handle the queue when the description is set. + setTimeout(function () { + peer.addIceCandidate(candidate) + .then(function () { + }) + .catch(function (err) { + console.error(err.message, peerPtr) + }); + }, 1000); + + // TODO: Fix async return value + return true; + }, + + PeerConnectionCreateOffer: function (peerPtr, optionsPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionCreateOffer', 'peer')) return; + var peer = UWManaged[peerPtr]; + var options = UTF8ToString(optionsPtr); + var options = JSON.parse(options); + peer.createOffer(options).then(function (offer) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionCreateOffer', peer.label + ':' + JSON.stringify(options) + ':' + offer.type); + uwcom_addManageObj(offer); + var sdpPtr = uwcom_strToPtr(offer.sdp); + Module.dynCall_viii(uwevt_OnSuccessCreateSessionDesc, peerPtr, 0, sdpPtr); // 0 === offer + }).catch(function (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionCreateOffer', peer.label + ':' + err.message); + var errorNo = uwcom_errorNo(err); + var errMsgPtr = uwcom_strToPtr(err.message); + Module.dynCall_viii(uwevt_OnFailureCreateSessionDesc, peerPtr, errorNo, errMsgPtr); + }); + }, + + PeerConnectionCreateAnswer: function (peerPtr, optionsPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionCreateAnswer', 'peer')) return; + var peer = UWManaged[peerPtr]; + var options = UTF8ToString(optionsPtr); + var options = JSON.parse(options); + peer.createAnswer(options).then(function (answer) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionCreateAnswer', peer.label + ':' + JSON.stringify(options) + ':' + answer.type); + uwcom_addManageObj(answer); + var sdpPtr = uwcom_strToPtr(answer.sdp); + Module.dynCall_viii(uwevt_OnSuccessCreateSessionDesc, peerPtr, 2, sdpPtr); // 2 == answer + }).catch(function (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionCreateAnswer', peer.label + ':' + err.message); + var errorNo = uwcom_errorNo(err); + var errMsgPtr = uwcom_strToPtr(err.message); + Module.dynCall_viii(uwevt_OnFailureCreateSessionDesc, peerPtr, errorNo, errMsgPtr); + }); + }, + + PeerConnectionGetStats: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetStats', 'peer')) return; + var peer = UWManaged[peerPtr]; + peer.getStats().then(function (stats) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetStats', peer.label + ': stats=' + stats.length); + uwcom_addManageObj(stats); + Module.dynCall_vii(uwevt_OnStatsDeliveredCallback, peer.managePtr, stats.managePtr); + }).catch(function (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionGetStats', peer.label + ':' + err.message); + }); + }, + + PeerConnectionTrackGetStats: function (peerPtr, trackPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionTrackGetStats', 'peer')) return; + if (!uwcom_existsCheck(trackPtr, 'PeerConnectionTrackGetStats', 'track')) return; + var peer = UWManaged[peerPtr]; + var track = UWManaged[trackPtr]; + peer.getStats(track).then(function (stats) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionTrackGetStats', peer.label + ':' + track.kind + ':stats=' + stats.length); + uwcom_addManageObj(stats); + Module.dynCall_vii(uwevt_OnStatsDeliveredCallback, peer.managePtr, stats.managePtr); + }).catch(function (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionTrackGetStats', peer.label + ':' + err.message); + }); + }, + + PeerConnectionSenderGetStats: function (peerPtr, senderPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSenderGetStats', 'peer')) return; + if (!uwcom_existsCheck(senderPtr, 'PeerConnectionSenderGetStats', 'sender')) return; + var peer = UWManaged[peerPtr]; + var sender = UWManaged[senderPtr]; + sender.getStats().then(function (stats) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionSenderGetStats', peer.label + ':' + sender.track.kind + ':stats=' + stats.length); + uwcom_addManageObj(stats); + Module.dynCall_vii(uwevt_OnStatsDeliveredCallback, peer.managePtr, stats.managePtr); + }).catch(function (err) { + uwcom_debugLog('error', 'RTCPeerConnection.jslib', 'PeerConnectionSenderGetStats', peer.label + ':' + err.message); + }); + }, + + PeerConnectionReceiverGetStats: function (peerPtr, receiverPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionReceiverGetStats', 'peer')) return; + if (!uwcom_existsCheck(receiverPtr, 'PeerConnectionReceiverGetStats', 'receiver')) return; + var peer = UWManaged[peerPtr]; + var receiver = UWManaged[receiverPtr]; + receiver.getStats().then(function (stats) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionReceiverGetStats', peer.label + ':' + receiver.track.kind + ':stats=' + stats.length); + uwcom_addManageObj(stats); + Module.dynCall_vii(uwevt_OnStatsDeliveredCallback, peer.managePtr, stats.managePtr); + }).catch(function (err) { + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionReceiverGetStats', peer.label + ':' + err.message); + }) + }, + + PeerConnectionGetLocalDescription: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetLocalDescription', 'peer')) return uwcom_strToPtr("false"); + var peer = UWManaged[peerPtr]; + if (!peer.localDescription) return uwcom_strToPtr("false"); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetLocalDescription', peer.label + ':' + peer.localDescription.type); + var type = UWRTCSdpType.indexOf(peer.localDescription.type); + var sdp = peer.localDescription.sdp; + return uwcom_strToPtr(JSON.stringify({type: type, sdp: sdp})); + }, + + PeerConnectionGetRemoteDescription: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetRemoteDescription', 'peer')) return uwcom_strToPtr("false"); + var peer = UWManaged[peerPtr]; + if (!peer.remoteDescription) return uwcom_strToPtr("false"); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetRemoteDescription', peer.label + ':' + peer.remoteDescription.type); + var type = UWRTCSdpType.indexOf(peer.remoteDescription.type); + var sdp = peer.remoteDescription.sdp; + return uwcom_strToPtr(JSON.stringify({type: type, sdp: sdp})); + }, + + PeerConnectionGetCurrentLocalDescription: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetCurrentLocalDescription', 'peer')) return uwcom_strToPtr("false"); + var peer = UWManaged[peerPtr]; + if (!peer.currentLocalDescription) return uwcom_strToPtr("false"); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetCurrentLocalDescription', peer.label + ':' + peer.currentLocalDescription.type); + var type = UWRTCSdpType.indexOf(peer.currentLocalDescription.type); + var sdp = peer.currentLocalDescription.sdp; + return uwcom_strToPtr(JSON.stringify({type: type, sdp: sdp})); + }, + + PeerConnectionGetCurrentRemoteDescription: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetCurrentRemoteDescription', 'peer')) return uwcom_strToPtr("false"); + var peer = UWManaged[peerPtr]; + if (!peer.currentRemoteDescription) return uwcom_strToPtr("false"); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetCurrentRemoteDescription', peer.label + ':' + peer.currentRemoteDescription.type); + var type = UWRTCSdpType.indexOf(peer.currentRemoteDescription.type); + var sdp = peer.currentRemoteDescription.sdp; + return uwcom_strToPtr(JSON.stringify({type: type, sdp: sdp})); + }, + + PeerConnectionGetPendingLocalDescription: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetPendingLocalDescription', 'peer')) return uwcom_strToPtr("false"); + var peer = UWManaged[peerPtr]; + if (!peer.pendingLocalDescription) return uwcom_strToPtr("false"); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetPendingLocalDescription', peer.label + ':' + peer.pendingLocalDescription.type); + var type = UWRTCSdpType.indexOf(peer.pendingLocalDescription.type); + var sdp = peer.pendingLocalDescription.sdp; + return uwcom_strToPtr(JSON.stringify({type: type, sdp: sdp})); + }, + + PeerConnectionGetPendingRemoteDescription: function (peerPtr) { + if (!uwcom_existsCheck(peerPtr, 'PeerConnectionGetPendingRemoteDescription', 'peer')) return uwcom_strToPtr("false"); + var peer = UWManaged[peerPtr]; + if (!peer.pendingRemoteDescription) return uwcom_strToPtr("false"); + uwcom_debugLog('log', 'RTCPeerConnection.jslib', 'PeerConnectionGetPendingRemoteDescription', peer.label + ':' + peer.pendingRemoteDescription.type); + var type = UWRTCSdpType.indexOf(peer.pendingRemoteDescription.type); + var sdp = peer.pendingRemoteDescription.sdp; + return uwcom_strToPtr(JSON.stringify({type: type, sdp: sdp})); + } +}; +mergeInto(LibraryManager.library, UnityWebRTCPeerConnection); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/RTCPeerConnection.jslib.meta b/Runtime/Plugins/WebGL/RTCPeerConnection.jslib.meta new file mode 100644 index 0000000000..0fdd14eabb --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCPeerConnection.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: efe1ed2a05ce44d428f4f632a4a91e46 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/RTCRtpReceiver.jslib b/Runtime/Plugins/WebGL/RTCRtpReceiver.jslib new file mode 100644 index 0000000000..e75f05dbed --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCRtpReceiver.jslib @@ -0,0 +1,18 @@ +var UnityWebRTCRtpReceiver = { + DeleteReceiver: function (receiverPtr) { + if (!uwcom_existsCheck(receiverPtr, 'DeleteReceiver', 'receiver')) return; + delete UWManaged[receiverPtr]; + }, + + ReceiverGetTrack: function (receiverPtr) { + if (!uwcom_existsCheck(receiverPtr, 'ReceiverGetTrack', 'receiver')) return; + var receiver = UWManaged[receiverPtr]; + uwcom_addManageObj(receiver.track); + return receiver.track.managePtr; + }, + + ReceiverGetStreams: function(receiverPtr, length){ + + } +}; +mergeInto(LibraryManager.library, UnityWebRTCRtpReceiver); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/RTCRtpReceiver.jslib.meta b/Runtime/Plugins/WebGL/RTCRtpReceiver.jslib.meta new file mode 100644 index 0000000000..8254f7fc6c --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCRtpReceiver.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 04644cf0d01425245be2f29da80d9db2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/RTCRtpSender.jslib b/Runtime/Plugins/WebGL/RTCRtpSender.jslib new file mode 100644 index 0000000000..8f9f21a601 --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCRtpSender.jslib @@ -0,0 +1,43 @@ +var UnityWebRTCRtpSender = { + DeleteSender: function (senderPtr) { + if (!uwcom_existsCheck(senderPtr, 'DeleteSender', 'sender')) return; + delete UWManaged[senderPtr]; + }, + + SenderGetTrack: function (senderPtr) { + if (!uwcom_existsCheck(senderPtr, 'SenderGetTrack', 'sender')) return; + var sender = UWManaged[senderPtr]; + if(sender.track){ + uwcom_addManageObj(sender.track); + return sender.track.managePtr; + } + }, + + SenderGetParameters: function (senderPtr) { + if (!uwcom_existsCheck(senderPtr, 'SenderGetParameters', 'sender')) return; + var sender = UWManaged[senderPtr]; + var parameters = sender.getParameters(); + var parametersJson = JSON.stringify(parameters); + var parametersJsonPtr = uwcom_strToPtr(parametersJson); + return parametersJsonPtr; + }, + + SenderSetParameters: function (senderPtr, parametersJsonPtr) { + if (!uwcom_existsCheck(senderPtr, 'SenderSetParameters', 'sender')) return; + var sender = UWManaged[senderPtr]; + var parametersJson = UTF8ToString(parametersJsonPtr); + var parameters = JSON.parse(parametersJson); + sender.setParameters(parameters).then(function () { + // TODO Send correct RTCErrorType. + }); + }, + + SenderReplaceTrack: function (senderPtr, trackPtr) { + if (!uwcom_existsCheck(senderPtr, 'SenderReplaceTrack', 'sender')) return; + if (!uwcom_existsCheck(trackPtr, 'SenderReplaceTrack', 'track')) return; + var sender = UWManaged[senderPtr]; + var track = UWManaged[trackPtr]; + sender.replaceTrack(track); + } +}; +mergeInto(LibraryManager.library, UnityWebRTCRtpSender); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/RTCRtpSender.jslib.meta b/Runtime/Plugins/WebGL/RTCRtpSender.jslib.meta new file mode 100644 index 0000000000..dd9cbf8c50 --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCRtpSender.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: bb7c8a3b5ea8c91499b72c9bf0c8056e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib b/Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib new file mode 100644 index 0000000000..ba032b3510 --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib @@ -0,0 +1,70 @@ +var UnityWebRTCRtpTransceiver = { + DeleteTransceiver: function (transceiverPtr) { + if (!uwcom_existsCheck(transceiverPtr, 'DeleteTransceiver', 'transceiver')) return; + delete UWManaged[transceiverPtr]; + }, + + TransceiverGetDirection: function (transceiverPtr) { + if (!uwcom_existsCheck(transceiverPtr, 'TransceiverGetDirection', 'transceiver')) return; + var transceiver = UWManaged[transceiverPtr]; + return UWRTCRtpTransceiverDirection.indexOf(transceiver.direction); + }, + + TransceiverSetDirection: function (transceiverPtr, directionIdx) { + if (!uwcom_existsCheck(transceiverPtr, 'TransceiverSetDirection', 'transceiver')) return 0; + var transceiver = UWManaged[transceiverPtr]; + transceiver.direction = UWRTCRtpTransceiverDirection[directionIdx]; + return 0; + }, + + TransceiverGetCurrentDirection: function (transceiverPtr) { + if (!uwcom_existsCheck(transceiverPtr, 'TransceiverGetCurrentDirection', 'transceiver')) return; + var transceiver = UWManaged[transceiverPtr]; + return UWRTCRtpTransceiverDirection.indexOf(transceiver.currentDirection); + }, + + TransceiverGetReceiver: function (transceiverPtr) { + if (!uwcom_existsCheck(transceiverPtr, 'TransceiverGetReceiver', 'transceiver')) return; + var transceiver = UWManaged[transceiverPtr]; + uwcom_addManageObj(transceiver.receiver); + return transceiver.receiver.managePtr; + }, + + TransceiverGetSender: function (transceiverPtr) { + if (!uwcom_existsCheck(transceiverPtr, 'TransceiverGetSender', 'transceiver')) return; + var transceiver = UWManaged[transceiverPtr]; + uwcom_addManageObj(transceiver.sender); + return transceiver.sender.managePtr; + }, + + TransceiverSetCodecPreferences: function (transceiverPtr, codecsPtr) { + if (!uwcom_existsCheck(transceiverPtr, 'TransceiverSetCodecPreferences', 'transceiver')) return UWRTCErrorType.indexOf("OperationErrorWithData"); + + var transceiver = UWManaged[transceiverPtr]; + var codecsJson = UTF8ToString(codecsPtr); + var codecs = JSON.parse(codecsJson); + + const supportsSetCodecPreferences = window.RTCRtpTransceiver && 'setCodecPreferences' in window.RTCRtpTransceiver.prototype; + if (supportsSetCodecPreferences) { + try { + transceiver.setCodecPreferences(codecs); + } catch (err) { + return UWRTCErrorType.indexOf("InvalidModification"); + } + } + else return UWRTCErrorType.indexOf("UnsupportedOperation"); + return UWRTCErrorType.indexOf("None"); + }, + + TransceiverStop: function (transceiverPtr) { + if (!uwcom_existsCheck(transceiverPtr, 'TransceiverStop', 'transceiver')) return; + try { + var transceiver = UWManaged[transceiverPtr]; + transceiver.stop(); + return true; + } catch (err) { + return false; + } + } +}; +mergeInto(LibraryManager.library, UnityWebRTCRtpTransceiver); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib.meta b/Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib.meta new file mode 100644 index 0000000000..3a05944fc2 --- /dev/null +++ b/Runtime/Plugins/WebGL/RTCRtpTransceiver.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 7e5d8020b50ed6a47ab8ab8dd12975b9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/VideoRenderer.jslib b/Runtime/Plugins/WebGL/VideoRenderer.jslib new file mode 100644 index 0000000000..79b97bc64c --- /dev/null +++ b/Runtime/Plugins/WebGL/VideoRenderer.jslib @@ -0,0 +1,72 @@ +var UnityWebRTCVideoRenderer = { + CreateVideoRenderer: function (contextPtr) { + }, + + CreateNativeTexture: function() { + //console.log('nativeTexture'); + var texPtr = 0; + for(var texPtr = 0; texPtr < GL.textures.length; texPtr++) { + if(GL.textures[texPtr] === undefined) + break; + } + var tex = GLctx.createTexture(); + tex.name = texPtr; + GL.textures[texPtr] = tex; + //console.log('nativeTexture' + texPtr); + return texPtr; + }, + + VideoSourceGetSyncApplicationFramerate: function (reportPtr) { + console.error("Not implemented"); + return false; + }, + + VideoSourceSetSyncApplicationFramerate: function (reportPtr) { + console.error("Not implemented"); + }, + + SetGraphicsSyncTimeout: function(nSecTimeout) { + throw new Error("Not implemented"); + }, + + GetVideoRendererId: function (sinkPtr) { + throw new Error("Not implemented"); + }, + + DeleteVideoRenderer: function (contextPtr, sinkPtr) { + throw new Error("Not implemented"); + }, + + UpdateRendererTexture: function (trackPtr, renderTexturePtr, needFlip) { + // console.log('UpdateRendererTexture'); + if (!uwcom_existsCheck(trackPtr, 'UpdateRendererTexture', 'track')) return; + if (!uwcom_remoteVideoTracks[trackPtr]) return; + //console.log('UpdateRendererTexture', renderTexturePtr); + var video = uwcom_remoteVideoTracks[trackPtr].video; + var tex = GL.textures[renderTexturePtr]; + GLctx.bindTexture(GLctx.TEXTURE_2D, tex); + if (!!needFlip){ + //GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, true); + } + // For now: Flip every time, since we want the correct image transfered over WebRTC + GLctx.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + GLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA, GLctx.UNSIGNED_BYTE, video); + GLctx.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + // GLctx.texSubImage2D(GLctx.TEXTURE_2D, 0, 0, 0, GLctx.RGBA, GLctx.UNSIGNED_BYTE, video); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_MAG_FILTER, GLctx.LINEAR); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_MIN_FILTER, GLctx.LINEAR); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_WRAP_S, GLctx.CLAMP_TO_EDGE); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_WRAP_T, GLctx.CLAMP_TO_EDGE); + GLctx.bindTexture(GLctx.TEXTURE_2D, null); + }, + + // Not used in WebGL, but is here to make the NativeMethods more maintainable. + VideoTrackAddOrUpdateSink: function(trackPtr, sinkPtr){ + + }, + + VideoTrackRemoveSink: function(trackPtr, sinkPtr){ + + } +}; +mergeInto(LibraryManager.library, UnityWebRTCVideoRenderer); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/VideoRenderer.jslib.meta b/Runtime/Plugins/WebGL/VideoRenderer.jslib.meta new file mode 100644 index 0000000000..9241b52f58 --- /dev/null +++ b/Runtime/Plugins/WebGL/VideoRenderer.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: 50b140155b56d87498473c49584b36d9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/VideoStreamTrack.jslib b/Runtime/Plugins/WebGL/VideoStreamTrack.jslib new file mode 100644 index 0000000000..76bd802266 --- /dev/null +++ b/Runtime/Plugins/WebGL/VideoStreamTrack.jslib @@ -0,0 +1,164 @@ +var UnityWebRTCVideoStreamTrack = { + CreateVideoTrack: function (srcPtr, dstPtr, width, height) { + var cnv = document.createElement('canvas'); + cnv.width = width; + cnv.height = height; + var ctx = cnv.getContext('2d'); + var imgData = ctx.createImageData(width, height); + var stream = cnv.captureStream(); + var track = stream.getVideoTracks()[0]; + var srcTexture = GL.textures[srcPtr]; + var dstTexture = GL.textures[dstPtr]; + var frameBuffer = GLctx.createFramebuffer(); + GLctx.bindFramebuffer(GLctx.FRAMEBUFFER, frameBuffer); + GLctx.framebufferTexture2D( + GLctx.FRAMEBUFFER, + GLctx.COLOR_ATTACHMENT0, + GLctx.TEXTURE_2D, + srcTexture, + 0 + ); + var canRead = (GLctx.checkFramebufferStatus(GLctx.FRAMEBUFFER) === GLctx.FRAMEBUFFER_COMPLETE); + GLctx.bindFramebuffer(GLctx.FRAMEBUFFER, null); + var localVideoData = { + cnv: cnv, + ctx: ctx, + imgData: imgData, + width: width, + height: height, + dstTexture: dstTexture, + stream: stream, + canRead: canRead, + buffer: new Uint8Array(width * height * 4), + lineBuffer: new Uint8Array(width * 4), + frameBuffer: frameBuffer + }; + + //uwcom_addManageObj(stream); + uwcom_addManageObj(track); + uwcom_localVideoTracks[track.managePtr] = localVideoData; + //console.log('localVideoData', track.managePtr); + return track.managePtr; + }, + + // Not finished, don't delete. + $readPixelsAsync: function (data) { + var w = data.width; + var h = data.height; + var buffer = data.buffer; + var lineBuffer = data.lineBuffer; + var frameBuffer = data.frameBuffer; + var imgData = data.imgData; + var cnv = data.cnv; + var ctx = data.ctx; + var dstTexture = data.dstTexture; + var buf = GLctx.createBuffer(); + GLctx.bindTexture(GLctx.TEXTURE_2D, dstTexture); + GLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA, GLctx.UNSIGNED_BYTE, cnv); + GLctx.bindBuffer(GLctx.PIXEL_PACK_BUFFER, buf); + GLctx.bufferData(GLctx.PIXEL_PACK_BUFFER, buffer.byteLength, GLctx.STREAM_READ); + GLctx.readPixels(0, 0, w, h, GLctx.RGBA, GLctx.UNSIGNED_BYTE, 0); + GLctx.bindBuffer(GLctx.PIXEL_PACK_BUFFER, null); + + var sync = GLctx.fenceSync(GLctx.SYNC_GPU_COMMANDS_COMPLETE, 0); + if (!sync) { + return null; + } + + GLctx.flush(); + + return clientWaitAsync(sync, 0, 10).then(function () { + GLctx.deleteSync(sync); + + GLctx.bindBuffer(GLctx.PIXEL_PACK_BUFFER, buf); + GLctx.getBufferSubData(GLctx.PIXEL_PACK_BUFFER, 0, buffer); + GLctx.bindBuffer(GLctx.PIXEL_PACK_BUFFER, null); + GLctx.deleteBuffer(buf); + + imgData.data.set(buffer); + ctx.putImageData(imgData, 0, 0); + + GLctx.bindTexture(GLctx.TEXTURE_2D, dstTexture); + GLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA, GLctx.UNSIGNED_BYTE, cnv); + //GLctx.texSubImage2D(GLctx.TEXTURE_2D, 0, 0, 0, GLctx.RGBA, GLctx.UNSIGNED_BYTE, cnv); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_MAG_FILTER, GLctx.LINEAR); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_MIN_FILTER, GLctx.LINEAR); + GLctx.generateMipmap(GLctx.TEXTURE_2D); + GLctx.bindTexture(GLctx.TEXTURE_2D, null); + }); + }, + + // Not finished, don't delete. + $clientWaitAsync: function (sync, flags, interval_ms) { + return new Promise(function (resolve, reject) { + var check = function() { + var res = GLctx.clientWaitSync(sync, flags, 0); + if (res === GLctx.WAIT_FAILED) { + reject(); + return; + } + if (res === GLctx.TIMEOUT_EXPIRED) { + setTimeout(check, interval_ms); + return; + } + resolve(); + }; + check(); + }); + }, + + RenderLocalVideotrack__deps: ['$readPixelsAsync', '$clientWaitAsync'], + RenderLocalVideotrack: function (trackPtr, needFlip) { + var data = uwcom_localVideoTracks[trackPtr]; + // console.log('RenderLocalVideotrack', trackPtr, data); + if (!data) return; + // readPixelsAsync(data); + // return; + var w = data.width; + var h = data.height; + var buffer = data.buffer; + var lineBuffer = data.lineBuffer; + var frameBuffer = data.frameBuffer; + var imgData = data.imgData; + var cnv = data.cnv; + var ctx = data.ctx; + var dstTexture = data.dstTexture; + + if (data.canRead) { + GLctx.bindFramebuffer(GLctx.FRAMEBUFFER, frameBuffer); + GLctx.readPixels(0, 0, w, h, GLctx.RGBA, GLctx.UNSIGNED_BYTE, buffer); + GLctx.bindFramebuffer(GLctx.FRAMEBUFFER, null); + + // var halfHeight = h / 2 | 0; + // var bytesPerRow = w; + // for (var y = 0; y < halfHeight; y++) { + // var topOffset = y * bytesPerRow; + // var bottomOffset = (h - y - 1) * bytesPerRow; + // data.lineBuffer.set(buffer.subarray(topOffset, topOffset + bytesPerRow)); + // buffer.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); + // buffer.set(lineBuffer, bottomOffset); + // } + + imgData.data.set(buffer); + ctx.putImageData(imgData, 0, 0); + + // For now: Flip every time, since we want the correct image transfered over WebRTC + ctx.globalCompositeOperation = 'copy'; + ctx.scale(1,-1); + ctx.translate(0, -imgData.height); + ctx.drawImage(cnv,0,0); + ctx.setTransform(1,0,0,1,0,0); + ctx.globalCompositeOperation = 'source-over'; + + GLctx.bindTexture(GLctx.TEXTURE_2D, dstTexture); + GLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA, GLctx.UNSIGNED_BYTE, cnv); + //GLctx.texSubImage2D(GLctx.TEXTURE_2D, 0, 0, 0, GLctx.RGBA, GLctx.UNSIGNED_BYTE, cnv); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_MAG_FILTER, GLctx.LINEAR); + GLctx.texParameteri(GLctx.TEXTURE_2D, GLctx.TEXTURE_MIN_FILTER, GLctx.LINEAR); + GLctx.generateMipmap(GLctx.TEXTURE_2D); + GLctx.bindTexture(GLctx.TEXTURE_2D, null); + } + }, + +}; +mergeInto(LibraryManager.library, UnityWebRTCVideoStreamTrack); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/VideoStreamTrack.jslib.meta b/Runtime/Plugins/WebGL/VideoStreamTrack.jslib.meta new file mode 100644 index 0000000000..18bd12fb0b --- /dev/null +++ b/Runtime/Plugins/WebGL/VideoStreamTrack.jslib.meta @@ -0,0 +1,44 @@ +fileFormatVersion: 2 +guid: d1d833d3ec5c529469746ebf5753090a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Plugins/WebGL/adapter.jspre b/Runtime/Plugins/WebGL/adapter.jspre new file mode 100644 index 0000000000..a0b3a6319b --- /dev/null +++ b/Runtime/Plugins/WebGL/adapter.jspre @@ -0,0 +1,3423 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0 && arguments[0] !== undefined ? arguments[0] : {}, + window = _ref.window; + + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { + shimChrome: true, + shimFirefox: true, + shimSafari: true + }; + + // Utils. + var logging = utils.log; + var browserDetails = utils.detectBrowser(window); + + var adapter = { + browserDetails: browserDetails, + commonShim: commonShim, + extractVersion: utils.extractVersion, + disableLog: utils.disableLog, + disableWarnings: utils.disableWarnings, + // Expose sdp as a convenience. For production apps include directly. + sdp: sdp + }; + + // Shim browser if found. + switch (browserDetails.browser) { + case 'chrome': + if (!chromeShim || !chromeShim.shimPeerConnection || !options.shimChrome) { + logging('Chrome shim is not included in this adapter release.'); + return adapter; + } + if (browserDetails.version === null) { + logging('Chrome shim can not determine version, not shimming.'); + return adapter; + } + logging('adapter.js shimming chrome.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = chromeShim; + + // Must be called before shimPeerConnection. + commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails); + + chromeShim.shimGetUserMedia(window, browserDetails); + chromeShim.shimMediaStream(window, browserDetails); + chromeShim.shimPeerConnection(window, browserDetails); + chromeShim.shimOnTrack(window, browserDetails); + chromeShim.shimAddTrackRemoveTrack(window, browserDetails); + chromeShim.shimGetSendersWithDtmf(window, browserDetails); + chromeShim.shimGetStats(window, browserDetails); + chromeShim.shimSenderReceiverGetStats(window, browserDetails); + chromeShim.fixNegotiationNeeded(window, browserDetails); + + commonShim.shimRTCIceCandidate(window, browserDetails); + commonShim.shimConnectionState(window, browserDetails); + commonShim.shimMaxMessageSize(window, browserDetails); + commonShim.shimSendThrowTypeError(window, browserDetails); + commonShim.removeExtmapAllowMixed(window, browserDetails); + break; + case 'firefox': + if (!firefoxShim || !firefoxShim.shimPeerConnection || !options.shimFirefox) { + logging('Firefox shim is not included in this adapter release.'); + return adapter; + } + logging('adapter.js shimming firefox.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = firefoxShim; + + // Must be called before shimPeerConnection. + commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails); + + firefoxShim.shimGetUserMedia(window, browserDetails); + firefoxShim.shimPeerConnection(window, browserDetails); + firefoxShim.shimOnTrack(window, browserDetails); + firefoxShim.shimRemoveStream(window, browserDetails); + firefoxShim.shimSenderGetStats(window, browserDetails); + firefoxShim.shimReceiverGetStats(window, browserDetails); + firefoxShim.shimRTCDataChannel(window, browserDetails); + firefoxShim.shimAddTransceiver(window, browserDetails); + firefoxShim.shimGetParameters(window, browserDetails); + firefoxShim.shimCreateOffer(window, browserDetails); + firefoxShim.shimCreateAnswer(window, browserDetails); + + commonShim.shimRTCIceCandidate(window, browserDetails); + commonShim.shimConnectionState(window, browserDetails); + commonShim.shimMaxMessageSize(window, browserDetails); + commonShim.shimSendThrowTypeError(window, browserDetails); + break; + case 'safari': + if (!safariShim || !options.shimSafari) { + logging('Safari shim is not included in this adapter release.'); + return adapter; + } + logging('adapter.js shimming safari.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = safariShim; + + // Must be called before shimCallbackAPI. + commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails); + + safariShim.shimRTCIceServerUrls(window, browserDetails); + safariShim.shimCreateOfferLegacy(window, browserDetails); + safariShim.shimCallbacksAPI(window, browserDetails); + safariShim.shimLocalStreamsAPI(window, browserDetails); + safariShim.shimRemoteStreamsAPI(window, browserDetails); + safariShim.shimTrackEventTransceiver(window, browserDetails); + safariShim.shimGetUserMedia(window, browserDetails); + safariShim.shimAudioContext(window, browserDetails); + + commonShim.shimRTCIceCandidate(window, browserDetails); + commonShim.shimMaxMessageSize(window, browserDetails); + commonShim.shimSendThrowTypeError(window, browserDetails); + commonShim.removeExtmapAllowMixed(window, browserDetails); + break; + default: + logging('Unsupported browser!'); + break; + } + + return adapter; + } + +// Browser shims. + + },{"./chrome/chrome_shim":3,"./common_shim":6,"./firefox/firefox_shim":7,"./safari/safari_shim":10,"./utils":11,"sdp":12}],3:[function(require,module,exports){ + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + var _getusermedia = require('./getusermedia'); + + Object.defineProperty(exports, 'shimGetUserMedia', { + enumerable: true, + get: function get() { + return _getusermedia.shimGetUserMedia; + } + }); + + var _getdisplaymedia = require('./getdisplaymedia'); + + Object.defineProperty(exports, 'shimGetDisplayMedia', { + enumerable: true, + get: function get() { + return _getdisplaymedia.shimGetDisplayMedia; + } + }); + exports.shimMediaStream = shimMediaStream; + exports.shimOnTrack = shimOnTrack; + exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf; + exports.shimGetStats = shimGetStats; + exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats; + exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative; + exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack; + exports.shimPeerConnection = shimPeerConnection; + exports.fixNegotiationNeeded = fixNegotiationNeeded; + + var _utils = require('../utils.js'); + + var utils = _interopRequireWildcard(_utils); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + + function shimMediaStream(window) { + window.MediaStream = window.MediaStream || window.webkitMediaStream; + } + + function shimOnTrack(window) { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', { + get: function get() { + return this._ontrack; + }, + set: function set(f) { + if (this._ontrack) { + this.removeEventListener('track', this._ontrack); + } + this.addEventListener('track', this._ontrack = f); + }, + + enumerable: true, + configurable: true + }); + var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() { + var _this = this; + + if (!this._ontrackpoly) { + this._ontrackpoly = function (e) { + // onaddstream does not fire when a track is added to an existing + // stream. But stream.onaddtrack is implemented so we use that. + e.stream.addEventListener('addtrack', function (te) { + var receiver = void 0; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = _this.getReceivers().find(function (r) { + return r.track && r.track.id === te.track.id; + }); + } else { + receiver = { track: te.track }; + } + + var event = new Event('track'); + event.track = te.track; + event.receiver = receiver; + event.transceiver = { receiver: receiver }; + event.streams = [e.stream]; + _this.dispatchEvent(event); + }); + e.stream.getTracks().forEach(function (track) { + var receiver = void 0; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = _this.getReceivers().find(function (r) { + return r.track && r.track.id === track.id; + }); + } else { + receiver = { track: track }; + } + var event = new Event('track'); + event.track = track; + event.receiver = receiver; + event.transceiver = { receiver: receiver }; + event.streams = [e.stream]; + _this.dispatchEvent(event); + }); + }; + this.addEventListener('addstream', this._ontrackpoly); + } + return origSetRemoteDescription.apply(this, arguments); + }; + } else { + // even if RTCRtpTransceiver is in window, it is only used and + // emitted in unified-plan. Unfortunately this means we need + // to unconditionally wrap the event. + utils.wrapPeerConnectionEvent(window, 'track', function (e) { + if (!e.transceiver) { + Object.defineProperty(e, 'transceiver', { value: { receiver: e.receiver } }); + } + return e; + }); + } + } + + function shimGetSendersWithDtmf(window) { + // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack. + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) { + var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) { + return { + track: track, + get dtmf() { + if (this._dtmf === undefined) { + if (track.kind === 'audio') { + this._dtmf = pc.createDTMFSender(track); + } else { + this._dtmf = null; + } + } + return this._dtmf; + }, + _pc: pc + }; + }; + + // augment addTrack when getSenders is not available. + if (!window.RTCPeerConnection.prototype.getSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + this._senders = this._senders || []; + return this._senders.slice(); // return a copy of the internal state. + }; + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) { + var sender = origAddTrack.apply(this, arguments); + if (!sender) { + sender = shimSenderWithDtmf(this, track); + this._senders.push(sender); + } + return sender; + }; + + var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; + window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) { + origRemoveTrack.apply(this, arguments); + var idx = this._senders.indexOf(sender); + if (idx !== -1) { + this._senders.splice(idx, 1); + } + }; + } + var origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this2 = this; + + this._senders = this._senders || []; + origAddStream.apply(this, [stream]); + stream.getTracks().forEach(function (track) { + _this2._senders.push(shimSenderWithDtmf(_this2, track)); + }); + }; + + var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + var _this3 = this; + + this._senders = this._senders || []; + origRemoveStream.apply(this, [stream]); + + stream.getTracks().forEach(function (track) { + var sender = _this3._senders.find(function (s) { + return s.track === track; + }); + if (sender) { + // remove sender + _this3._senders.splice(_this3._senders.indexOf(sender), 1); + } + }); + }; + } else if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && 'getSenders' in window.RTCPeerConnection.prototype && 'createDTMFSender' in window.RTCPeerConnection.prototype && window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) { + var origGetSenders = window.RTCPeerConnection.prototype.getSenders; + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + var _this4 = this; + + var senders = origGetSenders.apply(this, []); + senders.forEach(function (sender) { + return sender._pc = _this4; + }); + return senders; + }; + + Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', { + get: function get() { + if (this._dtmf === undefined) { + if (this.track.kind === 'audio') { + this._dtmf = this._pc.createDTMFSender(this.track); + } else { + this._dtmf = null; + } + } + return this._dtmf; + } + }); + } + } + + function shimGetStats(window) { + if (!window.RTCPeerConnection) { + return; + } + + var origGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + var _this5 = this; + + var _arguments = Array.prototype.slice.call(arguments), + selector = _arguments[0], + onSucc = _arguments[1], + onErr = _arguments[2]; + + // If selector is a function then we are in the old style stats so just + // pass back the original getStats format to avoid breaking old users. + + + if (arguments.length > 0 && typeof selector === 'function') { + return origGetStats.apply(this, arguments); + } + + // When spec-style getStats is supported, return those when called with + // either no arguments or the selector argument is null. + if (origGetStats.length === 0 && (arguments.length === 0 || typeof selector !== 'function')) { + return origGetStats.apply(this, []); + } + + var fixChromeStats_ = function fixChromeStats_(response) { + var standardReport = {}; + var reports = response.result(); + reports.forEach(function (report) { + var standardStats = { + id: report.id, + timestamp: report.timestamp, + type: { + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }[report.type] || report.type + }; + report.names().forEach(function (name) { + standardStats[name] = report.stat(name); + }); + standardReport[standardStats.id] = standardStats; + }); + + return standardReport; + }; + + // shim getStats with maplike support + var makeMapStats = function makeMapStats(stats) { + return new Map(Object.keys(stats).map(function (key) { + return [key, stats[key]]; + })); + }; + + if (arguments.length >= 2) { + var successCallbackWrapper_ = function successCallbackWrapper_(response) { + onSucc(makeMapStats(fixChromeStats_(response))); + }; + + return origGetStats.apply(this, [successCallbackWrapper_, selector]); + } + + // promise-support + return new Promise(function (resolve, reject) { + origGetStats.apply(_this5, [function (response) { + resolve(makeMapStats(fixChromeStats_(response))); + }, reject]); + }).then(onSucc, onErr); + }; + } + + function shimSenderReceiverGetStats(window) { + if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) { + return; + } + + // shim sender stats. + if (!('getStats' in window.RTCRtpSender.prototype)) { + var origGetSenders = window.RTCPeerConnection.prototype.getSenders; + if (origGetSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + var _this6 = this; + + var senders = origGetSenders.apply(this, []); + senders.forEach(function (sender) { + return sender._pc = _this6; + }); + return senders; + }; + } + + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + if (origAddTrack) { + window.RTCPeerConnection.prototype.addTrack = function addTrack() { + var sender = origAddTrack.apply(this, arguments); + sender._pc = this; + return sender; + }; + } + window.RTCRtpSender.prototype.getStats = function getStats() { + var sender = this; + return this._pc.getStats().then(function (result) { + return ( + /* Note: this will include stats of all senders that + * send a track with the same id as sender.track as + * it is not possible to identify the RTCRtpSender. + */ + utils.filterStats(result, sender.track, true) + ); + }); + }; + } + + // shim receiver stats. + if (!('getStats' in window.RTCRtpReceiver.prototype)) { + var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; + if (origGetReceivers) { + window.RTCPeerConnection.prototype.getReceivers = function getReceivers() { + var _this7 = this; + + var receivers = origGetReceivers.apply(this, []); + receivers.forEach(function (receiver) { + return receiver._pc = _this7; + }); + return receivers; + }; + } + utils.wrapPeerConnectionEvent(window, 'track', function (e) { + e.receiver._pc = e.srcElement; + return e; + }); + window.RTCRtpReceiver.prototype.getStats = function getStats() { + var receiver = this; + return this._pc.getStats().then(function (result) { + return utils.filterStats(result, receiver.track, false); + }); + }; + } + + if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) { + return; + } + + // shim RTCPeerConnection.getStats(track). + var origGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) { + var track = arguments[0]; + var sender = void 0; + var receiver = void 0; + var err = void 0; + this.getSenders().forEach(function (s) { + if (s.track === track) { + if (sender) { + err = true; + } else { + sender = s; + } + } + }); + this.getReceivers().forEach(function (r) { + if (r.track === track) { + if (receiver) { + err = true; + } else { + receiver = r; + } + } + return r.track === track; + }); + if (err || sender && receiver) { + return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError')); + } else if (sender) { + return sender.getStats(); + } else if (receiver) { + return receiver.getStats(); + } + return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError')); + } + return origGetStats.apply(this, arguments); + }; + } + + function shimAddTrackRemoveTrackWithNative(window) { + // shim addTrack/removeTrack with native variants in order to make + // the interactions with legacy getLocalStreams behave as in other browsers. + // Keeps a mapping stream.id => [stream, rtpsenders...] + window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() { + var _this8 = this; + + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + return Object.keys(this._shimmedLocalStreams).map(function (streamId) { + return _this8._shimmedLocalStreams[streamId][0]; + }); + }; + + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) { + if (!stream) { + return origAddTrack.apply(this, arguments); + } + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + + var sender = origAddTrack.apply(this, arguments); + if (!this._shimmedLocalStreams[stream.id]) { + this._shimmedLocalStreams[stream.id] = [stream, sender]; + } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) { + this._shimmedLocalStreams[stream.id].push(sender); + } + return sender; + }; + + var origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this9 = this; + + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + + stream.getTracks().forEach(function (track) { + var alreadyExists = _this9.getSenders().find(function (s) { + return s.track === track; + }); + if (alreadyExists) { + throw new DOMException('Track already exists.', 'InvalidAccessError'); + } + }); + var existingSenders = this.getSenders(); + origAddStream.apply(this, arguments); + var newSenders = this.getSenders().filter(function (newSender) { + return existingSenders.indexOf(newSender) === -1; + }); + this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders); + }; + + var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + delete this._shimmedLocalStreams[stream.id]; + return origRemoveStream.apply(this, arguments); + }; + + var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; + window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) { + var _this10 = this; + + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + if (sender) { + Object.keys(this._shimmedLocalStreams).forEach(function (streamId) { + var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender); + if (idx !== -1) { + _this10._shimmedLocalStreams[streamId].splice(idx, 1); + } + if (_this10._shimmedLocalStreams[streamId].length === 1) { + delete _this10._shimmedLocalStreams[streamId]; + } + }); + } + return origRemoveTrack.apply(this, arguments); + }; + } + + function shimAddTrackRemoveTrack(window, browserDetails) { + if (!window.RTCPeerConnection) { + return; + } + // shim addTrack and removeTrack. + if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) { + return shimAddTrackRemoveTrackWithNative(window); + } + + // also shim pc.getLocalStreams when addTrack is shimmed + // to return the original streams. + var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams; + window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() { + var _this11 = this; + + var nativeStreams = origGetLocalStreams.apply(this); + this._reverseStreams = this._reverseStreams || {}; + return nativeStreams.map(function (stream) { + return _this11._reverseStreams[stream.id]; + }); + }; + + var origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this12 = this; + + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + + stream.getTracks().forEach(function (track) { + var alreadyExists = _this12.getSenders().find(function (s) { + return s.track === track; + }); + if (alreadyExists) { + throw new DOMException('Track already exists.', 'InvalidAccessError'); + } + }); + // Add identity mapping for consistency with addTrack. + // Unless this is being used with a stream from addTrack. + if (!this._reverseStreams[stream.id]) { + var newStream = new window.MediaStream(stream.getTracks()); + this._streams[stream.id] = newStream; + this._reverseStreams[newStream.id] = stream; + stream = newStream; + } + origAddStream.apply(this, [stream]); + }; + + var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + + origRemoveStream.apply(this, [this._streams[stream.id] || stream]); + delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id]; + delete this._streams[stream.id]; + }; + + window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) { + var _this13 = this; + + if (this.signalingState === 'closed') { + throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError'); + } + var streams = [].slice.call(arguments, 1); + if (streams.length !== 1 || !streams[0].getTracks().find(function (t) { + return t === track; + })) { + // this is not fully correct but all we can manage without + // [[associated MediaStreams]] internal slot. + throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError'); + } + + var alreadyExists = this.getSenders().find(function (s) { + return s.track === track; + }); + if (alreadyExists) { + throw new DOMException('Track already exists.', 'InvalidAccessError'); + } + + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + var oldStream = this._streams[stream.id]; + if (oldStream) { + // this is using odd Chrome behaviour, use with caution: + // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815 + // Note: we rely on the high-level addTrack/dtmf shim to + // create the sender with a dtmf sender. + oldStream.addTrack(track); + + // Trigger ONN async. + Promise.resolve().then(function () { + _this13.dispatchEvent(new Event('negotiationneeded')); + }); + } else { + var newStream = new window.MediaStream([track]); + this._streams[stream.id] = newStream; + this._reverseStreams[newStream.id] = stream; + this.addStream(newStream); + } + return this.getSenders().find(function (s) { + return s.track === track; + }); + }; + + // replace the internal stream id with the external one and + // vice versa. + function replaceInternalStreamId(pc, description) { + var sdp = description.sdp; + Object.keys(pc._reverseStreams || []).forEach(function (internalId) { + var externalStream = pc._reverseStreams[internalId]; + var internalStream = pc._streams[externalStream.id]; + sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id); + }); + return new RTCSessionDescription({ + type: description.type, + sdp: sdp + }); + } + function replaceExternalStreamId(pc, description) { + var sdp = description.sdp; + Object.keys(pc._reverseStreams || []).forEach(function (internalId) { + var externalStream = pc._reverseStreams[internalId]; + var internalStream = pc._streams[externalStream.id]; + sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id); + }); + return new RTCSessionDescription({ + type: description.type, + sdp: sdp + }); + } + ['createOffer', 'createAnswer'].forEach(function (method) { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + var methodObj = _defineProperty({}, method, function () { + var _this14 = this; + + var args = arguments; + var isLegacyCall = arguments.length && typeof arguments[0] === 'function'; + if (isLegacyCall) { + return nativeMethod.apply(this, [function (description) { + var desc = replaceInternalStreamId(_this14, description); + args[0].apply(null, [desc]); + }, function (err) { + if (args[1]) { + args[1].apply(null, err); + } + }, arguments[2]]); + } + return nativeMethod.apply(this, arguments).then(function (description) { + return replaceInternalStreamId(_this14, description); + }); + }); + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + + var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription; + window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() { + if (!arguments.length || !arguments[0].type) { + return origSetLocalDescription.apply(this, arguments); + } + arguments[0] = replaceExternalStreamId(this, arguments[0]); + return origSetLocalDescription.apply(this, arguments); + }; + + // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier + + var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription'); + Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', { + get: function get() { + var description = origLocalDescription.get.apply(this); + if (description.type === '') { + return description; + } + return replaceInternalStreamId(this, description); + } + }); + + window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) { + var _this15 = this; + + if (this.signalingState === 'closed') { + throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError'); + } + // We can not yet check for sender instanceof RTCRtpSender + // since we shim RTPSender. So we check if sender._pc is set. + if (!sender._pc) { + throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError'); + } + var isLocal = sender._pc === this; + if (!isLocal) { + throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError'); + } + + // Search for the native stream the senders track belongs to. + this._streams = this._streams || {}; + var stream = void 0; + Object.keys(this._streams).forEach(function (streamid) { + var hasTrack = _this15._streams[streamid].getTracks().find(function (track) { + return sender.track === track; + }); + if (hasTrack) { + stream = _this15._streams[streamid]; + } + }); + + if (stream) { + if (stream.getTracks().length === 1) { + // if this is the last track of the stream, remove the stream. This + // takes care of any shimmed _senders. + this.removeStream(this._reverseStreams[stream.id]); + } else { + // relying on the same odd chrome behaviour as above. + stream.removeTrack(sender.track); + } + this.dispatchEvent(new Event('negotiationneeded')); + } + }; + } + + function shimPeerConnection(window, browserDetails) { + if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) { + // very basic support for old versions. + window.RTCPeerConnection = window.webkitRTCPeerConnection; + } + if (!window.RTCPeerConnection) { + return; + } + + // shim implicit creation of RTCSessionDescription/RTCIceCandidate + if (browserDetails.version < 53) { + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + var methodObj = _defineProperty({}, method, function () { + arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]); + return nativeMethod.apply(this, arguments); + }); + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + } + } + +// Attempt to fix ONN in plan-b mode. + function fixNegotiationNeeded(window, browserDetails) { + utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) { + var pc = e.target; + if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') { + if (pc.signalingState !== 'stable') { + return; + } + } + return e; + }); + } + + },{"../utils.js":11,"./getdisplaymedia":4,"./getusermedia":5}],4:[function(require,module,exports){ + /* + * Copyright (c) 2018 The adapter.js 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. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.shimGetDisplayMedia = shimGetDisplayMedia; + function shimGetDisplayMedia(window, getSourceId) { + if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) { + return; + } + if (!window.navigator.mediaDevices) { + return; + } + // getSourceId is a function that returns a promise resolving with + // the sourceId of the screen/window/tab to be shared. + if (typeof getSourceId !== 'function') { + console.error('shimGetDisplayMedia: getSourceId argument is not ' + 'a function'); + return; + } + window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) { + return getSourceId(constraints).then(function (sourceId) { + var widthSpecified = constraints.video && constraints.video.width; + var heightSpecified = constraints.video && constraints.video.height; + var frameRateSpecified = constraints.video && constraints.video.frameRate; + constraints.video = { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sourceId, + maxFrameRate: frameRateSpecified || 3 + } + }; + if (widthSpecified) { + constraints.video.mandatory.maxWidth = widthSpecified; + } + if (heightSpecified) { + constraints.video.mandatory.maxHeight = heightSpecified; + } + return window.navigator.mediaDevices.getUserMedia(constraints); + }); + }; + } + + },{}],5:[function(require,module,exports){ + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.shimGetUserMedia = shimGetUserMedia; + + var _utils = require('../utils.js'); + + var utils = _interopRequireWildcard(_utils); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + var logging = utils.log; + + function shimGetUserMedia(window, browserDetails) { + var navigator = window && window.navigator; + + if (!navigator.mediaDevices) { + return; + } + + var constraintsToChrome_ = function constraintsToChrome_(c) { + if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) !== 'object' || c.mandatory || c.optional) { + return c; + } + var cc = {}; + Object.keys(c).forEach(function (key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = _typeof(c[key]) === 'object' ? c[key] : { ideal: c[key] }; + if (r.exact !== undefined && typeof r.exact === 'number') { + r.min = r.max = r.exact; + } + var oldname_ = function oldname_(prefix, name) { + if (prefix) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + } + return name === 'deviceId' ? 'sourceId' : name; + }; + if (r.ideal !== undefined) { + cc.optional = cc.optional || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[oldname_('min', key)] = r.ideal; + cc.optional.push(oc); + oc = {}; + oc[oldname_('max', key)] = r.ideal; + cc.optional.push(oc); + } else { + oc[oldname_('', key)] = r.ideal; + cc.optional.push(oc); + } + } + if (r.exact !== undefined && typeof r.exact !== 'number') { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname_('', key)] = r.exact; + } else { + ['min', 'max'].forEach(function (mix) { + if (r[mix] !== undefined) { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname_(mix, key)] = r[mix]; + } + }); + } + }); + if (c.advanced) { + cc.optional = (cc.optional || []).concat(c.advanced); + } + return cc; + }; + + var shimConstraints_ = function shimConstraints_(constraints, func) { + if (browserDetails.version >= 61) { + return func(constraints); + } + constraints = JSON.parse(JSON.stringify(constraints)); + if (constraints && _typeof(constraints.audio) === 'object') { + var remap = function remap(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + constraints = JSON.parse(JSON.stringify(constraints)); + remap(constraints.audio, 'autoGainControl', 'googAutoGainControl'); + remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression'); + constraints.audio = constraintsToChrome_(constraints.audio); + } + if (constraints && _typeof(constraints.video) === 'object') { + // Shim facingMode for mobile & surface pro. + var face = constraints.video.facingMode; + face = face && ((typeof face === 'undefined' ? 'undefined' : _typeof(face)) === 'object' ? face : { ideal: face }); + var getSupportedFacingModeLies = browserDetails.version < 66; + + if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode && !getSupportedFacingModeLies)) { + delete constraints.video.facingMode; + var matches = void 0; + if (face.exact === 'environment' || face.ideal === 'environment') { + matches = ['back', 'rear']; + } else if (face.exact === 'user' || face.ideal === 'user') { + matches = ['front']; + } + if (matches) { + // Look for matches in label, or use last cam for back (typical). + return navigator.mediaDevices.enumerateDevices().then(function (devices) { + devices = devices.filter(function (d) { + return d.kind === 'videoinput'; + }); + var dev = devices.find(function (d) { + return matches.some(function (match) { + return d.label.toLowerCase().includes(match); + }); + }); + if (!dev && devices.length && matches.includes('back')) { + dev = devices[devices.length - 1]; // more likely the back cam + } + if (dev) { + constraints.video.deviceId = face.exact ? { exact: dev.deviceId } : { ideal: dev.deviceId }; + } + constraints.video = constraintsToChrome_(constraints.video); + logging('chrome: ' + JSON.stringify(constraints)); + return func(constraints); + }); + } + } + constraints.video = constraintsToChrome_(constraints.video); + } + logging('chrome: ' + JSON.stringify(constraints)); + return func(constraints); + }; + + var shimError_ = function shimError_(e) { + if (browserDetails.version >= 64) { + return e; + } + return { + name: { + PermissionDeniedError: 'NotAllowedError', + PermissionDismissedError: 'NotAllowedError', + InvalidStateError: 'NotAllowedError', + DevicesNotFoundError: 'NotFoundError', + ConstraintNotSatisfiedError: 'OverconstrainedError', + TrackStartError: 'NotReadableError', + MediaDeviceFailedDueToShutdown: 'NotAllowedError', + MediaDeviceKillSwitchOn: 'NotAllowedError', + TabCaptureError: 'AbortError', + ScreenCaptureError: 'AbortError', + DeviceCaptureError: 'AbortError' + }[e.name] || e.name, + message: e.message, + constraint: e.constraint || e.constraintName, + toString: function toString() { + return this.name + (this.message && ': ') + this.message; + } + }; + }; + + var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) { + shimConstraints_(constraints, function (c) { + navigator.webkitGetUserMedia(c, onSuccess, function (e) { + if (onError) { + onError(shimError_(e)); + } + }); + }); + }; + navigator.getUserMedia = getUserMedia_.bind(navigator); + + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia + // function which returns a Promise, it does not accept spec-style + // constraints. + if (navigator.mediaDevices.getUserMedia) { + var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function (cs) { + return shimConstraints_(cs, function (c) { + return origGetUserMedia(c).then(function (stream) { + if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) { + stream.getTracks().forEach(function (track) { + track.stop(); + }); + throw new DOMException('', 'NotFoundError'); + } + return stream; + }, function (e) { + return Promise.reject(shimError_(e)); + }); + }); + }; + } + } + + },{"../utils.js":11}],6:[function(require,module,exports){ + /* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.shimRTCIceCandidate = shimRTCIceCandidate; + exports.shimMaxMessageSize = shimMaxMessageSize; + exports.shimSendThrowTypeError = shimSendThrowTypeError; + exports.shimConnectionState = shimConnectionState; + exports.removeExtmapAllowMixed = removeExtmapAllowMixed; + exports.shimAddIceCandidateNullOrEmpty = shimAddIceCandidateNullOrEmpty; + + var _sdp = require('sdp'); + + var _sdp2 = _interopRequireDefault(_sdp); + + var _utils = require('./utils'); + + var utils = _interopRequireWildcard(_utils); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + function shimRTCIceCandidate(window) { + // foundation is arbitrarily chosen as an indicator for full support for + // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface + if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) { + return; + } + + var NativeRTCIceCandidate = window.RTCIceCandidate; + window.RTCIceCandidate = function RTCIceCandidate(args) { + // Remove the a= which shouldn't be part of the candidate string. + if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) { + args = JSON.parse(JSON.stringify(args)); + args.candidate = args.candidate.substr(2); + } + + if (args.candidate && args.candidate.length) { + // Augment the native candidate with the parsed fields. + var nativeCandidate = new NativeRTCIceCandidate(args); + var parsedCandidate = _sdp2.default.parseCandidate(args.candidate); + var augmentedCandidate = Object.assign(nativeCandidate, parsedCandidate); + + // Add a serializer that does not serialize the extra attributes. + augmentedCandidate.toJSON = function toJSON() { + return { + candidate: augmentedCandidate.candidate, + sdpMid: augmentedCandidate.sdpMid, + sdpMLineIndex: augmentedCandidate.sdpMLineIndex, + usernameFragment: augmentedCandidate.usernameFragment + }; + }; + return augmentedCandidate; + } + return new NativeRTCIceCandidate(args); + }; + window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype; + + // Hook up the augmented candidate in onicecandidate and + // addEventListener('icecandidate', ...) + utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) { + if (e.candidate) { + Object.defineProperty(e, 'candidate', { + value: new window.RTCIceCandidate(e.candidate), + writable: 'false' + }); + } + return e; + }); + } + + function shimMaxMessageSize(window, browserDetails) { + if (!window.RTCPeerConnection) { + return; + } + + if (!('sctp' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', { + get: function get() { + return typeof this._sctp === 'undefined' ? null : this._sctp; + } + }); + } + + var sctpInDescription = function sctpInDescription(description) { + if (!description || !description.sdp) { + return false; + } + var sections = _sdp2.default.splitSections(description.sdp); + sections.shift(); + return sections.some(function (mediaSection) { + var mLine = _sdp2.default.parseMLine(mediaSection); + return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1; + }); + }; + + var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) { + // TODO: Is there a better solution for detecting Firefox? + var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/); + if (match === null || match.length < 2) { + return -1; + } + var version = parseInt(match[1], 10); + // Test for NaN (yes, this is ugly) + return version !== version ? -1 : version; + }; + + var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) { + // Every implementation we know can send at least 64 KiB. + // Note: Although Chrome is technically able to send up to 256 KiB, the + // data does not reach the other peer reliably. + // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419 + var canSendMaxMessageSize = 65536; + if (browserDetails.browser === 'firefox') { + if (browserDetails.version < 57) { + if (remoteIsFirefox === -1) { + // FF < 57 will send in 16 KiB chunks using the deprecated PPID + // fragmentation. + canSendMaxMessageSize = 16384; + } else { + // However, other FF (and RAWRTC) can reassemble PPID-fragmented + // messages. Thus, supporting ~2 GiB when sending. + canSendMaxMessageSize = 2147483637; + } + } else if (browserDetails.version < 60) { + // Currently, all FF >= 57 will reset the remote maximum message size + // to the default value when a data channel is created at a later + // stage. :( + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 + canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536; + } else { + // FF >= 60 supports sending ~2 GiB + canSendMaxMessageSize = 2147483637; + } + } + return canSendMaxMessageSize; + }; + + var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) { + // Note: 65536 bytes is the default value from the SDP spec. Also, + // every implementation we know supports receiving 65536 bytes. + var maxMessageSize = 65536; + + // FF 57 has a slightly incorrect default remote max message size, so + // we need to adjust it here to avoid a failure when sending. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697 + if (browserDetails.browser === 'firefox' && browserDetails.version === 57) { + maxMessageSize = 65535; + } + + var match = _sdp2.default.matchPrefix(description.sdp, 'a=max-message-size:'); + if (match.length > 0) { + maxMessageSize = parseInt(match[0].substr(19), 10); + } else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) { + // If the maximum message size is not present in the remote SDP and + // both local and remote are Firefox, the remote peer can receive + // ~2 GiB. + maxMessageSize = 2147483637; + } + return maxMessageSize; + }; + + var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() { + this._sctp = null; + // Chrome decided to not expose .sctp in plan-b mode. + // As usual, adapter.js has to do an 'ugly worakaround' + // to cover up the mess. + if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) { + var _getConfiguration = this.getConfiguration(), + sdpSemantics = _getConfiguration.sdpSemantics; + + if (sdpSemantics === 'plan-b') { + Object.defineProperty(this, 'sctp', { + get: function get() { + return typeof this._sctp === 'undefined' ? null : this._sctp; + }, + + enumerable: true, + configurable: true + }); + } + } + + if (sctpInDescription(arguments[0])) { + // Check if the remote is FF. + var isFirefox = getRemoteFirefoxVersion(arguments[0]); + + // Get the maximum message size the local peer is capable of sending + var canSendMMS = getCanSendMaxMessageSize(isFirefox); + + // Get the maximum message size of the remote peer. + var remoteMMS = getMaxMessageSize(arguments[0], isFirefox); + + // Determine final maximum message size + var maxMessageSize = void 0; + if (canSendMMS === 0 && remoteMMS === 0) { + maxMessageSize = Number.POSITIVE_INFINITY; + } else if (canSendMMS === 0 || remoteMMS === 0) { + maxMessageSize = Math.max(canSendMMS, remoteMMS); + } else { + maxMessageSize = Math.min(canSendMMS, remoteMMS); + } + + // Create a dummy RTCSctpTransport object and the 'maxMessageSize' + // attribute. + var sctp = {}; + Object.defineProperty(sctp, 'maxMessageSize', { + get: function get() { + return maxMessageSize; + } + }); + this._sctp = sctp; + } + + return origSetRemoteDescription.apply(this, arguments); + }; + } + + function shimSendThrowTypeError(window) { + if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) { + return; + } + + // Note: Although Firefox >= 57 has a native implementation, the maximum + // message size can be reset for all data channels at a later stage. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 + + function wrapDcSend(dc, pc) { + var origDataChannelSend = dc.send; + dc.send = function send() { + var data = arguments[0]; + var length = data.length || data.size || data.byteLength; + if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) { + throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)'); + } + return origDataChannelSend.apply(dc, arguments); + }; + } + var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel; + window.RTCPeerConnection.prototype.createDataChannel = function createDataChannel() { + var dataChannel = origCreateDataChannel.apply(this, arguments); + wrapDcSend(dataChannel, this); + return dataChannel; + }; + utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) { + wrapDcSend(e.channel, e.target); + return e; + }); + } + + /* shims RTCConnectionState by pretending it is the same as iceConnectionState. + * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12 + * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect + * since DTLS failures would be hidden. See + * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827 + * for the Firefox tracking bug. + */ + function shimConnectionState(window) { + if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) { + return; + } + var proto = window.RTCPeerConnection.prototype; + Object.defineProperty(proto, 'connectionState', { + get: function get() { + return { + completed: 'connected', + checking: 'connecting' + }[this.iceConnectionState] || this.iceConnectionState; + }, + + enumerable: true, + configurable: true + }); + Object.defineProperty(proto, 'onconnectionstatechange', { + get: function get() { + return this._onconnectionstatechange || null; + }, + set: function set(cb) { + if (this._onconnectionstatechange) { + this.removeEventListener('connectionstatechange', this._onconnectionstatechange); + delete this._onconnectionstatechange; + } + if (cb) { + this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb); + } + }, + + enumerable: true, + configurable: true + }); + + ['setLocalDescription', 'setRemoteDescription'].forEach(function (method) { + var origMethod = proto[method]; + proto[method] = function () { + if (!this._connectionstatechangepoly) { + this._connectionstatechangepoly = function (e) { + var pc = e.target; + if (pc._lastConnectionState !== pc.connectionState) { + pc._lastConnectionState = pc.connectionState; + var newEvent = new Event('connectionstatechange', e); + pc.dispatchEvent(newEvent); + } + return e; + }; + this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly); + } + return origMethod.apply(this, arguments); + }; + }); + } + + function removeExtmapAllowMixed(window, browserDetails) { + /* remove a=extmap-allow-mixed for webrtc.org < M71 */ + if (!window.RTCPeerConnection) { + return; + } + if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) { + return; + } + if (browserDetails.browser === 'safari' && browserDetails.version >= 605) { + return; + } + var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(desc) { + if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) { + var sdp = desc.sdp.split('\n').filter(function (line) { + return line.trim() !== 'a=extmap-allow-mixed'; + }).join('\n'); + // Safari enforces read-only-ness of RTCSessionDescription fields. + if (window.RTCSessionDescription && desc instanceof window.RTCSessionDescription) { + arguments[0] = new window.RTCSessionDescription({ + type: desc.type, + sdp: sdp + }); + } else { + desc.sdp = sdp; + } + } + return nativeSRD.apply(this, arguments); + }; + } + + function shimAddIceCandidateNullOrEmpty(window, browserDetails) { + // Support for addIceCandidate(null or undefined) + // as well as addIceCandidate({candidate: "", ...}) + // https://bugs.chromium.org/p/chromium/issues/detail?id=978582 + // Note: must be called before other polyfills which change the signature. + if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) { + return; + } + var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate; + if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) { + return; + } + window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() { + if (!arguments[0]) { + if (arguments[1]) { + arguments[1].apply(null); + } + return Promise.resolve(); + } + // Firefox 68+ emits and processes {candidate: "", ...}, ignore + // in older versions. + // Native support for ignoring exists for Chrome M77+. + // Safari ignores as well, exact version unknown but works in the same + // version that also ignores addIceCandidate(null). + if ((browserDetails.browser === 'chrome' && browserDetails.version < 78 || browserDetails.browser === 'firefox' && browserDetails.version < 68 || browserDetails.browser === 'safari') && arguments[0] && arguments[0].candidate === '') { + return Promise.resolve(); + } + return nativeAddIceCandidate.apply(this, arguments); + }; + } + + },{"./utils":11,"sdp":12}],7:[function(require,module,exports){ + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + var _getusermedia = require('./getusermedia'); + + Object.defineProperty(exports, 'shimGetUserMedia', { + enumerable: true, + get: function get() { + return _getusermedia.shimGetUserMedia; + } + }); + + var _getdisplaymedia = require('./getdisplaymedia'); + + Object.defineProperty(exports, 'shimGetDisplayMedia', { + enumerable: true, + get: function get() { + return _getdisplaymedia.shimGetDisplayMedia; + } + }); + exports.shimOnTrack = shimOnTrack; + exports.shimPeerConnection = shimPeerConnection; + exports.shimSenderGetStats = shimSenderGetStats; + exports.shimReceiverGetStats = shimReceiverGetStats; + exports.shimRemoveStream = shimRemoveStream; + exports.shimRTCDataChannel = shimRTCDataChannel; + exports.shimAddTransceiver = shimAddTransceiver; + exports.shimGetParameters = shimGetParameters; + exports.shimCreateOffer = shimCreateOffer; + exports.shimCreateAnswer = shimCreateAnswer; + + var _utils = require('../utils'); + + var utils = _interopRequireWildcard(_utils); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + + function shimOnTrack(window) { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) { + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { + get: function get() { + return { receiver: this.receiver }; + } + }); + } + } + + function shimPeerConnection(window, browserDetails) { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) { + return; // probably media.peerconnection.enabled=false in about:config + } + if (!window.RTCPeerConnection && window.mozRTCPeerConnection) { + // very basic support for old versions. + window.RTCPeerConnection = window.mozRTCPeerConnection; + } + + if (browserDetails.version < 53) { + // shim away need for obsolete RTCIceCandidate/RTCSessionDescription. + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + var methodObj = _defineProperty({}, method, function () { + arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]); + return nativeMethod.apply(this, arguments); + }); + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + } + + var modernStatsTypes = { + inboundrtp: 'inbound-rtp', + outboundrtp: 'outbound-rtp', + candidatepair: 'candidate-pair', + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }; + + var nativeGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + var _arguments = Array.prototype.slice.call(arguments), + selector = _arguments[0], + onSucc = _arguments[1], + onErr = _arguments[2]; + + return nativeGetStats.apply(this, [selector || null]).then(function (stats) { + if (browserDetails.version < 53 && !onSucc) { + // Shim only promise getStats with spec-hyphens in type names + // Leave callback version alone; misc old uses of forEach before Map + try { + stats.forEach(function (stat) { + stat.type = modernStatsTypes[stat.type] || stat.type; + }); + } catch (e) { + if (e.name !== 'TypeError') { + throw e; + } + // Avoid TypeError: "type" is read-only, in old versions. 34-43ish + stats.forEach(function (stat, i) { + stats.set(i, Object.assign({}, stat, { + type: modernStatsTypes[stat.type] || stat.type + })); + }); + } + } + return stats; + }).then(onSucc, onErr); + }; + } + + function shimSenderGetStats(window) { + if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) { + return; + } + if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) { + return; + } + var origGetSenders = window.RTCPeerConnection.prototype.getSenders; + if (origGetSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + var _this = this; + + var senders = origGetSenders.apply(this, []); + senders.forEach(function (sender) { + return sender._pc = _this; + }); + return senders; + }; + } + + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + if (origAddTrack) { + window.RTCPeerConnection.prototype.addTrack = function addTrack() { + var sender = origAddTrack.apply(this, arguments); + sender._pc = this; + return sender; + }; + } + window.RTCRtpSender.prototype.getStats = function getStats() { + return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map()); + }; + } + + function shimReceiverGetStats(window) { + if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) { + return; + } + if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) { + return; + } + var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; + if (origGetReceivers) { + window.RTCPeerConnection.prototype.getReceivers = function getReceivers() { + var _this2 = this; + + var receivers = origGetReceivers.apply(this, []); + receivers.forEach(function (receiver) { + return receiver._pc = _this2; + }); + return receivers; + }; + } + utils.wrapPeerConnectionEvent(window, 'track', function (e) { + e.receiver._pc = e.srcElement; + return e; + }); + window.RTCRtpReceiver.prototype.getStats = function getStats() { + return this._pc.getStats(this.track); + }; + } + + function shimRemoveStream(window) { + if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) { + return; + } + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + var _this3 = this; + + utils.deprecated('removeStream', 'removeTrack'); + this.getSenders().forEach(function (sender) { + if (sender.track && stream.getTracks().includes(sender.track)) { + _this3.removeTrack(sender); + } + }); + }; + } + + function shimRTCDataChannel(window) { + // rename DataChannel to RTCDataChannel (native fix in FF60): + // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851 + if (window.DataChannel && !window.RTCDataChannel) { + window.RTCDataChannel = window.DataChannel; + } + } + + function shimAddTransceiver(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) { + return; + } + var origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver; + if (origAddTransceiver) { + window.RTCPeerConnection.prototype.addTransceiver = function addTransceiver() { + this.setParametersPromises = []; + var initParameters = arguments[1]; + var shouldPerformCheck = initParameters && 'sendEncodings' in initParameters; + if (shouldPerformCheck) { + // If sendEncodings params are provided, validate grammar + initParameters.sendEncodings.forEach(function (encodingParam) { + if ('rid' in encodingParam) { + var ridRegex = /^[a-z0-9]{0,16}$/i; + if (!ridRegex.test(encodingParam.rid)) { + throw new TypeError('Invalid RID value provided.'); + } + } + if ('scaleResolutionDownBy' in encodingParam) { + if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) { + throw new RangeError('scale_resolution_down_by must be >= 1.0'); + } + } + if ('maxFramerate' in encodingParam) { + if (!(parseFloat(encodingParam.maxFramerate) >= 0)) { + throw new RangeError('max_framerate must be >= 0.0'); + } + } + }); + } + var transceiver = origAddTransceiver.apply(this, arguments); + if (shouldPerformCheck) { + // Check if the init options were applied. If not we do this in an + // asynchronous way and save the promise reference in a global object. + // This is an ugly hack, but at the same time is way more robust than + // checking the sender parameters before and after the createOffer + // Also note that after the createoffer we are not 100% sure that + // the params were asynchronously applied so we might miss the + // opportunity to recreate offer. + var sender = transceiver.sender; + + var params = sender.getParameters(); + if (!('encodings' in params) || + // Avoid being fooled by patched getParameters() below. + params.encodings.length === 1 && Object.keys(params.encodings[0]).length === 0) { + params.encodings = initParameters.sendEncodings; + sender.sendEncodings = initParameters.sendEncodings; + this.setParametersPromises.push(sender.setParameters(params).then(function () { + delete sender.sendEncodings; + }).catch(function () { + delete sender.sendEncodings; + })); + } + } + return transceiver; + }; + } + } + + function shimGetParameters(window) { + if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCRtpSender)) { + return; + } + var origGetParameters = window.RTCRtpSender.prototype.getParameters; + if (origGetParameters) { + window.RTCRtpSender.prototype.getParameters = function getParameters() { + var params = origGetParameters.apply(this, arguments); + if (!('encodings' in params)) { + params.encodings = [].concat(this.sendEncodings || [{}]); + } + return params; + }; + } + } + + function shimCreateOffer(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) { + return; + } + var origCreateOffer = window.RTCPeerConnection.prototype.createOffer; + window.RTCPeerConnection.prototype.createOffer = function createOffer() { + var _this4 = this, + _arguments2 = arguments; + + if (this.setParametersPromises && this.setParametersPromises.length) { + return Promise.all(this.setParametersPromises).then(function () { + return origCreateOffer.apply(_this4, _arguments2); + }).finally(function () { + _this4.setParametersPromises = []; + }); + } + return origCreateOffer.apply(this, arguments); + }; + } + + function shimCreateAnswer(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) { + return; + } + var origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer; + window.RTCPeerConnection.prototype.createAnswer = function createAnswer() { + var _this5 = this, + _arguments3 = arguments; + + if (this.setParametersPromises && this.setParametersPromises.length) { + return Promise.all(this.setParametersPromises).then(function () { + return origCreateAnswer.apply(_this5, _arguments3); + }).finally(function () { + _this5.setParametersPromises = []; + }); + } + return origCreateAnswer.apply(this, arguments); + }; + } + + },{"../utils":11,"./getdisplaymedia":8,"./getusermedia":9}],8:[function(require,module,exports){ + /* + * Copyright (c) 2018 The adapter.js 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. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.shimGetDisplayMedia = shimGetDisplayMedia; + function shimGetDisplayMedia(window, preferredMediaSource) { + if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) { + return; + } + if (!window.navigator.mediaDevices) { + return; + } + window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) { + if (!(constraints && constraints.video)) { + var err = new DOMException('getDisplayMedia without video ' + 'constraints is undefined'); + err.name = 'NotFoundError'; + // from https://heycam.github.io/webidl/#idl-DOMException-error-names + err.code = 8; + return Promise.reject(err); + } + if (constraints.video === true) { + constraints.video = { mediaSource: preferredMediaSource }; + } else { + constraints.video.mediaSource = preferredMediaSource; + } + return window.navigator.mediaDevices.getUserMedia(constraints); + }; + } + + },{}],9:[function(require,module,exports){ + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.shimGetUserMedia = shimGetUserMedia; + + var _utils = require('../utils'); + + var utils = _interopRequireWildcard(_utils); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function shimGetUserMedia(window, browserDetails) { + var navigator = window && window.navigator; + var MediaStreamTrack = window && window.MediaStreamTrack; + + navigator.getUserMedia = function (constraints, onSuccess, onError) { + // Replace Firefox 44+'s deprecation warning with unprefixed version. + utils.deprecated('navigator.getUserMedia', 'navigator.mediaDevices.getUserMedia'); + navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError); + }; + + if (!(browserDetails.version > 55 && 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) { + var remap = function remap(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + + var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function (c) { + if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object' && _typeof(c.audio) === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c.audio, 'autoGainControl', 'mozAutoGainControl'); + remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeGetUserMedia(c); + }; + + if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) { + var nativeGetSettings = MediaStreamTrack.prototype.getSettings; + MediaStreamTrack.prototype.getSettings = function () { + var obj = nativeGetSettings.apply(this, arguments); + remap(obj, 'mozAutoGainControl', 'autoGainControl'); + remap(obj, 'mozNoiseSuppression', 'noiseSuppression'); + return obj; + }; + } + + if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) { + var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints; + MediaStreamTrack.prototype.applyConstraints = function (c) { + if (this.kind === 'audio' && (typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c, 'autoGainControl', 'mozAutoGainControl'); + remap(c, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeApplyConstraints.apply(this, [c]); + }; + } + } + } + + },{"../utils":11}],10:[function(require,module,exports){ + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.shimLocalStreamsAPI = shimLocalStreamsAPI; + exports.shimRemoteStreamsAPI = shimRemoteStreamsAPI; + exports.shimCallbacksAPI = shimCallbacksAPI; + exports.shimGetUserMedia = shimGetUserMedia; + exports.shimConstraints = shimConstraints; + exports.shimRTCIceServerUrls = shimRTCIceServerUrls; + exports.shimTrackEventTransceiver = shimTrackEventTransceiver; + exports.shimCreateOfferLegacy = shimCreateOfferLegacy; + exports.shimAudioContext = shimAudioContext; + + var _utils = require('../utils'); + + var utils = _interopRequireWildcard(_utils); + + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + + function shimLocalStreamsAPI(window) { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) { + return; + } + if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() { + if (!this._localStreams) { + this._localStreams = []; + } + return this._localStreams; + }; + } + if (!('addStream' in window.RTCPeerConnection.prototype)) { + var _addTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this = this; + + if (!this._localStreams) { + this._localStreams = []; + } + if (!this._localStreams.includes(stream)) { + this._localStreams.push(stream); + } + // Try to emulate Chrome's behaviour of adding in audio-video order. + // Safari orders by track id. + stream.getAudioTracks().forEach(function (track) { + return _addTrack.call(_this, track, stream); + }); + stream.getVideoTracks().forEach(function (track) { + return _addTrack.call(_this, track, stream); + }); + }; + + window.RTCPeerConnection.prototype.addTrack = function addTrack(track) { + var _this2 = this; + + for (var _len = arguments.length, streams = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + streams[_key - 1] = arguments[_key]; + } + + if (streams) { + streams.forEach(function (stream) { + if (!_this2._localStreams) { + _this2._localStreams = [stream]; + } else if (!_this2._localStreams.includes(stream)) { + _this2._localStreams.push(stream); + } + }); + } + return _addTrack.apply(this, arguments); + }; + } + if (!('removeStream' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + var _this3 = this; + + if (!this._localStreams) { + this._localStreams = []; + } + var index = this._localStreams.indexOf(stream); + if (index === -1) { + return; + } + this._localStreams.splice(index, 1); + var tracks = stream.getTracks(); + this.getSenders().forEach(function (sender) { + if (tracks.includes(sender.track)) { + _this3.removeTrack(sender); + } + }); + }; + } + } + + function shimRemoteStreamsAPI(window) { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) { + return; + } + if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.getRemoteStreams = function getRemoteStreams() { + return this._remoteStreams ? this._remoteStreams : []; + }; + } + if (!('onaddstream' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', { + get: function get() { + return this._onaddstream; + }, + set: function set(f) { + var _this4 = this; + + if (this._onaddstream) { + this.removeEventListener('addstream', this._onaddstream); + this.removeEventListener('track', this._onaddstreampoly); + } + this.addEventListener('addstream', this._onaddstream = f); + this.addEventListener('track', this._onaddstreampoly = function (e) { + e.streams.forEach(function (stream) { + if (!_this4._remoteStreams) { + _this4._remoteStreams = []; + } + if (_this4._remoteStreams.includes(stream)) { + return; + } + _this4._remoteStreams.push(stream); + var event = new Event('addstream'); + event.stream = stream; + _this4.dispatchEvent(event); + }); + }); + } + }); + var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() { + var pc = this; + if (!this._onaddstreampoly) { + this.addEventListener('track', this._onaddstreampoly = function (e) { + e.streams.forEach(function (stream) { + if (!pc._remoteStreams) { + pc._remoteStreams = []; + } + if (pc._remoteStreams.indexOf(stream) >= 0) { + return; + } + pc._remoteStreams.push(stream); + var event = new Event('addstream'); + event.stream = stream; + pc.dispatchEvent(event); + }); + }); + } + return origSetRemoteDescription.apply(pc, arguments); + }; + } + } + + function shimCallbacksAPI(window) { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) { + return; + } + var prototype = window.RTCPeerConnection.prototype; + var origCreateOffer = prototype.createOffer; + var origCreateAnswer = prototype.createAnswer; + var setLocalDescription = prototype.setLocalDescription; + var setRemoteDescription = prototype.setRemoteDescription; + var addIceCandidate = prototype.addIceCandidate; + + prototype.createOffer = function createOffer(successCallback, failureCallback) { + var options = arguments.length >= 2 ? arguments[2] : arguments[0]; + var promise = origCreateOffer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + + prototype.createAnswer = function createAnswer(successCallback, failureCallback) { + var options = arguments.length >= 2 ? arguments[2] : arguments[0]; + var promise = origCreateAnswer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + + var withCallback = function withCallback(description, successCallback, failureCallback) { + var promise = setLocalDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setLocalDescription = withCallback; + + withCallback = function withCallback(description, successCallback, failureCallback) { + var promise = setRemoteDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setRemoteDescription = withCallback; + + withCallback = function withCallback(candidate, successCallback, failureCallback) { + var promise = addIceCandidate.apply(this, [candidate]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.addIceCandidate = withCallback; + } + + function shimGetUserMedia(window) { + var navigator = window && window.navigator; + + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + // shim not needed in Safari 12.1 + var mediaDevices = navigator.mediaDevices; + var _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices); + navigator.mediaDevices.getUserMedia = function (constraints) { + return _getUserMedia(shimConstraints(constraints)); + }; + } + + if (!navigator.getUserMedia && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) { + navigator.mediaDevices.getUserMedia(constraints).then(cb, errcb); + }.bind(navigator); + } + } + + function shimConstraints(constraints) { + if (constraints && constraints.video !== undefined) { + return Object.assign({}, constraints, { video: utils.compactObject(constraints.video) }); + } + + return constraints; + } + + function shimRTCIceServerUrls(window) { + if (!window.RTCPeerConnection) { + return; + } + // migrate from non-spec RTCIceServer.url to RTCIceServer.urls + var OrigPeerConnection = window.RTCPeerConnection; + window.RTCPeerConnection = function RTCPeerConnection(pcConfig, pcConstraints) { + if (pcConfig && pcConfig.iceServers) { + var newIceServers = []; + for (var i = 0; i < pcConfig.iceServers.length; i++) { + var server = pcConfig.iceServers[i]; + if (!server.hasOwnProperty('urls') && server.hasOwnProperty('url')) { + utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls'); + server = JSON.parse(JSON.stringify(server)); + server.urls = server.url; + delete server.url; + newIceServers.push(server); + } else { + newIceServers.push(pcConfig.iceServers[i]); + } + } + pcConfig.iceServers = newIceServers; + } + return new OrigPeerConnection(pcConfig, pcConstraints); + }; + window.RTCPeerConnection.prototype = OrigPeerConnection.prototype; + // wrap static methods. Currently just generateCertificate. + if ('generateCertificate' in OrigPeerConnection) { + Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', { + get: function get() { + return OrigPeerConnection.generateCertificate; + } + }); + } + } + + function shimTrackEventTransceiver(window) { + // Add event.transceiver member over deprecated event.receiver + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) { + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { + get: function get() { + return { receiver: this.receiver }; + } + }); + } + } + + function shimCreateOfferLegacy(window) { + var origCreateOffer = window.RTCPeerConnection.prototype.createOffer; + window.RTCPeerConnection.prototype.createOffer = function createOffer(offerOptions) { + if (offerOptions) { + if (typeof offerOptions.offerToReceiveAudio !== 'undefined') { + // support bit values + offerOptions.offerToReceiveAudio = !!offerOptions.offerToReceiveAudio; + } + var audioTransceiver = this.getTransceivers().find(function (transceiver) { + return transceiver.receiver.track.kind === 'audio'; + }); + if (offerOptions.offerToReceiveAudio === false && audioTransceiver) { + if (audioTransceiver.direction === 'sendrecv') { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection('sendonly'); + } else { + audioTransceiver.direction = 'sendonly'; + } + } else if (audioTransceiver.direction === 'recvonly') { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection('inactive'); + } else { + audioTransceiver.direction = 'inactive'; + } + } + } else if (offerOptions.offerToReceiveAudio === true && !audioTransceiver) { + this.addTransceiver('audio'); + } + + if (typeof offerOptions.offerToReceiveVideo !== 'undefined') { + // support bit values + offerOptions.offerToReceiveVideo = !!offerOptions.offerToReceiveVideo; + } + var videoTransceiver = this.getTransceivers().find(function (transceiver) { + return transceiver.receiver.track.kind === 'video'; + }); + if (offerOptions.offerToReceiveVideo === false && videoTransceiver) { + if (videoTransceiver.direction === 'sendrecv') { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection('sendonly'); + } else { + videoTransceiver.direction = 'sendonly'; + } + } else if (videoTransceiver.direction === 'recvonly') { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection('inactive'); + } else { + videoTransceiver.direction = 'inactive'; + } + } + } else if (offerOptions.offerToReceiveVideo === true && !videoTransceiver) { + this.addTransceiver('video'); + } + } + return origCreateOffer.apply(this, arguments); + }; + } + + function shimAudioContext(window) { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || window.AudioContext) { + return; + } + window.AudioContext = window.webkitAudioContext; + } + + },{"../utils":11}],11:[function(require,module,exports){ + /* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + /* eslint-env node */ + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports.extractVersion = extractVersion; + exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent; + exports.disableLog = disableLog; + exports.disableWarnings = disableWarnings; + exports.log = log; + exports.deprecated = deprecated; + exports.detectBrowser = detectBrowser; + exports.compactObject = compactObject; + exports.walkStats = walkStats; + exports.filterStats = filterStats; + + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + + var logDisabled_ = true; + var deprecationWarnings_ = true; + + /** + * Extract browser version out of the provided user agent string. + * + * @param {!string} uastring userAgent string. + * @param {!string} expr Regular expression used as match criteria. + * @param {!number} pos position in the version string to be returned. + * @return {!number} browser version. + */ + function extractVersion(uastring, expr, pos) { + var match = uastring.match(expr); + return match && match.length >= pos && parseInt(match[pos], 10); + } + +// Wraps the peerconnection event eventNameToWrap in a function +// which returns the modified event object (or false to prevent +// the event). + function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) { + if (!window.RTCPeerConnection) { + return; + } + var proto = window.RTCPeerConnection.prototype; + var nativeAddEventListener = proto.addEventListener; + proto.addEventListener = function (nativeEventName, cb) { + if (nativeEventName !== eventNameToWrap) { + return nativeAddEventListener.apply(this, arguments); + } + var wrappedCallback = function wrappedCallback(e) { + var modifiedEvent = wrapper(e); + if (modifiedEvent) { + if (cb.handleEvent) { + cb.handleEvent(modifiedEvent); + } else { + cb(modifiedEvent); + } + } + }; + this._eventMap = this._eventMap || {}; + if (!this._eventMap[eventNameToWrap]) { + this._eventMap[eventNameToWrap] = new Map(); + } + this._eventMap[eventNameToWrap].set(cb, wrappedCallback); + return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]); + }; + + var nativeRemoveEventListener = proto.removeEventListener; + proto.removeEventListener = function (nativeEventName, cb) { + if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[eventNameToWrap]) { + return nativeRemoveEventListener.apply(this, arguments); + } + if (!this._eventMap[eventNameToWrap].has(cb)) { + return nativeRemoveEventListener.apply(this, arguments); + } + var unwrappedCb = this._eventMap[eventNameToWrap].get(cb); + this._eventMap[eventNameToWrap].delete(cb); + if (this._eventMap[eventNameToWrap].size === 0) { + delete this._eventMap[eventNameToWrap]; + } + if (Object.keys(this._eventMap).length === 0) { + delete this._eventMap; + } + return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]); + }; + + Object.defineProperty(proto, 'on' + eventNameToWrap, { + get: function get() { + return this['_on' + eventNameToWrap]; + }, + set: function set(cb) { + if (this['_on' + eventNameToWrap]) { + this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]); + delete this['_on' + eventNameToWrap]; + } + if (cb) { + this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb); + } + }, + + enumerable: true, + configurable: true + }); + } + + function disableLog(bool) { + if (typeof bool !== 'boolean') { + return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.'); + } + logDisabled_ = bool; + return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled'; + } + + /** + * Disable or enable deprecation warnings + * @param {!boolean} bool set to true to disable warnings. + */ + function disableWarnings(bool) { + if (typeof bool !== 'boolean') { + return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.'); + } + deprecationWarnings_ = !bool; + return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled'); + } + + function log() { + if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object') { + if (logDisabled_) { + return; + } + if (typeof console !== 'undefined' && typeof console.log === 'function') { + console.log.apply(console, arguments); + } + } + } + + /** + * Shows a deprecation warning suggesting the modern and spec-compatible API. + */ + function deprecated(oldMethod, newMethod) { + if (!deprecationWarnings_) { + return; + } + console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.'); + } + + /** + * Browser detector. + * + * @return {object} result containing browser and version + * properties. + */ + function detectBrowser(window) { + // Returned result object. + var result = { browser: null, version: null }; + + // Fail early if it's not a browser + if (typeof window === 'undefined' || !window.navigator) { + result.browser = 'Not a browser.'; + return result; + } + + var navigator = window.navigator; + + + if (navigator.mozGetUserMedia) { + // Firefox. + result.browser = 'firefox'; + result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1); + } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection && !window.RTCIceGatherer) { + // Chrome, Chromium, Webview, Opera. + // Version matches Chrome/WebRTC version. + // Chrome 74 removed webkitGetUserMedia on http as well so we need the + // more complicated fallback to webkitRTCPeerConnection. + result.browser = 'chrome'; + result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2); + } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { + // Safari. + result.browser = 'safari'; + result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1); + result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype; + } else { + // Default fallthrough: not supported. + result.browser = 'Not a supported browser.'; + return result; + } + + return result; + } + + /** + * Checks if something is an object. + * + * @param {*} val The something you want to check. + * @return true if val is an object, false otherwise. + */ + function isObject(val) { + return Object.prototype.toString.call(val) === '[object Object]'; + } + + /** + * Remove all empty objects and undefined values + * from a nested object -- an enhanced and vanilla version + * of Lodash's `compact`. + */ + function compactObject(data) { + if (!isObject(data)) { + return data; + } + + return Object.keys(data).reduce(function (accumulator, key) { + var isObj = isObject(data[key]); + var value = isObj ? compactObject(data[key]) : data[key]; + var isEmptyObject = isObj && !Object.keys(value).length; + if (value === undefined || isEmptyObject) { + return accumulator; + } + return Object.assign(accumulator, _defineProperty({}, key, value)); + }, {}); + } + + /* iterates the stats graph recursively. */ + function walkStats(stats, base, resultSet) { + if (!base || resultSet.has(base.id)) { + return; + } + resultSet.set(base.id, base); + Object.keys(base).forEach(function (name) { + if (name.endsWith('Id')) { + walkStats(stats, stats.get(base[name]), resultSet); + } else if (name.endsWith('Ids')) { + base[name].forEach(function (id) { + walkStats(stats, stats.get(id), resultSet); + }); + } + }); + } + + /* filter getStats for a sender/receiver track. */ + function filterStats(result, track, outbound) { + var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp'; + var filteredResult = new Map(); + if (track === null) { + return filteredResult; + } + var trackStats = []; + result.forEach(function (value) { + if (value.type === 'track' && value.trackIdentifier === track.id) { + trackStats.push(value); + } + }); + trackStats.forEach(function (trackStat) { + result.forEach(function (stats) { + if (stats.type === streamStatsType && stats.trackId === trackStat.id) { + walkStats(result, stats, filteredResult); + } + }); + }); + return filteredResult; + } + + },{}],12:[function(require,module,exports){ + /* eslint-env node */ + 'use strict'; + +// SDP helpers. + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + var SDPUtils = {}; + +// Generate an alphanumeric identifier for cname or mids. +// TODO: use UUIDs instead? https://gist.github.com/jed/982883 + SDPUtils.generateIdentifier = function () { + return Math.random().toString(36).substr(2, 10); + }; + +// The RTCP CNAME used by all peerconnections from the same JS. + SDPUtils.localCName = SDPUtils.generateIdentifier(); + +// Splits SDP into lines, dealing with both CRLF and LF. + SDPUtils.splitLines = function (blob) { + return blob.trim().split('\n').map(function (line) { + return line.trim(); + }); + }; +// Splits SDP into sessionpart and mediasections. Ensures CRLF. + SDPUtils.splitSections = function (blob) { + var parts = blob.split('\nm='); + return parts.map(function (part, index) { + return (index > 0 ? 'm=' + part : part).trim() + '\r\n'; + }); + }; + +// returns the session description. + SDPUtils.getDescription = function (blob) { + var sections = SDPUtils.splitSections(blob); + return sections && sections[0]; + }; + +// returns the individual media sections. + SDPUtils.getMediaSections = function (blob) { + var sections = SDPUtils.splitSections(blob); + sections.shift(); + return sections; + }; + +// Returns lines that start with a certain prefix. + SDPUtils.matchPrefix = function (blob, prefix) { + return SDPUtils.splitLines(blob).filter(function (line) { + return line.indexOf(prefix) === 0; + }); + }; + +// Parses an ICE candidate line. Sample input: +// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 +// rport 55996" + SDPUtils.parseCandidate = function (line) { + var parts = void 0; + // Parse both variants. + if (line.indexOf('a=candidate:') === 0) { + parts = line.substring(12).split(' '); + } else { + parts = line.substring(10).split(' '); + } + + var candidate = { + foundation: parts[0], + component: { 1: 'rtp', 2: 'rtcp' }[parts[1]], + protocol: parts[2].toLowerCase(), + priority: parseInt(parts[3], 10), + ip: parts[4], + address: parts[4], // address is an alias for ip. + port: parseInt(parts[5], 10), + // skip parts[6] == 'typ' + type: parts[7] + }; + + for (var i = 8; i < parts.length; i += 2) { + switch (parts[i]) { + case 'raddr': + candidate.relatedAddress = parts[i + 1]; + break; + case 'rport': + candidate.relatedPort = parseInt(parts[i + 1], 10); + break; + case 'tcptype': + candidate.tcpType = parts[i + 1]; + break; + case 'ufrag': + candidate.ufrag = parts[i + 1]; // for backward compatibility. + candidate.usernameFragment = parts[i + 1]; + break; + default: + // extension handling, in particular ufrag. Don't overwrite. + if (candidate[parts[i]] === undefined) { + candidate[parts[i]] = parts[i + 1]; + } + break; + } + } + return candidate; + }; + +// Translates a candidate object into SDP candidate attribute. + SDPUtils.writeCandidate = function (candidate) { + var sdp = []; + sdp.push(candidate.foundation); + + var component = candidate.component; + if (component === 'rtp') { + sdp.push(1); + } else if (component === 'rtcp') { + sdp.push(2); + } else { + sdp.push(component); + } + sdp.push(candidate.protocol.toUpperCase()); + sdp.push(candidate.priority); + sdp.push(candidate.address || candidate.ip); + sdp.push(candidate.port); + + var type = candidate.type; + sdp.push('typ'); + sdp.push(type); + if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) { + sdp.push('raddr'); + sdp.push(candidate.relatedAddress); + sdp.push('rport'); + sdp.push(candidate.relatedPort); + } + if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') { + sdp.push('tcptype'); + sdp.push(candidate.tcpType); + } + if (candidate.usernameFragment || candidate.ufrag) { + sdp.push('ufrag'); + sdp.push(candidate.usernameFragment || candidate.ufrag); + } + return 'candidate:' + sdp.join(' '); + }; + +// Parses an ice-options line, returns an array of option tags. +// a=ice-options:foo bar + SDPUtils.parseIceOptions = function (line) { + return line.substr(14).split(' '); + }; + +// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input: +// a=rtpmap:111 opus/48000/2 + SDPUtils.parseRtpMap = function (line) { + var parts = line.substr(9).split(' '); + var parsed = { + payloadType: parseInt(parts.shift(), 10) // was: id + }; + + parts = parts[0].split('/'); + + parsed.name = parts[0]; + parsed.clockRate = parseInt(parts[1], 10); // was: clockrate + parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1; + // legacy alias, got renamed back to channels in ORTC. + parsed.numChannels = parsed.channels; + return parsed; + }; + +// Generate an a=rtpmap line from RTCRtpCodecCapability or +// RTCRtpCodecParameters. + SDPUtils.writeRtpMap = function (codec) { + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + var channels = codec.channels || codec.numChannels || 1; + return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (channels !== 1 ? '/' + channels : '') + '\r\n'; + }; + +// Parses an a=extmap line (headerextension from RFC 5285). Sample input: +// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset +// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset + SDPUtils.parseExtmap = function (line) { + var parts = line.substr(9).split(' '); + return { + id: parseInt(parts[0], 10), + direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', + uri: parts[1] + }; + }; + +// Generates a=extmap line from RTCRtpHeaderExtensionParameters or +// RTCRtpHeaderExtension. + SDPUtils.writeExtmap = function (headerExtension) { + return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + (headerExtension.direction && headerExtension.direction !== 'sendrecv' ? '/' + headerExtension.direction : '') + ' ' + headerExtension.uri + '\r\n'; + }; + +// Parses an ftmp line, returns dictionary. Sample input: +// a=fmtp:96 vbr=on;cng=on +// Also deals with vbr=on; cng=on + SDPUtils.parseFmtp = function (line) { + var parsed = {}; + var kv = void 0; + var parts = line.substr(line.indexOf(' ') + 1).split(';'); + for (var j = 0; j < parts.length; j++) { + kv = parts[j].trim().split('='); + parsed[kv[0].trim()] = kv[1]; + } + return parsed; + }; + +// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters. + SDPUtils.writeFmtp = function (codec) { + var line = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.parameters && Object.keys(codec.parameters).length) { + var params = []; + Object.keys(codec.parameters).forEach(function (param) { + if (codec.parameters[param]) { + params.push(param + '=' + codec.parameters[param]); + } else { + params.push(param); + } + }); + line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n'; + } + return line; + }; + +// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: +// a=rtcp-fb:98 nack rpsi + SDPUtils.parseRtcpFb = function (line) { + var parts = line.substr(line.indexOf(' ') + 1).split(' '); + return { + type: parts.shift(), + parameter: parts.join(' ') + }; + }; +// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters. + SDPUtils.writeRtcpFb = function (codec) { + var lines = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.rtcpFeedback && codec.rtcpFeedback.length) { + // FIXME: special handling for trr-int? + codec.rtcpFeedback.forEach(function (fb) { + lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n'; + }); + } + return lines; + }; + +// Parses an RFC 5576 ssrc media attribute. Sample input: +// a=ssrc:3735928559 cname:something + SDPUtils.parseSsrcMedia = function (line) { + var sp = line.indexOf(' '); + var parts = { + ssrc: parseInt(line.substr(7, sp - 7), 10) + }; + var colon = line.indexOf(':', sp); + if (colon > -1) { + parts.attribute = line.substr(sp + 1, colon - sp - 1); + parts.value = line.substr(colon + 1); + } else { + parts.attribute = line.substr(sp + 1); + } + return parts; + }; + + SDPUtils.parseSsrcGroup = function (line) { + var parts = line.substr(13).split(' '); + return { + semantics: parts.shift(), + ssrcs: parts.map(function (ssrc) { + return parseInt(ssrc, 10); + }) + }; + }; + +// Extracts the MID (RFC 5888) from a media section. +// returns the MID or undefined if no mid line was found. + SDPUtils.getMid = function (mediaSection) { + var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; + if (mid) { + return mid.substr(6); + } + }; + + SDPUtils.parseFingerprint = function (line) { + var parts = line.substr(14).split(' '); + return { + algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. + value: parts[1] + }; + }; + +// Extracts DTLS parameters from SDP media section or sessionpart. +// FIXME: for consistency with other functions this should only +// get the fingerprint line as input. See also getIceParameters. + SDPUtils.getDtlsParameters = function (mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=fingerprint:'); + // Note: a=setup line is ignored since we use the 'auto' role. + // Note2: 'algorithm' is not case sensitive except in Edge. + return { + role: 'auto', + fingerprints: lines.map(SDPUtils.parseFingerprint) + }; + }; + +// Serializes DTLS parameters to SDP. + SDPUtils.writeDtlsParameters = function (params, setupType) { + var sdp = 'a=setup:' + setupType + '\r\n'; + params.fingerprints.forEach(function (fp) { + sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n'; + }); + return sdp; + }; + +// Parses a=crypto lines into +// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members + SDPUtils.parseCryptoLine = function (line) { + var parts = line.substr(9).split(' '); + return { + tag: parseInt(parts[0], 10), + cryptoSuite: parts[1], + keyParams: parts[2], + sessionParams: parts.slice(3) + }; + }; + + SDPUtils.writeCryptoLine = function (parameters) { + return 'a=crypto:' + parameters.tag + ' ' + parameters.cryptoSuite + ' ' + (_typeof(parameters.keyParams) === 'object' ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) : parameters.keyParams) + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + '\r\n'; + }; + +// Parses the crypto key parameters into +// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam* + SDPUtils.parseCryptoKeyParams = function (keyParams) { + if (keyParams.indexOf('inline:') !== 0) { + return null; + } + var parts = keyParams.substr(7).split('|'); + return { + keyMethod: 'inline', + keySalt: parts[0], + lifeTime: parts[1], + mkiValue: parts[2] ? parts[2].split(':')[0] : undefined, + mkiLength: parts[2] ? parts[2].split(':')[1] : undefined + }; + }; + + SDPUtils.writeCryptoKeyParams = function (keyParams) { + return keyParams.keyMethod + ':' + keyParams.keySalt + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + (keyParams.mkiValue && keyParams.mkiLength ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength : ''); + }; + +// Extracts all SDES parameters. + SDPUtils.getCryptoParameters = function (mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=crypto:'); + return lines.map(SDPUtils.parseCryptoLine); + }; + +// Parses ICE information from SDP media section or sessionpart. +// FIXME: for consistency with other functions this should only +// get the ice-ufrag and ice-pwd lines as input. + SDPUtils.getIceParameters = function (mediaSection, sessionpart) { + var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-ufrag:')[0]; + var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-pwd:')[0]; + if (!(ufrag && pwd)) { + return null; + } + return { + usernameFragment: ufrag.substr(12), + password: pwd.substr(10) + }; + }; + +// Serializes ICE parameters to SDP. + SDPUtils.writeIceParameters = function (params) { + var sdp = 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n'; + if (params.iceLite) { + sdp += 'a=ice-lite\r\n'; + } + return sdp; + }; + +// Parses the SDP media section and returns RTCRtpParameters. + SDPUtils.parseRtpParameters = function (mediaSection) { + var description = { + codecs: [], + headerExtensions: [], + fecMechanisms: [], + rtcp: [] + }; + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + for (var i = 3; i < mline.length; i++) { + // find all codecs from mline[3..] + var pt = mline[i]; + var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0]; + if (rtpmapline) { + var codec = SDPUtils.parseRtpMap(rtpmapline); + var fmtps = SDPUtils.matchPrefix(mediaSection, 'a=fmtp:' + pt + ' '); + // Only the first a=fmtp: is considered. + codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {}; + codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb); + description.codecs.push(codec); + // parse FEC mechanisms from rtpmap lines. + switch (codec.name.toUpperCase()) { + case 'RED': + case 'ULPFEC': + description.fecMechanisms.push(codec.name.toUpperCase()); + break; + default: + // only RED and ULPFEC are recognized as FEC mechanisms. + break; + } + } + } + SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function (line) { + description.headerExtensions.push(SDPUtils.parseExtmap(line)); + }); + // FIXME: parse rtcp. + return description; + }; + +// Generates parts of the SDP media section describing the capabilities / +// parameters. + SDPUtils.writeRtpDescription = function (kind, caps) { + var sdp = ''; + + // Build the mline. + sdp += 'm=' + kind + ' '; + sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs. + sdp += ' UDP/TLS/RTP/SAVPF '; + sdp += caps.codecs.map(function (codec) { + if (codec.preferredPayloadType !== undefined) { + return codec.preferredPayloadType; + } + return codec.payloadType; + }).join(' ') + '\r\n'; + + sdp += 'c=IN IP4 0.0.0.0\r\n'; + sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n'; + + // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb. + caps.codecs.forEach(function (codec) { + sdp += SDPUtils.writeRtpMap(codec); + sdp += SDPUtils.writeFmtp(codec); + sdp += SDPUtils.writeRtcpFb(codec); + }); + var maxptime = 0; + caps.codecs.forEach(function (codec) { + if (codec.maxptime > maxptime) { + maxptime = codec.maxptime; + } + }); + if (maxptime > 0) { + sdp += 'a=maxptime:' + maxptime + '\r\n'; + } + + if (caps.headerExtensions) { + caps.headerExtensions.forEach(function (extension) { + sdp += SDPUtils.writeExtmap(extension); + }); + } + // FIXME: write fecMechanisms. + return sdp; + }; + +// Parses the SDP media section and returns an array of +// RTCRtpEncodingParameters. + SDPUtils.parseRtpEncodingParameters = function (mediaSection) { + var encodingParameters = []; + var description = SDPUtils.parseRtpParameters(mediaSection); + var hasRed = description.fecMechanisms.indexOf('RED') !== -1; + var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; + + // filter a=ssrc:... cname:, ignore PlanB-msid + var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) { + return SDPUtils.parseSsrcMedia(line); + }).filter(function (parts) { + return parts.attribute === 'cname'; + }); + var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc; + var secondarySsrc = void 0; + + var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID').map(function (line) { + var parts = line.substr(17).split(' '); + return parts.map(function (part) { + return parseInt(part, 10); + }); + }); + if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) { + secondarySsrc = flows[0][1]; + } + + description.codecs.forEach(function (codec) { + if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) { + var encParam = { + ssrc: primarySsrc, + codecPayloadType: parseInt(codec.parameters.apt, 10) + }; + if (primarySsrc && secondarySsrc) { + encParam.rtx = { ssrc: secondarySsrc }; + } + encodingParameters.push(encParam); + if (hasRed) { + encParam = JSON.parse(JSON.stringify(encParam)); + encParam.fec = { + ssrc: primarySsrc, + mechanism: hasUlpfec ? 'red+ulpfec' : 'red' + }; + encodingParameters.push(encParam); + } + } + }); + if (encodingParameters.length === 0 && primarySsrc) { + encodingParameters.push({ + ssrc: primarySsrc + }); + } + + // we support both b=AS and b=TIAS but interpret AS as TIAS. + var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); + if (bandwidth.length) { + if (bandwidth[0].indexOf('b=TIAS:') === 0) { + bandwidth = parseInt(bandwidth[0].substr(7), 10); + } else if (bandwidth[0].indexOf('b=AS:') === 0) { + // use formula from JSEP to convert b=AS to TIAS value. + bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 - 50 * 40 * 8; + } else { + bandwidth = undefined; + } + encodingParameters.forEach(function (params) { + params.maxBitrate = bandwidth; + }); + } + return encodingParameters; + }; + +// parses http://draft.ortc.org/#rtcrtcpparameters* + SDPUtils.parseRtcpParameters = function (mediaSection) { + var rtcpParameters = {}; + + // Gets the first SSRC. Note that with RTX there might be multiple + // SSRCs. + var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) { + return SDPUtils.parseSsrcMedia(line); + }).filter(function (obj) { + return obj.attribute === 'cname'; + })[0]; + if (remoteSsrc) { + rtcpParameters.cname = remoteSsrc.value; + rtcpParameters.ssrc = remoteSsrc.ssrc; + } + + // Edge uses the compound attribute instead of reducedSize + // compound is !reducedSize + var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize'); + rtcpParameters.reducedSize = rsize.length > 0; + rtcpParameters.compound = rsize.length === 0; + + // parses the rtcp-mux attrіbute. + // Note that Edge does not support unmuxed RTCP. + var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux'); + rtcpParameters.mux = mux.length > 0; + + return rtcpParameters; + }; + + SDPUtils.writeRtcpParameters = function (rtcpParameters) { + var sdp = ''; + if (rtcpParameters.reducedSize) { + sdp += 'a=rtcp-rsize\r\n'; + } + if (rtcpParameters.mux) { + sdp += 'a=rtcp-mux\r\n'; + } + if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) { + sdp += 'a=ssrc:' + rtcpParameters.ssrc + ' cname:' + rtcpParameters.cname + '\r\n'; + } + return sdp; + }; + +// parses either a=msid: or a=ssrc:... msid lines and returns +// the id of the MediaStream and MediaStreamTrack. + SDPUtils.parseMsid = function (mediaSection) { + var parts = void 0; + var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); + if (spec.length === 1) { + parts = spec[0].substr(7).split(' '); + return { stream: parts[0], track: parts[1] }; + } + var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) { + return SDPUtils.parseSsrcMedia(line); + }).filter(function (msidParts) { + return msidParts.attribute === 'msid'; + }); + if (planB.length > 0) { + parts = planB[0].value.split(' '); + return { stream: parts[0], track: parts[1] }; + } + }; + +// SCTP +// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back +// to draft-ietf-mmusic-sctp-sdp-05 + SDPUtils.parseSctpDescription = function (mediaSection) { + var mline = SDPUtils.parseMLine(mediaSection); + var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:'); + var maxMessageSize = void 0; + if (maxSizeLine.length > 0) { + maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10); + } + if (isNaN(maxMessageSize)) { + maxMessageSize = 65536; + } + var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:'); + if (sctpPort.length > 0) { + return { + port: parseInt(sctpPort[0].substr(12), 10), + protocol: mline.fmt, + maxMessageSize: maxMessageSize + }; + } + var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:'); + if (sctpMapLines.length > 0) { + var parts = sctpMapLines[0].substr(10).split(' '); + return { + port: parseInt(parts[0], 10), + protocol: parts[1], + maxMessageSize: maxMessageSize + }; + } + }; + +// SCTP +// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers +// support by now receiving in this format, unless we originally parsed +// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line +// protocol of DTLS/SCTP -- without UDP/ or TCP/) + SDPUtils.writeSctpDescription = function (media, sctp) { + var output = []; + if (media.protocol !== 'DTLS/SCTP') { + output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctp-port:' + sctp.port + '\r\n']; + } else { + output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n']; + } + if (sctp.maxMessageSize !== undefined) { + output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n'); + } + return output.join(''); + }; + +// Generate a session ID for SDP. +// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1 +// recommends using a cryptographically random +ve 64-bit value +// but right now this should be acceptable and within the right range + SDPUtils.generateSessionId = function () { + return Math.random().toString().substr(2, 21); + }; + +// Write boiler plate for start of SDP +// sessId argument is optional - if not supplied it will +// be generated randomly +// sessVersion is optional and defaults to 2 +// sessUser is optional and defaults to 'thisisadapterortc' + SDPUtils.writeSessionBoilerplate = function (sessId, sessVer, sessUser) { + var sessionId = void 0; + var version = sessVer !== undefined ? sessVer : 2; + if (sessId) { + sessionId = sessId; + } else { + sessionId = SDPUtils.generateSessionId(); + } + var user = sessUser || 'thisisadapterortc'; + // FIXME: sess-id should be an NTP timestamp. + return 'v=0\r\n' + 'o=' + user + ' ' + sessionId + ' ' + version + ' IN IP4 127.0.0.1\r\n' + 's=-\r\n' + 't=0 0\r\n'; + }; + +// Gets the direction from the mediaSection or the sessionpart. + SDPUtils.getDirection = function (mediaSection, sessionpart) { + // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv. + var lines = SDPUtils.splitLines(mediaSection); + for (var i = 0; i < lines.length; i++) { + switch (lines[i]) { + case 'a=sendrecv': + case 'a=sendonly': + case 'a=recvonly': + case 'a=inactive': + return lines[i].substr(2); + default: + // FIXME: What should happen here? + } + } + if (sessionpart) { + return SDPUtils.getDirection(sessionpart); + } + return 'sendrecv'; + }; + + SDPUtils.getKind = function (mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + return mline[0].substr(2); + }; + + SDPUtils.isRejected = function (mediaSection) { + return mediaSection.split(' ', 2)[1] === '0'; + }; + + SDPUtils.parseMLine = function (mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var parts = lines[0].substr(2).split(' '); + return { + kind: parts[0], + port: parseInt(parts[1], 10), + protocol: parts[2], + fmt: parts.slice(3).join(' ') + }; + }; + + SDPUtils.parseOLine = function (mediaSection) { + var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; + var parts = line.substr(2).split(' '); + return { + username: parts[0], + sessionId: parts[1], + sessionVersion: parseInt(parts[2], 10), + netType: parts[3], + addressType: parts[4], + address: parts[5] + }; + }; + +// a very naive interpretation of a valid SDP. + SDPUtils.isValidSDP = function (blob) { + if (typeof blob !== 'string' || blob.length === 0) { + return false; + } + var lines = SDPUtils.splitLines(blob); + for (var i = 0; i < lines.length; i++) { + if (lines[i].length < 2 || lines[i].charAt(1) !== '=') { + return false; + } + // TODO: check the modifier a bit more. + } + return true; + }; + +// Expose public methods. + if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') { + module.exports = SDPUtils; + } + },{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/Runtime/Plugins/WebGL/adapter.jspre.meta b/Runtime/Plugins/WebGL/adapter.jspre.meta new file mode 100644 index 0000000000..fe0f01103f --- /dev/null +++ b/Runtime/Plugins/WebGL/adapter.jspre.meta @@ -0,0 +1,76 @@ +fileFormatVersion: 2 +guid: d661aa39cbfa723409a4ba8a7aca10b9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Context.cs b/Runtime/Scripts/Context.cs index 55c5ca637c..b218f15dc5 100644 --- a/Runtime/Scripts/Context.cs +++ b/Runtime/Scripts/Context.cs @@ -1,7 +1,13 @@ using System; using System.Runtime.InteropServices; using System.Threading; +using System.Collections; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + using UnityEngine; +using UnityEngine.Experimental.Rendering; #if UNITY_EDITOR using UnityEditor; @@ -219,34 +225,139 @@ public void DeletePeerConnection(IntPtr ptr) NativeMethods.ContextDeletePeerConnection(self, ptr); } + public RTCError PeerConnectionSetLocalDescription(IntPtr ptr, ref RTCSessionDescription desc) + { + IntPtr ptrError = IntPtr.Zero; +#if !UNITY_WEBGL + var observer = NativeMethods.PeerConnectionSetLocalDescription(ptr, ref desc, out var errorType, ref ptrError); + string message = ptrError != IntPtr.Zero ? ptrError.AsAnsiStringWithFreeMem() : null; + return new RTCError { errorType = errorType, message = message}; +#else + IntPtr buf = NativeMethods.PeerConnectionSetLocalDescription(self, ptr, desc.type, desc.sdp); + var arr = NativeMethods.ptrToIntPtrArray(buf); + RTCErrorType errorType = (RTCErrorType)arr[0]; + string errorMsg = arr[1].AsAnsiStringWithFreeMem(); + return new RTCError { errorType = errorType, message = errorMsg }; +#endif + } + + public RTCError PeerConnectionSetLocalDescription(IntPtr ptr) + { + IntPtr ptrError = IntPtr.Zero; +#if !UNITY_WEBGL + var observer = NativeMethods.PeerConnectionSetLocalDescriptionWithoutDescription(ptr, out var errorType, ref ptrError); + string message = ptrError != IntPtr.Zero ? ptrError.AsAnsiStringWithFreeMem() : null; + return new RTCError { errorType = errorType, message = message }; +#else + IntPtr buf = NativeMethods.PeerConnectionSetLocalDescriptionWithoutDescription(self, ptr); + var arr = NativeMethods.ptrToIntPtrArray(buf); + RTCErrorType errorType = (RTCErrorType)arr[0]; + string errorMsg = arr[1].AsAnsiStringWithFreeMem(); + return new RTCError { errorType = errorType, message = errorMsg }; +#endif + + } + + public RTCError PeerConnectionSetRemoteDescription(IntPtr ptr, ref RTCSessionDescription desc) + { + IntPtr ptrError = IntPtr.Zero; +#if !UNITY_WEBGL + var observer = NativeMethods.PeerConnectionSetRemoteDescription(ptr, ref desc, out var errorType, ref ptrError); + string message = ptrError != IntPtr.Zero ? ptrError.AsAnsiStringWithFreeMem() : null; + return new RTCError { errorType = errorType, message = message}; +#else + IntPtr buf = NativeMethods.PeerConnectionSetRemoteDescription(self, ptr, desc.type, desc.sdp); + var arr = NativeMethods.ptrToIntPtrArray(buf); + RTCErrorType errorType = (RTCErrorType)arr[0]; + string errorMsg = arr[1].AsAnsiStringWithFreeMem(); + return new RTCError { errorType = errorType, message = errorMsg }; +#endif + } + + public void PeerConnectionRegisterOnSetSessionDescSuccess(IntPtr ptr, DelegateNativePeerConnectionSetSessionDescSuccess callback) + { + NativeMethods.PeerConnectionRegisterOnSetSessionDescSuccess(self, ptr, callback); + } + + public void PeerConnectionRegisterOnSetSessionDescFailure(IntPtr ptr, DelegateNativePeerConnectionSetSessionDescFailure callback) + { + NativeMethods.PeerConnectionRegisterOnSetSessionDescFailure(self, ptr, callback); + } + + public IntPtr PeerConnectionAddTransceiver(IntPtr pc, IntPtr track) + { + return NativeMethods.PeerConnectionAddTransceiver(pc, track); + } + + public IntPtr PeerConnectionAddTransceiverWithType(IntPtr pc, TrackKind kind) + { + return NativeMethods.PeerConnectionAddTransceiverWithType(pc, kind); + } + public IntPtr PeerConnectionGetReceivers(IntPtr ptr, out ulong length) { +#if !UNITY_WEBGL return NativeMethods.PeerConnectionGetReceivers(self, ptr, out length); +#else + length = 0; + return NativeMethods.PeerConnectionGetReceivers(self, ptr); +#endif } public IntPtr PeerConnectionGetSenders(IntPtr ptr, out ulong length) { +#if !UNITY_WEBGL return NativeMethods.PeerConnectionGetSenders(self, ptr, out length); +#else + length = 0; + return NativeMethods.PeerConnectionGetSenders(self, ptr); +#endif } public IntPtr PeerConnectionGetTransceivers(IntPtr ptr, out ulong length) { +#if !UNITY_WEBGL return NativeMethods.PeerConnectionGetTransceivers(self, ptr, out length); +#else + length = 0; + return NativeMethods.PeerConnectionGetTransceivers(self, ptr); +#endif } public CreateSessionDescriptionObserver PeerConnectionCreateOffer(IntPtr ptr, ref RTCOfferAnswerOptions options) { +#if !UNITY_WEBGL return NativeMethods.PeerConnectionCreateOffer(self, ptr, ref options); +#else + var observer = new CreateSessionDescriptionObserver(); + NativeMethods.PeerConnectionRegisterOnSetSessionDescSuccess(self,ptr,(result) => observer.Invoke(RTCSdpType.Offer,result.ToString(),RTCErrorType.None,null)); + NativeMethods.PeerConnectionRegisterOnSetSessionDescFailure(self,ptr,(result,errorType,msg) => observer.Invoke(RTCSdpType.Offer,result.ToString(),(RTCErrorType)errorType,msg.ToString())); + NativeMethods.PeerConnectionCreateOffer(ptr,JObject.FromObject(options).ToString()); + return observer; +#endif } public CreateSessionDescriptionObserver PeerConnectionCreateAnswer(IntPtr ptr, ref RTCOfferAnswerOptions options) { +#if !UNITY_WEBGL return NativeMethods.PeerConnectionCreateAnswer(self, ptr, ref options); +#else + var observer = new CreateSessionDescriptionObserver(); + NativeMethods.PeerConnectionRegisterOnSetSessionDescSuccess(self,ptr,(result) => observer.Invoke(RTCSdpType.Answer,result.ToString(),RTCErrorType.None,null)); + NativeMethods.PeerConnectionRegisterOnSetSessionDescFailure(self,ptr,(result,errorType,msg) => observer.Invoke(RTCSdpType.Answer,result.ToString(),(RTCErrorType)errorType,msg.ToString())); + NativeMethods.PeerConnectionCreateAnswer(ptr,JObject.FromObject(options).ToString()); + return observer; +#endif } public IntPtr CreateDataChannel(IntPtr ptr, string label, ref RTCDataChannelInitInternal options) { +#if !UNITY_WEBGL return NativeMethods.ContextCreateDataChannel(self, ptr, label, ref options); +#else + var optionsJson = JsonUtility.ToJson(options); + return NativeMethods.ContextCreateDataChannel(self, ptr, label, optionsJson); +#endif } public void DeleteDataChannel(IntPtr ptr) @@ -334,11 +445,22 @@ public IntPtr CreateAudioTrack(string label, IntPtr trackSource) { return NativeMethods.ContextCreateAudioTrack(self, label, trackSource); } - +#if !UNITY_WEBGL public IntPtr CreateVideoTrack(string label, IntPtr source) { return NativeMethods.ContextCreateVideoTrack(self, label, source); } +#else + public IntPtr CreateVideoTrack(string label) + { + return IntPtr.Zero; // NativeMethods.ContextCreateVideoTrack(self, label); + } + + public IntPtr CreateVideoTrack(IntPtr srcTexturePtr, IntPtr dstTexturePtr, int width, int height) + { + return NativeMethods.ContextCreateVideoTrack(self, srcTexturePtr, dstTexturePtr, width, height); + } +#endif public void StopMediaStreamTrack(IntPtr track) { @@ -366,15 +488,36 @@ public void DeleteStatsReport(IntPtr report) NativeMethods.ContextDeleteStatsReport(self, report); } + public void SetVideoEncoderParameter(IntPtr track, int width, int height, GraphicsFormat format, IntPtr texturePtr) + { + NativeMethods.ContextSetVideoEncoderParameter(self, track, width, height, format, texturePtr); + } + +#if !UNITY_WEBGL public void GetSenderCapabilities(TrackKind kind, out IntPtr capabilities) { NativeMethods.ContextGetSenderCapabilities(self, kind, out capabilities); } +#else + public RTCRtpCapabilities GetSenderCapabilities(TrackKind kind) + { + string json = NativeMethods.ContextGetSenderCapabilities(self, kind); + return JsonConvert.DeserializeObject(json); + } +#endif +#if !UNITY_WEBGL public void GetReceiverCapabilities(TrackKind kind, out IntPtr capabilities) { NativeMethods.ContextGetReceiverCapabilities(self, kind, out capabilities); } +#else + public RTCRtpCapabilities GetReceiverCapabilities(TrackKind kind) + { + string json = NativeMethods.ContextGetReceiverCapabilities(self, kind); + return JsonConvert.DeserializeObject(json); + } +#endif internal void BatchUpdate(IntPtr batchData) { diff --git a/Runtime/Scripts/CreateSessionDescriptionObserver.cs b/Runtime/Scripts/CreateSessionDescriptionObserver.cs index 385533fd51..c797f3b2a6 100644 --- a/Runtime/Scripts/CreateSessionDescriptionObserver.cs +++ b/Runtime/Scripts/CreateSessionDescriptionObserver.cs @@ -7,7 +7,7 @@ class CreateSessionDescriptionObserver : SafeHandle { public Action onCreateSessionDescription; - private CreateSessionDescriptionObserver() + internal CreateSessionDescriptionObserver() : base(IntPtr.Zero, true) { } diff --git a/Runtime/Scripts/MediaStream.cs b/Runtime/Scripts/MediaStream.cs index 9e9b29137c..ba51fdeff2 100644 --- a/Runtime/Scripts/MediaStream.cs +++ b/Runtime/Scripts/MediaStream.cs @@ -1,17 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; +using UnityEngine; namespace Unity.WebRTC { /// - /// + /// /// /// public delegate void DelegateOnAddTrack(MediaStreamTrackEvent e); /// - /// + /// /// /// public delegate void DelegateOnRemoveTrack(MediaStreamTrackEvent e); @@ -37,6 +38,20 @@ public class MediaStream : RefCountedObject private HashSet cacheTracks = new HashSet(); +#if UNITY_WEBGL + // TODO Use MediaTrackConstraints instead of booleans + public class MediaStreamConstraints + { + public bool audio = true; + public bool video = true; + } + + public void AddUserMedia(MediaStreamConstraints constraints) + { + NativeMethods.MediaStreamAddUserMedia(self, JsonUtility.ToJson(constraints)); + } +#endif + /// /// String containing 36 characters denoting a unique identifier for the object. /// @@ -58,7 +73,7 @@ public class MediaStream : RefCountedObject /// Disposes of MediaStream. ///
/// - /// `Dispose` method disposes of the MediaStream and releases the associated resources. + /// `Dispose` method disposes of the MediaStream and releases the associated resources. /// /// /// public IEnumerable GetVideoTracks() { +#if !UNITY_WEBGL var buf = NativeMethods.MediaStreamGetVideoTracks(GetSelfOrThrow(), out ulong length); return WebRTC.Deserialize(buf, (int)length, ptr => new VideoStreamTrack(ptr)); +#else + var ptr = NativeMethods.MediaStreamGetVideoTracks(GetSelfOrThrow()); + var buf = NativeMethods.ptrToIntPtrArray(ptr); + return WebRTC.Deserialize(buf, p => new VideoStreamTrack(p)); +#endif } /// @@ -139,8 +160,14 @@ public IEnumerable GetVideoTracks() /// public IEnumerable GetAudioTracks() { +#if !UNITY_WEBGL var buf = NativeMethods.MediaStreamGetAudioTracks(GetSelfOrThrow(), out ulong length); return WebRTC.Deserialize(buf, (int)length, ptr => new AudioStreamTrack(ptr)); +#else + var ptr = NativeMethods.MediaStreamGetAudioTracks(GetSelfOrThrow()); + var buf = NativeMethods.ptrToIntPtrArray(ptr); + return WebRTC.Deserialize(buf, p => new AudioStreamTrack(p)); +#endif } /// @@ -224,7 +251,7 @@ public MediaStream() : this(WebRTC.Context.CreateMediaStream(Guid.NewGuid().ToSt } /// - /// + /// /// /// internal MediaStream(IntPtr ptr) : base(ptr) diff --git a/Runtime/Scripts/MediaStreamTrack.cs b/Runtime/Scripts/MediaStreamTrack.cs index 3bf49047a5..dfcf963642 100644 --- a/Runtime/Scripts/MediaStreamTrack.cs +++ b/Runtime/Scripts/MediaStreamTrack.cs @@ -205,6 +205,40 @@ internal RTCTrackEvent(IntPtr ptrTransceiver, RTCPeerConnection peer) } } +#if UNITY_WEBGL + public class RTCVideoTrackEvent + { + public RTCRtpTransceiver Transceiver { get; } + + public RTCRtpReceiver Receiver + { + get + { + return Transceiver.Receiver; + } + } + + public MediaStreamTrack Track + { + get + { + return Receiver.Track; + } + } + + public int Width { get; } + public int Height { get; } + + internal RTCVideoTrackEvent(IntPtr ptrTransceiver, RTCPeerConnection peer, int width, int height) + { + Transceiver = WebRTC.FindOrCreate( + ptrTransceiver, ptr => new RTCRtpTransceiver(ptr, peer)); + Width = width; + Height = height; + } + } +#endif + /// /// /// diff --git a/Runtime/Scripts/RTCDataChannel.cs b/Runtime/Scripts/RTCDataChannel.cs index 0f6a7370ef..1bb448e242 100644 --- a/Runtime/Scripts/RTCDataChannel.cs +++ b/Runtime/Scripts/RTCDataChannel.cs @@ -218,6 +218,10 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or /// public delegate void DelegateOnDataChannel(RTCDataChannel channel); +#if UNITY_WEBGL + public delegate void DelegateOnTextMessage(string msg); +#endif + /// /// Represents the method that will be invoked when an error occurs on the data channel. /// @@ -287,6 +291,9 @@ public static explicit operator RTCDataChannelInitInternal(RTCDataChannelInit or public class RTCDataChannel : RefCountedObject { private DelegateOnMessage onMessage; +#if UNITY_WEBGL + private DelegateOnTextMessage onTextMessage; +#endif private DelegateOnOpen onOpen; private DelegateOnClose onClose; private DelegateOnError onError; @@ -325,6 +332,17 @@ public DelegateOnMessage OnMessage set => onMessage = value; } +#if UNITY_WEBGL + /// + /// + /// + public DelegateOnTextMessage OnTextMessage + { + get { return onTextMessage; } + set { onTextMessage = value; } + } +#endif + /// /// Delegate to be called when the data channel's message transport mechanism is opened or reopened. /// @@ -753,6 +771,21 @@ static void DataChannelNativeOnMessage(IntPtr ptr, byte[] msg, int size) }); } +#if UNITY_WEBGL + [AOT.MonoPInvokeCallback(typeof(DelegateNativeOnMessage))] + static void DataChannelNativeOnTextMessage(IntPtr ptr, IntPtr msgPtr) + { + WebRTC.Sync(ptr, () => + { + if (WebRTC.Table[ptr] is RTCDataChannel channel) + { + var msg = msgPtr.AsAnsiStringWithFreeMem(); + channel.onTextMessage?.Invoke(msg); + } + }); + } +#endif + [AOT.MonoPInvokeCallback(typeof(DelegateNativeOnOpen))] static void DataChannelNativeOnOpen(IntPtr ptr) { diff --git a/Runtime/Scripts/RTCIceCandidate.cs b/Runtime/Scripts/RTCIceCandidate.cs index ce00232554..c1f1b56358 100644 --- a/Runtime/Scripts/RTCIceCandidate.cs +++ b/Runtime/Scripts/RTCIceCandidate.cs @@ -1,23 +1,24 @@ using System; using System.Runtime.InteropServices; +using UnityEngine; namespace Unity.WebRTC { /// - /// + /// /// public class RTCIceCandidateInit { /// - /// + /// /// public string candidate; /// - /// + /// /// public string sdpMid; /// - /// + /// /// public int? sdpMLineIndex; } @@ -29,68 +30,68 @@ public class RTCIceCandidateInit public enum RTCIceComponent : int { /// - /// + /// /// Rtp = 1, /// - /// + /// /// Rtcp = 2, } /// - /// + /// /// public enum RTCIceProtocol : int { /// - /// + /// /// Udp = 1, /// - /// + /// /// Tcp = 2 } /// - /// + /// /// public enum RTCIceCandidateType { /// - /// + /// /// Host, /// - /// + /// /// Srflx, /// - /// + /// /// Prflx, /// - /// + /// /// Relay } /// - /// + /// /// public enum RTCIceTcpCandidateType { /// - /// + /// /// Active, /// - /// + /// /// Passive, /// - /// + /// /// So } @@ -114,8 +115,10 @@ public static RTCIceCandidateType ParseRTCIceCandidateType(this string src) { switch (src) { + case "host": case "local": return RTCIceCandidateType.Host; + case "srflx": case "stun": return RTCIceCandidateType.Srflx; case "prflx": @@ -147,64 +150,64 @@ public static RTCIceCandidateType ParseRTCIceCandidateType(this string src) } /// - /// + /// /// public class RTCIceCandidate : IDisposable { /// - /// + /// /// public string Candidate => NativeMethods.IceCandidateGetSdp(self); /// - /// + /// /// public string SdpMid => NativeMethods.IceCandidateGetSdpMid(self); /// - /// + /// /// public int? SdpMLineIndex => NativeMethods.IceCandidateGetSdpLineIndex(self); /// - /// + /// /// public string Foundation => _candidate.foundation; /// - /// + /// /// public RTCIceComponent? Component => _candidate.component; /// - /// + /// /// public uint Priority => _candidate.priority; /// - /// + /// /// public string Address => _candidate.address; /// - /// + /// /// public RTCIceProtocol? Protocol => _candidate.protocol.ParseRTCIceProtocol(); /// - /// + /// /// public ushort? Port => _candidate.port; /// - /// + /// /// public RTCIceCandidateType? Type => _candidate.type.ParseRTCIceCandidateType(); /// - /// + /// /// public RTCIceTcpCandidateType? TcpType => _candidate.tcpType.ParseRTCIceTcpCandidateType(); /// - /// + /// /// public string RelatedAddress => _candidate.relatedAddress; /// - /// + /// /// public ushort? RelatedPort => _candidate.relatedPort; /// - /// + /// /// public string UserNameFragment => _candidate.usernameFragment; @@ -214,7 +217,7 @@ public class RTCIceCandidate : IDisposable private bool disposed; /// - /// + /// /// ~RTCIceCandidate() { @@ -242,16 +245,21 @@ public void Dispose() } /// - /// + /// /// /// +#if !UNITY_WEBGL public RTCIceCandidate(RTCIceCandidateInit candidateInfo = null) +#else + public RTCIceCandidate(RTCIceCandidateInit candidateInfo = null, IntPtr? iceCandidatePtr = null) +#endif { candidateInfo = candidateInfo ?? new RTCIceCandidateInit(); if (candidateInfo.sdpMLineIndex == null && candidateInfo.sdpMid == null) throw new ArgumentException("sdpMid and sdpMLineIndex are both null"); - RTCIceCandidateInitInternal option = (RTCIceCandidateInitInternal)candidateInfo; + RTCIceCandidateInitInternal option = (RTCIceCandidateInitInternal) candidateInfo; +#if !UNITY_WEBGL RTCErrorType error = NativeMethods.CreateIceCandidate(ref option, out self); if (error != RTCErrorType.None) throw new ArgumentException( @@ -259,8 +267,18 @@ public RTCIceCandidate(RTCIceCandidateInit candidateInfo = null) $"candidate:{candidateInfo.candidate}\n" + $"sdpMid:{candidateInfo.sdpMid}\n" + $"sdpMLineIndex:{candidateInfo.sdpMLineIndex}\n"); - NativeMethods.IceCandidateGetCandidate(self, out _candidate); +#else + + if (iceCandidatePtr == null) + { + iceCandidatePtr = NativeMethods.CreateNativeRTCIceCandidate(option.candidate, option.sdpMid, option.sdpMLineIndex); + } + + self = iceCandidatePtr.Value; + string json = NativeMethods.IceCandidateGetCandidate(self); + _candidate = JsonUtility.FromJson(json); +#endif } } diff --git a/Runtime/Scripts/RTCPeerConnection.cs b/Runtime/Scripts/RTCPeerConnection.cs index 48071fb517..3fb056bbf6 100644 --- a/Runtime/Scripts/RTCPeerConnection.cs +++ b/Runtime/Scripts/RTCPeerConnection.cs @@ -1,7 +1,12 @@ using System; +using System.Linq; using System.Collections.Generic; +using System.Runtime.InteropServices; + using UnityEngine; +using Newtonsoft.Json; + namespace Unity.WebRTC { /// @@ -286,7 +291,12 @@ private void DisposeAllTransceivers() public IEnumerable GetReceivers() { IntPtr buf = WebRTC.Context.PeerConnectionGetReceivers(GetSelfOrThrow(), out ulong length); +#if !UNITY_WEBGL return WebRTC.Deserialize(buf, (int)length, CreateReceiver); +#else + var arr = NativeMethods.ptrToIntPtrArray(buf); + return WebRTC.Deserialize(arr, ptr => new RTCRtpReceiver(ptr, this)); +#endif } /// @@ -313,7 +323,12 @@ public IEnumerable GetReceivers() public IEnumerable GetSenders() { var buf = WebRTC.Context.PeerConnectionGetSenders(GetSelfOrThrow(), out ulong length); +#if !UNITY_WEBGL return WebRTC.Deserialize(buf, (int)length, CreateSender); +#else + var arr = NativeMethods.ptrToIntPtrArray(buf); + return WebRTC.Deserialize(arr, ptr => new RTCRtpSender(ptr, this)); +#endif } /// @@ -339,7 +354,12 @@ public IEnumerable GetSenders() public IEnumerable GetTransceivers() { var buf = WebRTC.Context.PeerConnectionGetTransceivers(GetSelfOrThrow(), out ulong length); +#if !UNITY_WEBGL return WebRTC.Deserialize(buf, (int)length, CreateTransceiver); +#else + var arr = NativeMethods.ptrToIntPtrArray(buf); + return WebRTC.Deserialize(arr, ptr => new RTCRtpTransceiver(ptr, this)); +#endif } RTCRtpReceiver CreateReceiver(IntPtr ptr) @@ -415,7 +435,11 @@ internal IntPtr GetSelfOrThrow() } [AOT.MonoPInvokeCallback(typeof(DelegateNativeOnIceCandidate))] +#if !UNITY_WEBGL static void PCOnIceCandidate(IntPtr ptr, string sdp, string sdpMid, int sdpMlineIndex) +#else + static void PCOnIceCandidate(IntPtr ptr, IntPtr iceCandidatePtr, string sdp, string sdpMid, int sdpMlineIndex) +#endif { WebRTC.Sync(ptr, () => { @@ -427,7 +451,11 @@ static void PCOnIceCandidate(IntPtr ptr, string sdp, string sdpMid, int sdpMline sdpMid = sdpMid, sdpMLineIndex = sdpMlineIndex }; +#if !UNITY_WEBGL var candidate = new RTCIceCandidate(options); +#else + var candidate = new RTCIceCandidate(options, iceCandidatePtr); +#endif connection.OnIceCandidate?.Invoke(candidate); } }); @@ -715,9 +743,17 @@ public RTCRtpSender AddTrack(MediaStreamTrack track, MediaStream stream = null) if (track == null) throw new ArgumentNullException("track is null."); +#if !UNITY_WEBGL var streamId = stream?.Id; RTCErrorType error = NativeMethods.PeerConnectionAddTrack( GetSelfOrThrow(), track.GetSelfOrThrow(), streamId, out var ptr); +#else + var streamPtr = stream == null ? IntPtr.Zero : stream.GetSelfOrThrow(); + IntPtr buf = NativeMethods.PeerConnectionAddTrack(GetSelfOrThrow(), track.GetSelfOrThrow(), streamPtr); + var arr = NativeMethods.ptrToIntPtrArray(buf); + RTCErrorType error = (RTCErrorType) arr[0]; + var ptr = arr[1]; +#endif if (error != RTCErrorType.None) throw new InvalidOperationException($"error occurred :{error}"); cacheTracks.Add(track); @@ -1026,7 +1062,7 @@ public RTCSetSessionDescriptionAsyncOperation SetLocalDescription( /// { /// RTCSetSessionDescriptionAsyncOperation asyncOperation = peerConnection.SetLocalDescription(ref description); /// yield return asyncOperation; - /// + /// /// if (asyncOperation.IsError) /// { /// Debug.LogError("Failed to set local description: " + asyncOperation.Error.message); @@ -1112,19 +1148,31 @@ public RTCSetSessionDescriptionAsyncOperation SetRemoteDescription( /// public RTCStatsReportAsyncOperation GetStats() { +#if !UNITY_WEBGL RTCStatsCollectorCallback callback = NativeMethods.PeerConnectionGetStats(GetSelfOrThrow()); return GetStats(callback); +#else + throw new NotImplementedException(); +#endif } internal RTCStatsReportAsyncOperation GetStats(RTCRtpSender sender) { +#if !UNITY_WEBGL RTCStatsCollectorCallback callback = NativeMethods.PeerConnectionSenderGetStats(GetSelfOrThrow(), sender.self); return GetStats(callback); +#else + throw new NotImplementedException(); +#endif } internal RTCStatsReportAsyncOperation GetStats(RTCRtpReceiver receiver) { +#if !UNITY_WEBGL RTCStatsCollectorCallback callback = NativeMethods.PeerConnectionReceiverGetStats(GetSelfOrThrow(), receiver.self); return GetStats(callback); +#else + throw new NotImplementedException(); +#endif } RTCStatsReportAsyncOperation GetStats(RTCStatsCollectorCallback callback) @@ -1160,10 +1208,19 @@ public RTCSessionDescription LocalDescription get { RTCSessionDescription desc = default; +#if !UNITY_WEBGL if (NativeMethods.PeerConnectionGetLocalDescription(GetSelfOrThrow(), ref desc)) { return desc; } +#else + string ret = NativeMethods.PeerConnectionGetLocalDescription(GetSelfOrThrow()); + if(!"false".Equals(ret)) + { + desc = JsonConvert.DeserializeObject(ret); + return desc; + } +#endif throw new InvalidOperationException("LocalDescription is not exist"); } } @@ -1176,10 +1233,19 @@ public RTCSessionDescription RemoteDescription get { RTCSessionDescription desc = default; +#if !UNITY_WEBGL if (NativeMethods.PeerConnectionGetRemoteDescription(GetSelfOrThrow(), ref desc)) { return desc; } +#else + string ret = NativeMethods.PeerConnectionGetRemoteDescription(GetSelfOrThrow()); + if(!"false".Equals(ret)) + { + desc = JsonConvert.DeserializeObject(ret); + return desc; + } +#endif throw new InvalidOperationException("RemoteDescription is not exist"); } } @@ -1193,10 +1259,19 @@ public RTCSessionDescription CurrentLocalDescription get { RTCSessionDescription desc = default; +#if !UNITY_WEBGL if (NativeMethods.PeerConnectionGetCurrentLocalDescription(GetSelfOrThrow(), ref desc)) { return desc; } +#else + string ret = NativeMethods.PeerConnectionGetCurrentLocalDescription(GetSelfOrThrow()); + if(!"false".Equals(ret)) + { + desc = JsonConvert.DeserializeObject(ret); + return desc; + } +#endif throw new InvalidOperationException("CurrentLocalDescription is not exist"); } } @@ -1210,10 +1285,19 @@ public RTCSessionDescription CurrentRemoteDescription get { RTCSessionDescription desc = default; +#if !UNITY_WEBGL if (NativeMethods.PeerConnectionGetCurrentRemoteDescription(GetSelfOrThrow(), ref desc)) { return desc; } +#else + string ret = NativeMethods.PeerConnectionGetCurrentRemoteDescription(GetSelfOrThrow()); + if(!"false".Equals(ret)) + { + desc = JsonConvert.DeserializeObject(ret); + return desc; + } +#endif throw new InvalidOperationException("CurrentRemoteDescription is not exist"); } } @@ -1226,10 +1310,19 @@ public RTCSessionDescription PendingLocalDescription get { RTCSessionDescription desc = default; +#if !UNITY_WEBGL if (NativeMethods.PeerConnectionGetPendingLocalDescription(GetSelfOrThrow(), ref desc)) { return desc; } +#else + string ret = NativeMethods.PeerConnectionGetPendingLocalDescription(GetSelfOrThrow()); + if(!"false".Equals(ret)) + { + desc = JsonConvert.DeserializeObject(ret); + return desc; + } +#endif throw new InvalidOperationException("PendingLocalDescription is not exist"); } } @@ -1242,10 +1335,19 @@ public RTCSessionDescription PendingRemoteDescription get { RTCSessionDescription desc = default; +#if !UNITY_WEBGL if (NativeMethods.PeerConnectionGetPendingRemoteDescription(GetSelfOrThrow(), ref desc)) { return desc; } +#else + string ret = NativeMethods.PeerConnectionGetPendingRemoteDescription(GetSelfOrThrow()); + if(!"false".Equals(ret)) + { + desc = JsonConvert.DeserializeObject(ret); + return desc; + } +#endif throw new InvalidOperationException("PendingRemoteDescription is not exist"); } } @@ -1325,33 +1427,45 @@ internal void RemoveObserver(SetSessionDescriptionObserver observer) static SetSessionDescriptionObserver PeerConnectionSetLocalDescription( IntPtr ptr, ref RTCSessionDescription desc, out RTCError error) { +#if !UNITY_WEBGL IntPtr ptrError = IntPtr.Zero; SetSessionDescriptionObserver observer = NativeMethods.PeerConnectionSetLocalDescription(ptr, ref desc, out var errorType, ref ptrError); string message = ptrError != IntPtr.Zero ? ptrError.AsAnsiStringWithFreeMem() : null; error = new RTCError { errorType = errorType, message = message }; return observer; +#else + throw new NotImplementedException(); +#endif } static SetSessionDescriptionObserver PeerConnectionSetLocalDescription(IntPtr ptr, out RTCError error) { +#if !UNITY_WEBGL IntPtr ptrError = IntPtr.Zero; SetSessionDescriptionObserver observer = NativeMethods.PeerConnectionSetLocalDescriptionWithoutDescription(ptr, out var errorType, ref ptrError); string message = ptrError != IntPtr.Zero ? ptrError.AsAnsiStringWithFreeMem() : null; error = new RTCError { errorType = errorType, message = message }; return observer; +#else + throw new NotImplementedException(); +#endif } static SetSessionDescriptionObserver PeerConnectionSetRemoteDescription( IntPtr ptr, ref RTCSessionDescription desc, out RTCError error) { +#if !UNITY_WEBGL IntPtr ptrError = IntPtr.Zero; SetSessionDescriptionObserver observer = NativeMethods.PeerConnectionSetRemoteDescription(ptr, ref desc, out var errorType, ref ptrError); string message = ptrError != IntPtr.Zero ? ptrError.AsAnsiStringWithFreeMem() : null; error = new RTCError { errorType = errorType, message = message }; return observer; +#else + throw new NotImplementedException(); +#endif } static IntPtr PeerConnectionAddTransceiver(IntPtr pc, IntPtr track, RTCRtpTransceiverInit init) diff --git a/Runtime/Scripts/RTCRtpReceiver.cs b/Runtime/Scripts/RTCRtpReceiver.cs index a2239a0000..777088f3cc 100644 --- a/Runtime/Scripts/RTCRtpReceiver.cs +++ b/Runtime/Scripts/RTCRtpReceiver.cs @@ -1,3 +1,4 @@ +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -5,7 +6,7 @@ namespace Unity.WebRTC { /// - /// + /// /// public class RTCRtpContributingSource { @@ -88,24 +89,33 @@ public override void Dispose() } if (self != IntPtr.Zero && !WebRTC.Context.IsNull) { +#if UNITY_WEBGL + NativeMethods.DeleteReceiver(self); +#endif WebRTC.Table.Remove(self); } base.Dispose(); } /// - /// + /// /// /// /// public static RTCRtpCapabilities GetCapabilities(TrackKind kind) { + +#if !UNITY_WEBGL WebRTC.Context.GetReceiverCapabilities(kind, out IntPtr ptr); RTCRtpCapabilitiesInternal capabilitiesInternal = Marshal.PtrToStructure(ptr); RTCRtpCapabilities capabilities = new RTCRtpCapabilities(capabilitiesInternal); Marshal.FreeHGlobal(ptr); return capabilities; +#else + return WebRTC.Context.GetReceiverCapabilities(kind); +#endif + } /// diff --git a/Runtime/Scripts/RTCRtpSender.cs b/Runtime/Scripts/RTCRtpSender.cs index 4a47fc3ae4..d0572ff54a 100644 --- a/Runtime/Scripts/RTCRtpSender.cs +++ b/Runtime/Scripts/RTCRtpSender.cs @@ -1,5 +1,9 @@ using System; using System.Runtime.InteropServices; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + using UnityEngine; namespace Unity.WebRTC @@ -57,6 +61,9 @@ public override void Dispose() } if (self != IntPtr.Zero && !WebRTC.Context.IsNull) { +#if UNITY_WEBGL + NativeMethods.DeleteSender(self); +#endif WebRTC.Table.Remove(self); } base.Dispose(); @@ -83,12 +90,17 @@ public override void Dispose() /// public static RTCRtpCapabilities GetCapabilities(TrackKind kind) { + +#if !UNITY_WEBGL WebRTC.Context.GetSenderCapabilities(kind, out IntPtr ptr); RTCRtpCapabilitiesInternal capabilitiesInternal = Marshal.PtrToStructure(ptr); RTCRtpCapabilities capabilities = new RTCRtpCapabilities(capabilitiesInternal); Marshal.FreeHGlobal(ptr); return capabilities; +#else + return WebRTC.Context.GetSenderCapabilities(kind); +#endif } /// @@ -148,7 +160,11 @@ public RTCRtpTransform Transform // cache reference transform = value; +#if !UNITY_WEBGL NativeMethods.SenderSetTransform(GetSelfOrThrow(), value.self); +#else + throw new NotImplementedException(); +#endif } get { @@ -215,11 +231,17 @@ public bool SyncApplicationFramerate /// public RTCRtpSendParameters GetParameters() { + +#if !UNITY_WEBGL NativeMethods.SenderGetParameters(GetSelfOrThrow(), out var ptr); RTCRtpSendParametersInternal parametersInternal = Marshal.PtrToStructure(ptr); RTCRtpSendParameters parameters = new RTCRtpSendParameters(ref parametersInternal); Marshal.FreeHGlobal(ptr); return parameters; +#else + string json = NativeMethods.SenderGetParameters(self); + return JsonConvert.DeserializeObject(json); +#endif } /// @@ -268,12 +290,20 @@ public RTCError SetParameters(RTCRtpSendParameters parameters) } } +#if !UNITY_WEBGL parameters.CreateInstance(out RTCRtpSendParametersInternal instance); IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(instance)); Marshal.StructureToPtr(instance, ptr, false); RTCErrorType type = NativeMethods.SenderSetParameters(GetSelfOrThrow(), ptr); Marshal.FreeCoTaskMem(ptr); return new RTCError { errorType = type }; +#else + string json = JsonConvert.SerializeObject(parameters, Formatting.None, new JsonSerializerSettings{NullValueHandling = NullValueHandling.Ignore}); + + //TODO Get correct RTCErrorType from jslib. + NativeMethods.SenderSetParameters(self, json); + return new RTCError { errorType = RTCErrorType.None }; +#endif } /// diff --git a/Runtime/Scripts/RTCRtpTransceiver.cs b/Runtime/Scripts/RTCRtpTransceiver.cs index c42a6730e5..55772cfdb1 100644 --- a/Runtime/Scripts/RTCRtpTransceiver.cs +++ b/Runtime/Scripts/RTCRtpTransceiver.cs @@ -1,4 +1,6 @@ using System; +using System.Runtime.InteropServices; +using Newtonsoft.Json; namespace Unity.WebRTC { @@ -60,6 +62,11 @@ public override void Dispose() } if (self != IntPtr.Zero && !WebRTC.Context.IsNull) { + +#if UNITY_WEBGL + NativeMethods.DeleteTransceiver(self); +#endif + WebRTC.Table.Remove(self); } base.Dispose(); @@ -105,12 +112,18 @@ public RTCRtpTransceiverDirection? CurrentDirection { get { +#if !UNITY_WEBGL if (NativeMethods.TransceiverGetCurrentDirection(GetSelfOrThrow(), out var direction)) { return direction; } return null; +#else + int currentDirection = NativeMethods.TransceiverGetCurrentDirection(GetSelfOrThrow()); + if (currentDirection == -1) return null; + else return (RTCRtpTransceiverDirection) currentDirection; +#endif } } @@ -166,6 +179,7 @@ public RTCRtpSender Sender /// public RTCErrorType SetCodecPreferences(RTCRtpCodecCapability[] codecs) { +#if !UNITY_WEBGL RTCRtpCodecCapabilityInternal[] array = Array.ConvertAll(codecs, v => v.Cast()); MarshallingArray instance = array; RTCErrorType error = NativeMethods.TransceiverSetCodecPreferences(GetSelfOrThrow(), instance.ptr, instance.length); @@ -175,6 +189,13 @@ public RTCErrorType SetCodecPreferences(RTCRtpCodecCapability[] codecs) } instance.Dispose(); return error; +#else + string json = JsonConvert.SerializeObject(codecs, Formatting.None, new JsonSerializerSettings{NullValueHandling = NullValueHandling.Ignore}); + + //TODO Get correct RTCErrorType from jslib. + NativeMethods.TransceiverSetCodecPreferences(self, json); + return RTCErrorType.None; +#endif } /// diff --git a/Runtime/Scripts/RTPParameters.cs b/Runtime/Scripts/RTPParameters.cs index f3a1ef808b..c9f13d0414 100644 --- a/Runtime/Scripts/RTPParameters.cs +++ b/Runtime/Scripts/RTPParameters.cs @@ -538,5 +538,4 @@ public void Dispose() streams.Dispose(); } } - } diff --git a/Runtime/Scripts/VideoStreamTrack.cs b/Runtime/Scripts/VideoStreamTrack.cs index d036a6f5f6..6d9fd9060c 100644 --- a/Runtime/Scripts/VideoStreamTrack.cs +++ b/Runtime/Scripts/VideoStreamTrack.cs @@ -335,7 +335,11 @@ static IntPtr CreateVideoTrack(Texture texture, out VideoTrackSource source) var label = Guid.NewGuid().ToString(); source = new VideoTrackSource(); +#if !UNITY_WEBGL return WebRTC.Context.CreateVideoTrack(label, source.GetSelfOrThrow()); +#else + return WebRTC.Context.CreateVideoTrack(source.sourceTexture_.GetNativeTexturePtr(),source.destTexture_.GetNativeTexturePtr(),source.sourceTexture_.width,source.sourceTexture_.height); +#endif } /// diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 1f8b239d97..2d6be5365a 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -3,10 +3,16 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; +using System.Runtime.CompilerServices; +using System.IO; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; +#if UNITY_WEBGL +[assembly: UnityEngine.Scripting.Preserve] +#endif + namespace Unity.WebRTC { /// @@ -633,6 +639,13 @@ public enum RTCIceTransportPolicy : int [Serializable] public struct RTCConfiguration { +#if UNITY_WEBGL + /// + /// + /// + /// public string label; +#endif + /// /// List of RTCIceServer objects, each describing one server which may be used by the ICE agent. /// @@ -729,7 +742,7 @@ public enum NativeLoggingSeverity /// public static class WebRTC { -#if UNITY_IOS +#if UNITY_IOS || UNITY_WEBGL internal const string Lib = "__Internal"; #else internal const string Lib = "webrtc"; @@ -918,6 +931,7 @@ internal static void DisposeInternal() s_context = null; } NativeMethods.RegisterDebugLog(null, false, NativeLoggingSeverity.Info); + s_syncContext = null; } internal static RTCError ValidateTextureSize(int width, int height, RuntimePlatform platform) @@ -1152,8 +1166,13 @@ internal static void DelayActionOnMainThread(Action callback, float delay) internal static void Sync(IntPtr ptr, Action callback) { +#if !UNITY_WEBGL s_syncContext.Post(SendOrPostCallback, new CallbackObject(ptr, callback)); +#else + callback(); +#endif } + internal static string GetModuleName() { return System.IO.Path.GetFileName(Lib); @@ -1205,6 +1224,16 @@ internal static IEnumerable Deserialize(IntPtr buf, int length, Func(); + foreach (var ptr in array) + { + list.Add(FindOrCreate(ptr, constructor)); + } + return list; + } + + internal static IEnumerable Deserialize(IntPtr[] array, Func constructor) where T : class + { var list = new List(); foreach (var ptr in array) { @@ -1347,7 +1376,6 @@ static void OnSetTransformedFrame(IntPtr ptr, IntPtr frame) } } - internal static Context Context { get { return s_context; } } internal static WeakReferenceTable Table { get { return s_context?.table; } } @@ -1391,8 +1419,17 @@ internal static IReadOnlyList> PeerList internal delegate void DelegateNativeOnConnectionStateChange(IntPtr ptr, RTCPeerConnectionState state); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void DelegateNativeOnIceGatheringChange(IntPtr ptr, RTCIceGatheringState state); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DelegateNativePeerConnectionSetSessionDescSuccess(IntPtr result); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DelegateNativePeerConnectionSetSessionDescFailure(IntPtr result, int errorType, IntPtr error); +#if !UNITY_WEBGL [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void DelegateNativeOnIceCandidate(IntPtr ptr, [MarshalAs(UnmanagedType.LPStr)] string candidate, [MarshalAs(UnmanagedType.LPStr)] string sdpMid, int sdpMlineIndex); +#else + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DelegateNativeOnIceCandidate(IntPtr ptr, IntPtr iceCandidatePtr, [MarshalAs(UnmanagedType.LPStr)] string candidate, [MarshalAs(UnmanagedType.LPStr)] string sdpMid, int sdpMlineIndex); +#endif [UnmanagedFunctionPointer(CallingConvention.Cdecl)] //according to JS API naming, use OnNegotiationNeeded instead of OnRenegotiationNeeded internal delegate void DelegateNativeOnNegotiationNeeded(IntPtr ptr); @@ -1404,6 +1441,10 @@ internal static IReadOnlyList> PeerList internal delegate void DelegateNativeOnDataChannel(IntPtr ptr, IntPtr ptrChannel); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void DelegateNativeOnMessage(IntPtr ptr, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] bytes, int size); +#if UNITY_WEBGL + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DelegateNativeOnTextMessage(IntPtr ptr, IntPtr msg); +#endif [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void DelegateNativeOnOpen(IntPtr ptr); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -1427,12 +1468,27 @@ internal static class NativeMethods [DllImport(WebRTC.Lib)] public static extern void RegisterRenderingWebRTCPlugin(); #endif + + public static IntPtr[] ptrToIntPtrArray(IntPtr ptr) + { + int len = Marshal.ReadInt32(ptr); + int[] arr = new int[len]; + Marshal.Copy(IntPtr.Add(ptr, 4), arr, 0, len); + IntPtr[] ret = new IntPtr[len]; + for (var i = 0; i < len; i++) + ret[i] = new IntPtr(arr[i]); + return ret; + } + [DllImport(WebRTC.Lib)] public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(UnmanagedType.U1)] bool enableNativeLog, NativeLoggingSeverity nativeLoggingSeverity); [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreate(int uid); [DllImport(WebRTC.Lib)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetHardwareEncoderSupport(); + [DllImport(WebRTC.Lib)] public static extern void ContextDestroy(int uid); [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreatePeerConnection(IntPtr ptr); @@ -1446,16 +1502,27 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern void PeerConnectionRestartIce(IntPtr ptr); [DllImport(WebRTC.Lib)] public static extern RTCErrorType PeerConnectionSetConfiguration(IntPtr ptr, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string conf); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreateDataChannel(IntPtr ptr, IntPtr ptrPeer, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string label, ref RTCDataChannelInitInternal options); +#else + [DllImport(WebRTC.Lib)] + public static extern IntPtr ContextCreateDataChannel(IntPtr ptr, IntPtr ptrPeer, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string label, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string optionsJson); +#endif [DllImport(WebRTC.Lib)] public static extern void ContextDeleteDataChannel(IntPtr ptr, IntPtr ptrChannel); + [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreateAudioTrackSource(IntPtr ptr); [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreateVideoTrackSource(IntPtr ptr); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreateVideoTrack(IntPtr ptr, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string label, IntPtr trackSource); +#else + [DllImport(WebRTC.Lib)] + public static extern IntPtr ContextCreateVideoTrack(IntPtr self, IntPtr srcTexturePtr, IntPtr dstTexturePtr, int width, int height); +#endif [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreateAudioTrack(IntPtr ptr, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string label, IntPtr trackSource); [DllImport(WebRTC.Lib)] @@ -1469,13 +1536,22 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm [DllImport(WebRTC.Lib)] public static extern void ContextDeleteRefPtr(IntPtr context, IntPtr ptr); [DllImport(WebRTC.Lib)] + public static extern void ContextSetVideoEncoderParameter(IntPtr context, IntPtr track, int width, int height, GraphicsFormat format, IntPtr texturePtr); + [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreateFrameTransformer(IntPtr context); [DllImport(WebRTC.Lib)] public static extern IntPtr PeerConnectionGetConfiguration(IntPtr ptr); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern CreateSessionDescriptionObserver PeerConnectionCreateOffer(IntPtr context, IntPtr ptr, ref RTCOfferAnswerOptions options); [DllImport(WebRTC.Lib)] public static extern CreateSessionDescriptionObserver PeerConnectionCreateAnswer(IntPtr context, IntPtr ptr, ref RTCOfferAnswerOptions options); +#else + [DllImport(WebRTC.Lib)] + public static extern void PeerConnectionCreateOffer(IntPtr ptr, string options); + [DllImport(WebRTC.Lib)] + public static extern void PeerConnectionCreateAnswer(IntPtr ptr, string options); +#endif [DllImport(WebRTC.Lib)] public static extern void StatsCollectorRegisterCallback(DelegateCollectStats onCollectStats); [DllImport(WebRTC.Lib)] @@ -1494,6 +1570,11 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern void PeerConnectionRegisterIceGatheringChange(IntPtr ptr, DelegateNativeOnIceGatheringChange callback); [DllImport(WebRTC.Lib)] public static extern void PeerConnectionRegisterOnIceCandidate(IntPtr ptr, DelegateNativeOnIceCandidate callback); + [DllImport(WebRTC.Lib)] + public static extern void PeerConnectionRegisterOnSetSessionDescSuccess(IntPtr pc, IntPtr ptr, DelegateNativePeerConnectionSetSessionDescSuccess callback); + [DllImport(WebRTC.Lib)] + public static extern void PeerConnectionRegisterOnSetSessionDescFailure(IntPtr pc, IntPtr ptr, DelegateNativePeerConnectionSetSessionDescFailure callback); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern SetSessionDescriptionObserver PeerConnectionSetLocalDescription(IntPtr ptr, ref RTCSessionDescription desc, out RTCErrorType errorType, ref IntPtr error); [DllImport(WebRTC.Lib)] @@ -1504,12 +1585,35 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern RTCStatsCollectorCallback PeerConnectionGetStats(IntPtr ptr); [DllImport(WebRTC.Lib)] public static extern RTCStatsCollectorCallback PeerConnectionSenderGetStats(IntPtr ptr, IntPtr sender); +#else + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionSetLocalDescription(IntPtr context, IntPtr ptr, RTCSdpType type, string sdp); + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionSetLocalDescriptionWithoutDescription(IntPtr context, IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionSetRemoteDescription(IntPtr context, IntPtr ptr, RTCSdpType type, string sdp); + [DllImport(WebRTC.Lib)] + public static extern void PeerConnectionGetStats(IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern void PeerConnectionSenderGetStats(IntPtr ptr, IntPtr sender); +#endif + +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern void ContextGetSenderCapabilities(IntPtr context, TrackKind kind, out IntPtr capabilities); +#else + [DllImport(WebRTC.Lib)] + public static extern string ContextGetSenderCapabilities(IntPtr context, TrackKind kind); +#endif +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern void ContextGetReceiverCapabilities(IntPtr context, TrackKind kind, out IntPtr capabilities); +#else [DllImport(WebRTC.Lib)] - public static extern RTCStatsCollectorCallback PeerConnectionReceiverGetStats(IntPtr sender, IntPtr receiver); + public static extern string ContextGetReceiverCapabilities(IntPtr context, TrackKind kind); +#endif + +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool PeerConnectionCanTrickleIceCandidates(IntPtr ptr, [MarshalAs(UnmanagedType.U1)] out bool value); @@ -1533,6 +1637,33 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern bool PeerConnectionGetCurrentRemoteDescription(IntPtr ptr, ref RTCSessionDescription desc); [DllImport(WebRTC.Lib)] public static extern RTCErrorType PeerConnectionAddTrack(IntPtr pc, IntPtr track, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string streamId, out IntPtr sender); +#else + public static bool PeerConnectionCanTrickleIceCandidates(IntPtr ptr, out bool value) => throw new NotImplementedException(); + public static bool PeerConnectionGetLocalDescription(IntPtr ptr, ref RTCSessionDescription desc) => throw new NotImplementedException(); + public static bool PeerConnectionGetRemoteDescription(IntPtr ptr, ref RTCSessionDescription desc) => throw new NotImplementedException(); + public static bool PeerConnectionGetPendingLocalDescription(IntPtr ptr, ref RTCSessionDescription desc) => throw new NotImplementedException(); + public static bool PeerConnectionGetPendingRemoteDescription(IntPtr ptr, ref RTCSessionDescription desc) => throw new NotImplementedException(); + public static bool PeerConnectionGetCurrentLocalDescription(IntPtr ptr, ref RTCSessionDescription desc) => throw new NotImplementedException(); + public static bool PeerConnectionGetCurrentRemoteDescription(IntPtr ptr, ref RTCSessionDescription desc) => throw new NotImplementedException(); + public static RTCErrorType PeerConnectionAddTrack(IntPtr pc, IntPtr track, string streamId, out IntPtr sender) => throw new NotImplementedException(); + + [DllImport(WebRTC.Lib)] + public static extern string PeerConnectionGetLocalDescription(IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern string PeerConnectionGetRemoteDescription(IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern string PeerConnectionGetCurrentLocalDescription(IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern string PeerConnectionGetCurrentRemoteDescription(IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern string PeerConnectionGetPendingLocalDescription(IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern string PeerConnectionGetPendingRemoteDescription(IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionAddTrack(IntPtr pc, IntPtr track, IntPtr streamId); +#endif + [DllImport(WebRTC.Lib)] + public static extern RTCStatsCollectorCallback PeerConnectionReceiverGetStats(IntPtr sender, IntPtr receiver); [DllImport(WebRTC.Lib)] public static extern IntPtr PeerConnectionAddTransceiver(IntPtr pc, IntPtr track); [DllImport(WebRTC.Lib)] @@ -1546,10 +1677,15 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm [DllImport(WebRTC.Lib)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool PeerConnectionAddIceCandidate(IntPtr ptr, IntPtr candidate); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern RTCErrorType CreateIceCandidate(ref RTCIceCandidateInitInternal options, out IntPtr candidate); +#endif + [DllImport(WebRTC.Lib)] + public static extern IntPtr CreateNativeRTCIceCandidate(string candidate, string sdpMid, int sdpMLineIndex); [DllImport(WebRTC.Lib)] public static extern RTCErrorType DeleteIceCandidate(IntPtr candidate); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern void IceCandidateGetCandidate(IntPtr candidate, out CandidateInternal dst); [DllImport(WebRTC.Lib)] @@ -1560,14 +1696,35 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm [DllImport(WebRTC.Lib)] [return: MarshalAs(UnmanagedType.LPStr)] public static extern string IceCandidateGetSdpMid(IntPtr candidate); +#else + [DllImport(WebRTC.Lib)] + public static extern string IceCandidateGetCandidate(IntPtr candidate); + [DllImport(WebRTC.Lib)] + public static extern int IceCandidateGetSdpLineIndex(IntPtr candidate); + [DllImport(WebRTC.Lib)] + [return: MarshalAs(UnmanagedType.LPStr)] + public static extern string IceCandidateGetSdp(IntPtr candidate); + [DllImport(WebRTC.Lib)] + [return: MarshalAs(UnmanagedType.LPStr)] + public static extern string IceCandidateGetSdpMid(IntPtr candidate); +#endif [DllImport(WebRTC.Lib)] public static extern RTCPeerConnectionState PeerConnectionState(IntPtr ptr); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern IntPtr PeerConnectionGetReceivers(IntPtr context, IntPtr ptr, out ulong length); [DllImport(WebRTC.Lib)] public static extern IntPtr PeerConnectionGetSenders(IntPtr context, IntPtr ptr, out ulong length); [DllImport(WebRTC.Lib)] public static extern IntPtr PeerConnectionGetTransceivers(IntPtr context, IntPtr ptr, out ulong length); +#else + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionGetReceivers(IntPtr context, IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionGetSenders(IntPtr context, IntPtr ptr); + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionGetTransceivers(IntPtr context, IntPtr ptr); +#endif [DllImport(WebRTC.Lib)] public static extern RTCIceConnectionState PeerConnectionIceConditionState(IntPtr ptr); [DllImport(WebRTC.Lib)] @@ -1582,9 +1739,14 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern void PeerConnectionRegisterOnTrack(IntPtr ptr, DelegateNativeOnTrack callback); [DllImport(WebRTC.Lib)] public static extern void PeerConnectionRegisterOnRemoveTrack(IntPtr ptr, DelegateNativeOnRemoveTrack callback); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool TransceiverGetCurrentDirection(IntPtr transceiver, out RTCRtpTransceiverDirection direction); +#else + [DllImport(WebRTC.Lib)] + public static extern int TransceiverGetCurrentDirection(IntPtr transceiver); +#endif [DllImport(WebRTC.Lib)] public static extern RTCErrorType TransceiverStop(IntPtr transceiver); [DllImport(WebRTC.Lib)] @@ -1593,20 +1755,32 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern RTCRtpTransceiverDirection TransceiverGetDirection(IntPtr transceiver); [DllImport(WebRTC.Lib)] public static extern RTCErrorType TransceiverSetDirection(IntPtr transceiver, RTCRtpTransceiverDirection direction); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern RTCErrorType TransceiverSetCodecPreferences(IntPtr transceiver, IntPtr capabilities, long length); +#else + [DllImport(WebRTC.Lib)] + public static extern RTCErrorType TransceiverSetCodecPreferences(IntPtr transceiver, string capabilities); +#endif [DllImport(WebRTC.Lib)] public static extern IntPtr TransceiverGetReceiver(IntPtr transceiver); [DllImport(WebRTC.Lib)] public static extern IntPtr TransceiverGetSender(IntPtr transceiver); [DllImport(WebRTC.Lib)] public static extern IntPtr SenderGetTrack(IntPtr sender); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern IntPtr SenderSetTransform(IntPtr sender, IntPtr transform); [DllImport(WebRTC.Lib)] public static extern void SenderGetParameters(IntPtr sender, out IntPtr parameters); [DllImport(WebRTC.Lib)] public static extern RTCErrorType SenderSetParameters(IntPtr sender, IntPtr parameters); +#else + [DllImport(WebRTC.Lib)] + public static extern string SenderGetParameters(IntPtr sender); + [DllImport(WebRTC.Lib)] + public static extern RTCErrorType SenderSetParameters(IntPtr sender, string parameters); +#endif [DllImport(WebRTC.Lib)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool SenderReplaceTrack(IntPtr sender, IntPtr track); @@ -1648,6 +1822,10 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern void DataChannelClose(IntPtr ptr); [DllImport(WebRTC.Lib)] public static extern void DataChannelRegisterOnMessage(IntPtr ctx, IntPtr ptr, DelegateNativeOnMessage callback); +#if UNITY_WEBGL + [DllImport(WebRTC.Lib)] + public static extern void DataChannelRegisterOnTextMessage(IntPtr ptr, DelegateNativeOnTextMessage callback); +#endif [DllImport(WebRTC.Lib)] public static extern void DataChannelRegisterOnOpen(IntPtr ctx, IntPtr ptr, DelegateNativeOnOpen callback); [DllImport(WebRTC.Lib)] @@ -1656,6 +1834,10 @@ public static extern void RegisterDebugLog(DelegateDebugLog func, [MarshalAs(Unm public static extern void DataChannelRegisterOnError(IntPtr ctx, IntPtr ptr, DelegateNativeOnError callback); [DllImport(WebRTC.Lib)] public static extern IntPtr ContextCreateMediaStream(IntPtr ctx, [MarshalAs(UnmanagedType.LPStr, SizeConst = 256)] string label); +#if UNITY_WEBGL + [DllImport(WebRTC.Lib)] + public static extern void MediaStreamAddUserMedia(IntPtr streamPtr, string constraints); +#endif [DllImport(WebRTC.Lib)] public static extern void ContextRegisterMediaStreamObserver(IntPtr ctx, IntPtr stream); [DllImport(WebRTC.Lib)] @@ -1677,10 +1859,17 @@ public static extern void AudioTrackSinkProcessAudio( [DllImport(WebRTC.Lib)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool MediaStreamRemoveTrack(IntPtr stream, IntPtr track); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern IntPtr MediaStreamGetVideoTracks(IntPtr stream, out ulong length); [DllImport(WebRTC.Lib)] public static extern IntPtr MediaStreamGetAudioTracks(IntPtr stream, out ulong length); +#else + [DllImport(WebRTC.Lib)] + public static extern IntPtr MediaStreamGetVideoTracks(IntPtr stream); + [DllImport(WebRTC.Lib)] + public static extern IntPtr MediaStreamGetAudioTracks(IntPtr stream); +#endif [DllImport(WebRTC.Lib)] public static extern IntPtr MediaStreamGetID(IntPtr stream); [DllImport(WebRTC.Lib)] @@ -1726,6 +1915,7 @@ public static extern IntPtr CreateVideoRenderer( public static extern void VideoSourceSetSyncApplicationFramerate(IntPtr source, [MarshalAs(UnmanagedType.U1)] bool value); [DllImport(WebRTC.Lib)] public static extern IntPtr StatsGetJson(IntPtr stats); +#if !UNITY_WEBGL [DllImport(WebRTC.Lib)] public static extern IntPtr StatsGetId(IntPtr stats); [DllImport(WebRTC.Lib)] @@ -1789,10 +1979,88 @@ public static extern IntPtr CreateVideoRenderer( public static extern bool VideoFrameIsKeyFrame(IntPtr frame); [DllImport(WebRTC.Lib)] public static extern void FrameTransformerSendFrameToSink(IntPtr transform, IntPtr frame); +#else + public static IntPtr StatsGetId(IntPtr stats){ return default; } + public static RTCStatsType StatsGetType(IntPtr stats){ return default; } + public static long StatsGetTimestamp(IntPtr stats){ return default; } + public static IntPtr StatsGetMembers(IntPtr stats, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetName(IntPtr member){ return default; } + public static StatsMemberType StatsMemberGetType(IntPtr member){ return default; } + public static bool StatsMemberIsDefined(IntPtr member){ return default; } + public static bool StatsMemberGetBool(IntPtr member){ return default; } + public static int StatsMemberGetInt(IntPtr member){ return default; } + public static uint StatsMemberGetUnsignedInt(IntPtr member){ return default; } + public static long StatsMemberGetLong(IntPtr member){ return default; } + public static ulong StatsMemberGetUnsignedLong(IntPtr member){ return default; } + public static double StatsMemberGetDouble(IntPtr member){ return default; } + public static IntPtr StatsMemberGetString(IntPtr member){ return default; } + public static IntPtr StatsMemberGetBoolArray(IntPtr member, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetIntArray(IntPtr member, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetUnsignedIntArray(IntPtr member, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetLongArray(IntPtr member, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetUnsignedLongArray(IntPtr member, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetDoubleArray(IntPtr member, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetStringArray(IntPtr member, out ulong length){ length = default; return default; } + public static IntPtr StatsMemberGetMapStringUint64(IntPtr member, out IntPtr values, out ulong length){ values = default; length = default; return default; } + public static IntPtr StatsMemberGetMapStringDouble(IntPtr member, out IntPtr values, out ulong length){ values = default; length = default; return default; } + public static uint FrameGetTimestamp(IntPtr frame) => default; + public static uint FrameGetSsrc(IntPtr frame) => default; + public static void FrameGetData(IntPtr frame, out IntPtr data, out int size) { size = default; data = default; } + public static void FrameSetData(IntPtr frame, IntPtr data, int size) { } + public static IntPtr VideoFrameGetMetadata(IntPtr frame) => default; + [return: MarshalAs(UnmanagedType.U1)] + public static bool VideoFrameIsKeyFrame(IntPtr frame) => default; + public static void FrameTransformerSendFrameToSink(IntPtr transform, IntPtr frame) { } + [UnityEngine.Scripting.Preserve] [DllImport(WebRTC.Lib)] - public static extern void SetGraphicsSyncTimeout(uint nSecTimeout); + public static extern IntPtr CreateAudioTrack(); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr CreateVideoTrack(); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr CreateMediaStream(); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr CreatePeerConnection(); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr CreatePeerConnectionWithConfig(string confJson); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionSetDescription(IntPtr peerPtr, RTCSdpType type, string sdp, string side); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr PeerConnectionSetDescriptionWithoutDescription(IntPtr peerPtr); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr CreateDataChannel(); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr DeleteMediaStream(IntPtr streamPtr); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr DeleteReceiver(IntPtr receiverPtr); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr DeleteSender(IntPtr senderPtr); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr DeleteTransceiver(IntPtr transceiverPtr); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern void RenderLocalVideotrack(IntPtr trackPtr, bool needFlip); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern void UpdateRendererTexture(IntPtr trackPtr, IntPtr renderTexturePtr, bool needFlip); + [UnityEngine.Scripting.Preserve] + [DllImport(WebRTC.Lib)] + public static extern IntPtr CreateNativeTexture(); +#endif + [DllImport(WebRTC.Lib)] + public static extern void SetGraphicsSyncTimeout(uint nSecTimeout); } internal static class VideoUpdateMethods diff --git a/Runtime/Unity.WebRTC.Runtime.asmdef b/Runtime/Unity.WebRTC.Runtime.asmdef index a913ec4726..953b8f6f9d 100644 --- a/Runtime/Unity.WebRTC.Runtime.asmdef +++ b/Runtime/Unity.WebRTC.Runtime.asmdef @@ -1,5 +1,6 @@ { "name": "Unity.WebRTC", + "rootNamespace": "", "references": [], "includePlatforms": [ "Android", @@ -7,6 +8,7 @@ "iOS", "LinuxStandalone64", "macOSStandalone", + "WebGL", "WindowsStandalone64" ], "excludePlatforms": [], diff --git a/Samples~/Audio/AudioSample.cs b/Samples~/Audio/AudioSample.cs index dcc7947d75..218d163054 100644 --- a/Samples~/Audio/AudioSample.cs +++ b/Samples~/Audio/AudioSample.cs @@ -65,7 +65,6 @@ void Start() toggleEnableMicrophone.isOn = false; toggleEnableMicrophone.onValueChanged.AddListener(OnEnableMicrophone); - toggleEnableMicrophone.isOn = false; toggleLoopback.onValueChanged.AddListener(OnChangeLoopback); dropdownAudioClips.interactable = true; dropdownAudioClips.options = @@ -129,8 +128,10 @@ void OnStart() { m_deviceName = dropdownMicrophoneDevices.captionText.text; m_clipInput = Microphone.Start(m_deviceName, true, m_lengthSeconds, m_samplingFrequency); +#if !UNITY_WEBGL // set the latency to “0” samples before the audio starts to play. while (!(Microphone.GetPosition(m_deviceName) > 0)) { } +#endif } else { diff --git a/Samples~/DataChannel/DataChannelSample.cs b/Samples~/DataChannel/DataChannelSample.cs index 5e23634b42..f2e95c01c1 100644 --- a/Samples~/DataChannel/DataChannelSample.cs +++ b/Samples~/DataChannel/DataChannelSample.cs @@ -22,6 +22,9 @@ class DataChannelSample : MonoBehaviour private DelegateOnIceCandidate pc1OnIceCandidate; private DelegateOnIceCandidate pc2OnIceCandidate; private DelegateOnMessage onDataChannelMessage; +#if UNITY_WEBGL + private DelegateOnTextMessage onDataChannelTextMessage; +#endif private DelegateOnOpen onDataChannelOpen; private DelegateOnClose onDataChannelClose; private DelegateOnDataChannel onDataChannel; @@ -46,8 +49,14 @@ private void Start() { remoteDataChannel = channel; remoteDataChannel.OnMessage = onDataChannelMessage; +#if UNITY_WEBGL + remoteDataChannel.OnTextMessage = onDataChannelTextMessage; +#endif }; onDataChannelMessage = bytes => { textReceive.text = System.Text.Encoding.UTF8.GetString(bytes); }; +#if UNITY_WEBGL + onDataChannelTextMessage = text => {textReceive.text = text;}; +#endif onDataChannelOpen = () => { sendButton.interactable = true; diff --git a/Samples~/Menu/Menu.unity b/Samples~/Menu/Menu.unity index d281113ea7..f1733ca0ca 100644 --- a/Samples~/Menu/Menu.unity +++ b/Samples~/Menu/Menu.unity @@ -7818,6 +7818,85 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1943595677 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1943595678} + - component: {fileID: 1943595680} + - component: {fileID: 1943595679} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1943595678 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1943595677} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1369578414} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1943595679 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1943595677} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 32 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 3 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: WebGL +--- !u!222 &1943595680 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1943595677} + m_CullTransparentMesh: 1 --- !u!1 &1993818744 GameObject: m_ObjectHideFlags: 0 diff --git a/Samples~/Scripts/SceneSelectUI.cs b/Samples~/Scripts/SceneSelectUI.cs index 08f0f34c55..e2f1fdb57c 100644 --- a/Samples~/Scripts/SceneSelectUI.cs +++ b/Samples~/Scripts/SceneSelectUI.cs @@ -53,6 +53,7 @@ internal class SceneSelectUI : MonoBehaviour [SerializeField] private Button buttonSimulcast; [SerializeField] private Button buttonMetadata; [SerializeField] private Button buttonEncryption; + [SerializeField] private Button buttonWebGL; List streamSizeList = new List() { @@ -133,6 +134,7 @@ void Start() buttonSimulcast.onClick.AddListener(OnPressedSimulcastButton); buttonMetadata.onClick.AddListener(OnPressedMetadataButton); buttonEncryption.onClick.AddListener(OnPressedEncryption); + buttonWebGL.onClick.AddListener(OnPressedWebGLButton); // This sample uses Compute Shader, so almost Android devices don't work correctly. if (!SystemInfo.supportsComputeShaders) @@ -275,5 +277,10 @@ private void OnPressedEncryption() { SceneManager.LoadScene("Encryption", LoadSceneMode.Single); } + + private void OnPressedWebGLButton() + { + SceneManager.LoadScene("WebGL", LoadSceneMode.Single); + } } } diff --git a/Samples~/WebGL.meta b/Samples~/WebGL.meta new file mode 100644 index 0000000000..a5bfc3fc4d --- /dev/null +++ b/Samples~/WebGL.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8008a7029f8f1f94d9f50d390fdfcb41 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/WebGL/WebGL.unity b/Samples~/WebGL/WebGL.unity new file mode 100644 index 0000000000..7fb56aa581 --- /dev/null +++ b/Samples~/WebGL/WebGL.unity @@ -0,0 +1,2386 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &141885429 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 141885430} + - component: {fileID: 141885432} + - component: {fileID: 141885431} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &141885430 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 141885429} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 370567958} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 728.0002, y: 33.89309} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &141885431 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 141885429} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!222 &141885432 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 141885429} + m_CullTransparentMesh: 0 +--- !u!1001 &158196923 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 858092574} + m_Modifications: + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_Pivot.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_Pivot.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_AnchorMin.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_AnchorMin.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_SizeDelta.x + value: 100 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_SizeDelta.y + value: 100 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4037113455314838175, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + propertyPath: m_Name + value: BackButtonManager + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 7aa5bec5b1e406445af144843fe4d62c, type: 3} +--- !u!224 &158196924 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4037113455314838168, guid: 7aa5bec5b1e406445af144843fe4d62c, + type: 3} + m_PrefabInstance: {fileID: 158196923} + m_PrefabAsset: {fileID: 0} +--- !u!1 &370567957 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 370567958} + - component: {fileID: 370567959} + m_Layer: 5 + m_Name: vertical layout + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &370567958 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 370567957} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 372881754} + - {fileID: 1198425309} + - {fileID: 2015830100} + - {fileID: 141885430} + m_Father: {fileID: 858092574} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &370567959 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 370567957} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 50 + m_Bottom: 0 + m_ChildAlignment: 4 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!1 &372881753 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 372881754} + - component: {fileID: 372881755} + m_Layer: 5 + m_Name: horizontal layout + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &372881754 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 372881753} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1839397353} + - {fileID: 2123783123} + m_Father: {fileID: 370567958} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 699.99994, y: 240} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &372881755 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 372881753} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 4 + m_Spacing: 10 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!1 &483628950 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 483628951} + - component: {fileID: 483628953} + - component: {fileID: 483628952} + m_Layer: 5 + m_Name: RemoteCandidate + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &483628951 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 483628950} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1039299225} + m_Father: {fileID: 1198425309} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &483628952 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 483628950} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'RemoteCandidateId:' +--- !u!222 &483628953 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 483628950} + m_CullTransparentMesh: 1 +--- !u!1 &562611379 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 562611380} + - component: {fileID: 562611382} + - component: {fileID: 562611381} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &562611380 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562611379} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1344461798} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &562611381 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562611379} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: HangUp +--- !u!222 &562611382 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562611379} + m_CullTransparentMesh: 0 +--- !u!1 &626388471 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 626388472} + - component: {fileID: 626388474} + - component: {fileID: 626388473} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &626388472 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 626388471} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2066401218} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &626388473 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 626388471} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Call +--- !u!222 &626388474 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 626388471} + m_CullTransparentMesh: 0 +--- !u!1 &645746053 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 645746055} + - component: {fileID: 645746054} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &645746054 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 645746053} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &645746055 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 645746053} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &814089928 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 814089931} + - component: {fileID: 814089930} + m_Layer: 0 + m_Name: UICamera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!20 &814089930 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 814089928} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 1, g: 1, b: 1, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &814089931 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 814089928} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 823.3642, y: 439.6452, z: -229.16667} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &858092570 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 858092574} + - component: {fileID: 858092573} + - component: {fileID: 858092572} + - component: {fileID: 858092571} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &858092571 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 858092570} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &858092572 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 858092570} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &858092573 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 858092570} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &858092574 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 858092570} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 370567958} + - {fileID: 158196924} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &906436438 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 906436439} + - component: {fileID: 906436442} + - component: {fileID: 906436441} + - component: {fileID: 906436440} + m_Layer: 5 + m_Name: RestartIce + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &906436439 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 906436438} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1590714497} + m_Father: {fileID: 2015830100} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 32} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &906436440 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 906436438} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 0 + m_TargetGraphic: {fileID: 906436441} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &906436441 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 906436438} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &906436442 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 906436438} + m_CullTransparentMesh: 0 +--- !u!1 &934963286 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 934963289} + - component: {fileID: 934963288} + - component: {fileID: 934963287} + - component: {fileID: 934963290} + m_Layer: 0 + m_Name: MainCamera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &934963287 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 934963286} + m_Enabled: 1 +--- !u!20 &934963288 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 934963286} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 1} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &934963289 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 934963286} + m_LocalRotation: {x: -0.08717229, y: 0.8995905, z: -0.21045253, w: -0.37262273} + m_LocalPosition: {x: 826.31604, y: 441.7115, z: -226.21486} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &934963290 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 934963286} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 112b75fc4a0640e1a80310351b92471d, type: 3} + m_Name: + m_EditorClassIdentifier: + startButton: {fileID: 1290512353} + callButton: {fileID: 2066401219} + restartButton: {fileID: 906436440} + hangUpButton: {fileID: 1344461799} + localCandidateId: {fileID: 1021020669} + remoteCandidateId: {fileID: 1039299226} + cam: {fileID: 934963288} + sourceImage: {fileID: 1839397354} + receiveImage: {fileID: 2123783124} + rotateObject: {fileID: 1823897046} +--- !u!1 &989765905 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 989765906} + - component: {fileID: 989765908} + - component: {fileID: 989765907} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &989765906 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 989765905} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1290512352} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &989765907 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 989765905} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Start +--- !u!222 &989765908 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 989765905} + m_CullTransparentMesh: 0 +--- !u!1 &1021020667 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1021020668} + - component: {fileID: 1021020670} + - component: {fileID: 1021020669} + m_Layer: 5 + m_Name: LocalCandidateId + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1021020668 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1021020667} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1314460981} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 220, y: 0} + m_SizeDelta: {x: 200, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1021020669 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1021020667} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 6 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: NotConnected +--- !u!222 &1021020670 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1021020667} + m_CullTransparentMesh: 1 +--- !u!1 &1039299224 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1039299225} + - component: {fileID: 1039299227} + - component: {fileID: 1039299226} + m_Layer: 5 + m_Name: RemoteCandidateId + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1039299225 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1039299224} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 483628951} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 235, y: 0} + m_SizeDelta: {x: 200, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1039299226 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1039299224} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 6 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: NotConnected +--- !u!222 &1039299227 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1039299224} + m_CullTransparentMesh: 1 +--- !u!1 &1068560632 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1068560635} + - component: {fileID: 1068560634} + - component: {fileID: 1068560633} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1068560633 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1068560632} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1068560634 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1068560632} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &1068560635 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1068560632} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1198425308 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1198425309} + - component: {fileID: 1198425310} + m_Layer: 5 + m_Name: horizontal layout + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1198425309 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1198425308} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1314460981} + - {fileID: 483628951} + m_Father: {fileID: 370567958} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 728.004, y: 33.47383} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1198425310 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1198425308} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 20 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!1 &1290512351 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1290512352} + - component: {fileID: 1290512355} + - component: {fileID: 1290512354} + - component: {fileID: 1290512353} + m_Layer: 5 + m_Name: Start + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1290512352 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290512351} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 989765906} + m_Father: {fileID: 2015830100} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 32} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1290512353 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290512351} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1290512354} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &1290512354 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290512351} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1290512355 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1290512351} + m_CullTransparentMesh: 0 +--- !u!1 &1314460980 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1314460981} + - component: {fileID: 1314460983} + - component: {fileID: 1314460982} + m_Layer: 5 + m_Name: LocalCandidate + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1314460981 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1314460980} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1021020668} + m_Father: {fileID: 1198425309} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1314460982 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1314460980} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'LocalCandidateId:' +--- !u!222 &1314460983 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1314460980} + m_CullTransparentMesh: 1 +--- !u!1 &1344461797 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1344461798} + - component: {fileID: 1344461801} + - component: {fileID: 1344461800} + - component: {fileID: 1344461799} + m_Layer: 5 + m_Name: HangUp + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1344461798 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1344461797} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 562611380} + m_Father: {fileID: 2015830100} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1344461799 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1344461797} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 0 + m_TargetGraphic: {fileID: 1344461800} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &1344461800 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1344461797} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1344461801 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1344461797} + m_CullTransparentMesh: 0 +--- !u!1 &1590714496 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1590714497} + - component: {fileID: 1590714499} + - component: {fileID: 1590714498} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1590714497 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1590714496} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 906436439} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1590714498 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1590714496} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: RestartIce +--- !u!222 &1590714499 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1590714496} + m_CullTransparentMesh: 0 +--- !u!1 &1823897042 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1823897046} + - component: {fileID: 1823897045} + - component: {fileID: 1823897044} + - component: {fileID: 1823897043} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1823897043 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1823897042} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1823897044 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1823897042} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1823897045 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1823897042} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1823897046 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1823897042} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 823.3642, y: 439.6452, z: -229.16667} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1839397352 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1839397353} + - component: {fileID: 1839397355} + - component: {fileID: 1839397354} + m_Layer: 5 + m_Name: SourceRawImage + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1839397353 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1839397352} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 372881754} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 320, y: 240} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1839397354 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1839397352} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Texture: {fileID: 0} + m_UVRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 +--- !u!222 &1839397355 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1839397352} + m_CullTransparentMesh: 0 +--- !u!1 &2015830099 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2015830100} + - component: {fileID: 2015830101} + m_Layer: 5 + m_Name: horizontal layout + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2015830100 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2015830099} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1290512352} + - {fileID: 2066401218} + - {fileID: 906436439} + - {fileID: 1344461798} + m_Father: {fileID: 370567958} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 728.004, y: 33.47383} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2015830101 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2015830099} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 20 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!1 &2066401217 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2066401218} + - component: {fileID: 2066401221} + - component: {fileID: 2066401220} + - component: {fileID: 2066401219} + m_Layer: 5 + m_Name: Call + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2066401218 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2066401217} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 626388472} + m_Father: {fileID: 2015830100} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 32} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2066401219 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2066401217} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 0 + m_TargetGraphic: {fileID: 2066401220} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &2066401220 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2066401217} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2066401221 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2066401217} + m_CullTransparentMesh: 0 +--- !u!1 &2123783122 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2123783123} + - component: {fileID: 2123783125} + - component: {fileID: 2123783124} + m_Layer: 5 + m_Name: ReceiveRawImage + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2123783123 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2123783122} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 372881754} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 320, y: 240} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2123783124 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2123783122} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Texture: {fileID: 0} + m_UVRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 +--- !u!222 &2123783125 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2123783122} + m_CullTransparentMesh: 0 diff --git a/Samples~/WebGL/WebGL.unity.meta b/Samples~/WebGL/WebGL.unity.meta new file mode 100644 index 0000000000..63700816dc --- /dev/null +++ b/Samples~/WebGL/WebGL.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8566d76aec4c20e4e819f649f8c65945 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/WebGL/WebGLSample.cs b/Samples~/WebGL/WebGLSample.cs new file mode 100644 index 0000000000..b9c5a60247 --- /dev/null +++ b/Samples~/WebGL/WebGLSample.cs @@ -0,0 +1,424 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Unity.WebRTC; +using Unity.WebRTC.Samples; +using UnityEngine.UI; +using static Unity.WebRTC.MediaStream; +using Button = UnityEngine.UI.Button; + +class WebGLSample : MonoBehaviour +{ +#pragma warning disable 0649 + [SerializeField] + private Button startButton; + [SerializeField] + private Button callButton; + [SerializeField] + private Button restartButton; + [SerializeField] + private Button hangUpButton; + [SerializeField] + private Text localCandidateId; + [SerializeField] + private Text remoteCandidateId; + + [SerializeField] + private Camera cam; + [SerializeField] + private RawImage sourceImage; + [SerializeField] + private RawImage receiveImage; + [SerializeField] + private Transform rotateObject; +#pragma warning restore 0649 + + private RTCPeerConnection _pc1, _pc2; + private List pc1Senders; + private MediaStream videoStream, receiveStream; + private DelegateOnIceConnectionChange pc1OnIceConnectionChange; + private DelegateOnIceConnectionChange pc2OnIceConnectionChange; + private DelegateOnIceCandidate pc1OnIceCandidate; + private DelegateOnIceCandidate pc2OnIceCandidate; + private DelegateOnTrack pc2Ontrack; + private DelegateOnNegotiationNeeded pc1OnNegotiationNeeded; + private bool videoUpdateStarted; + + private const int width = 1280; + private const int height = 720; + + private void Awake() + { + startButton.onClick.AddListener(OnStart); + callButton.onClick.AddListener(Call); + restartButton.onClick.AddListener(RestartIce); + hangUpButton.onClick.AddListener(HangUp); + receiveStream = new MediaStream(); + } + + private void Start() + { + pc1Senders = new List(); + callButton.interactable = false; + restartButton.interactable = false; + hangUpButton.interactable = false; + + pc1OnIceConnectionChange = state => { OnIceConnectionChange(_pc1, state); }; + pc2OnIceConnectionChange = state => { OnIceConnectionChange(_pc2, state); }; + pc1OnIceCandidate = candidate => { OnIceCandidate(_pc1, candidate); }; + pc2OnIceCandidate = candidate => { OnIceCandidate(_pc2, candidate); }; + pc2Ontrack = e => { receiveStream.AddTrack(e.Track); }; + pc1OnNegotiationNeeded = () => { StartCoroutine(PeerNegotiationNeeded(_pc1)); }; + + receiveStream.OnAddTrack = e => + { + if (e.Track is VideoStreamTrack track) + { + receiveImage.texture = track.Texture; + receiveImage.color = Color.white; + } + }; + } + + private void OnStart() + { + startButton.interactable = false; + callButton.interactable = true; + + if (videoStream == null) + { +#if !UNITY_WEBGL + Debug.LogWarning("UserMedia is only available in WebGL builds, this is equal to PeerConnectionSample"); + videoStream = cam.CaptureStream(width, height); + sourceImage.texture = cam.targetTexture; + sourceImage.color = Color.white; +#else + MediaStreamConstraints constraints = new MediaStreamConstraints(); + videoStream = new MediaStream(); + videoStream.AddUserMedia(constraints); + videoStream.OnAddTrack = e => + { + if (e.Track is VideoStreamTrack track) + { + sourceImage.texture = track.Texture; + sourceImage.color = Color.white; + } + }; +#endif + + } + } + + private void Update() + { + if (rotateObject != null) + { + float t = Time.deltaTime; + rotateObject.Rotate(100 * t, 200 * t, 300 * t); + } + } + + private static RTCConfiguration GetSelectedSdpSemantics() + { + RTCConfiguration config = default; + config.iceServers = new[] {new RTCIceServer {urls = new[] {"stun:stun.l.google.com:19302"}}}; + + return config; + } + + private void OnIceConnectionChange(RTCPeerConnection pc, RTCIceConnectionState state) + { + Debug.Log($"{GetName(pc)} IceConnectionState: {state}"); + + if (state == RTCIceConnectionState.Connected || state == RTCIceConnectionState.Completed) + { + StartCoroutine(CheckStats(pc)); + } + } + + // Display the video codec that is actually used. + IEnumerator CheckStats(RTCPeerConnection pc) + { + yield return new WaitForSeconds(0.1f); + + if (pc == null) + yield break; + + var op = pc.GetStats(); + + yield return op; + + if (op.IsError) + { + Debug.LogErrorFormat("RTCPeerConnection.GetStats failed: {0}", op.Error); + + yield break; + } + + RTCStatsReport report = op.Value; + RTCIceCandidatePairStats activeCandidatePairStats = null; + RTCIceCandidateStats remoteCandidateStats = null; + + foreach (var transportStatus in report.Stats.Values.OfType()) + { + if (report.Stats.TryGetValue(transportStatus.selectedCandidatePairId, out var tmp)) + { + activeCandidatePairStats = tmp as RTCIceCandidatePairStats; + } + } + + if (activeCandidatePairStats == null || string.IsNullOrEmpty(activeCandidatePairStats.remoteCandidateId)) + { + yield break; + } + + foreach (var iceCandidateStatus in report.Stats.Values.OfType()) + { + if (iceCandidateStatus.Id == activeCandidatePairStats.remoteCandidateId) + { + remoteCandidateStats = iceCandidateStatus; + } + } + + if (remoteCandidateStats == null || string.IsNullOrEmpty(remoteCandidateStats.Id)) + { + yield break; + } + + Debug.Log($"{GetName(pc)} candidate stats Id:{remoteCandidateStats.Id}, Type:{remoteCandidateStats.candidateType}"); + var updateText = GetName(pc) == "pc1" ? localCandidateId : remoteCandidateId; + updateText.text = remoteCandidateStats.Id; + } + + IEnumerator PeerNegotiationNeeded(RTCPeerConnection pc) + { + var op = pc.CreateOffer(); + + yield return op; + + if (!op.IsError) + { + if (pc.SignalingState != RTCSignalingState.Stable) + { + Debug.LogError($"{GetName(pc)} signaling state is not stable."); + + yield break; + } + + yield return StartCoroutine(OnCreateOfferSuccess(pc, op.Desc)); + } + else + { + OnCreateSessionDescriptionError(op.Error); + } + } + + private void AddTracks() + { + foreach (var track in videoStream.GetTracks()) + { + pc1Senders.Add(_pc1.AddTrack(track, videoStream)); + } + + if (!videoUpdateStarted) + { + StartCoroutine(WebRTC.Update()); + videoUpdateStarted = true; + } + } + + private void RemoveTracks() + { + foreach (var sender in pc1Senders) + { + _pc1.RemoveTrack(sender); + } + + pc1Senders.Clear(); + + MediaStreamTrack[] tracks = receiveStream.GetTracks().ToArray(); + + foreach (var track in tracks) + { + receiveStream.RemoveTrack(track); + track.Dispose(); + } + } + + private void Call() + { + callButton.interactable = false; + hangUpButton.interactable = true; + restartButton.interactable = true; + + var configuration = GetSelectedSdpSemantics(); + _pc1 = new RTCPeerConnection(ref configuration); + _pc1.OnIceCandidate = pc1OnIceCandidate; + _pc1.OnIceConnectionChange = pc1OnIceConnectionChange; + _pc1.OnNegotiationNeeded = pc1OnNegotiationNeeded; + _pc2 = new RTCPeerConnection(ref configuration); + _pc2.OnIceCandidate = pc2OnIceCandidate; + _pc2.OnIceConnectionChange = pc2OnIceConnectionChange; + _pc2.OnTrack = pc2Ontrack; + + AddTracks(); + } + + private void RestartIce() + { + restartButton.interactable = false; + + _pc1.RestartIce(); + } + + private void HangUp() + { + RemoveTracks(); + + _pc1.Close(); + _pc2.Close(); + _pc1.Dispose(); + _pc2.Dispose(); + _pc1 = null; + _pc2 = null; + + callButton.interactable = true; + restartButton.interactable = false; + hangUpButton.interactable = false; + + receiveImage.color = Color.black; + } + + private void OnIceCandidate(RTCPeerConnection pc, RTCIceCandidate candidate) + { + GetOtherPc(pc).AddIceCandidate(candidate); + Debug.Log($"{GetName(pc)} ICE candidate:\n {candidate.Candidate}"); + } + + private string GetName(RTCPeerConnection pc) + { + return (pc == _pc1) ? "pc1" : "pc2"; + } + + private RTCPeerConnection GetOtherPc(RTCPeerConnection pc) + { + return (pc == _pc1) ? _pc2 : _pc1; + } + + private IEnumerator OnCreateOfferSuccess(RTCPeerConnection pc, RTCSessionDescription desc) + { + Debug.Log($"Offer from {GetName(pc)}\n{desc.sdp}"); + Debug.Log($"{GetName(pc)} setLocalDescription start"); + var op = pc.SetLocalDescription(ref desc); + + yield return op; + + if (!op.IsError) + { + OnSetLocalSuccess(pc); + } + else + { + var error = op.Error; + OnSetSessionDescriptionError(ref error); + + yield break; + } + + var otherPc = GetOtherPc(pc); + Debug.Log($"{GetName(otherPc)} setRemoteDescription start"); + var op2 = otherPc.SetRemoteDescription(ref desc); + + yield return op2; + + if (!op2.IsError) + { + OnSetRemoteSuccess(otherPc); + } + else + { + var error = op2.Error; + OnSetSessionDescriptionError(ref error); + + yield break; + } + + Debug.Log($"{GetName(otherPc)} createAnswer start"); + + // Since the 'remote' side has no media stream we need + // to pass in the right constraints in order for it to + // accept the incoming offer of audio and video. + + var op3 = otherPc.CreateAnswer(); + + yield return op3; + + if (!op3.IsError) + { + yield return OnCreateAnswerSuccess(otherPc, op3.Desc); + } + else + { + OnCreateSessionDescriptionError(op3.Error); + } + } + + private void OnSetLocalSuccess(RTCPeerConnection pc) + { + Debug.Log($"{GetName(pc)} SetLocalDescription complete"); + } + + void OnSetSessionDescriptionError(ref RTCError error) + { + Debug.LogError($"Error Detail Type: {error.message}"); + HangUp(); + } + + private void OnSetRemoteSuccess(RTCPeerConnection pc) + { + Debug.Log($"{GetName(pc)} SetRemoteDescription complete"); + } + + IEnumerator OnCreateAnswerSuccess(RTCPeerConnection pc, RTCSessionDescription desc) + { + Debug.Log($"Answer from {GetName(pc)}:\n{desc.sdp}"); + Debug.Log($"{GetName(pc)} setLocalDescription start"); + var op = pc.SetLocalDescription(ref desc); + + yield return op; + + if (!op.IsError) + { + OnSetLocalSuccess(pc); + } + else + { + var error = op.Error; + OnSetSessionDescriptionError(ref error); + } + + var otherPc = GetOtherPc(pc); + Debug.Log($"{GetName(otherPc)} setRemoteDescription start"); + + var op2 = otherPc.SetRemoteDescription(ref desc); + + yield return op2; + + if (!op2.IsError) + { + OnSetRemoteSuccess(otherPc); + } + else + { + var error = op2.Error; + OnSetSessionDescriptionError(ref error); + } + } + + private static void OnCreateSessionDescriptionError(RTCError error) + { + Debug.LogError($"Error Detail Type: {error.message}"); + } +} diff --git a/Samples~/WebGL/WebGLSample.cs.meta b/Samples~/WebGL/WebGLSample.cs.meta new file mode 100644 index 0000000000..9529c96831 --- /dev/null +++ b/Samples~/WebGL/WebGLSample.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 112b75fc4a0640e1a80310351b92471d +timeCreated: 1625599611 \ No newline at end of file diff --git a/Tests/Editor/PluginTest.cs b/Tests/Editor/PluginTest.cs index 12938c5757..4a3d1897b1 100644 --- a/Tests/Editor/PluginTest.cs +++ b/Tests/Editor/PluginTest.cs @@ -36,7 +36,6 @@ public static void IsPluginLoaded() [Test] public static void CheckPluginImportSettings() { - string[] guids = AssetDatabase.FindAssets("", new[] { "Packages/com.unity.webrtc/Runtime/Plugins" }); foreach (string guid in guids) { @@ -47,11 +46,12 @@ public static void CheckPluginImportSettings() continue; PluginImporter pluginImporter = assetImporter as PluginImporter; Assert.That(pluginImporter, Is.Not.Null); - Assert.That(pluginImporter.isPreloaded, Is.True); + if(!path.Contains("WebGL")) + { + Assert.That(pluginImporter.isPreloaded, Is.True); + } } - } - } } //namespace Unity.WebRTC.EditorTest diff --git a/Tests/Runtime/AudioStreamTrackTest.cs b/Tests/Runtime/AudioStreamTrackTest.cs index 33d1c98bf3..2e5fdd6186 100644 --- a/Tests/Runtime/AudioStreamTrackTest.cs +++ b/Tests/Runtime/AudioStreamTrackTest.cs @@ -12,6 +12,7 @@ namespace Unity.WebRTC.RuntimeTest class AudioStreamTrackTest { [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] [Timeout(5000)] public IEnumerator Constructor() { @@ -110,6 +111,7 @@ public IEnumerator GCCollect() } [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] [Timeout(5000)] public IEnumerator AddMultiAudioTrack() { @@ -182,7 +184,7 @@ public void AudioStreamTrackInstantiateMultiple() UnityEngine.Object.DestroyImmediate(obj1); UnityEngine.Object.DestroyImmediate(obj2); } - +#if !UNITY_WEBGL [Test] public void AudioStreamTrackSetData() { @@ -234,6 +236,7 @@ public void AudioStreamTrackPlayAudio() UnityEngine.Object.DestroyImmediate(obj); } + [Test] public void AudioStreamRenderer() { @@ -243,5 +246,6 @@ public void AudioStreamRenderer() renderer.Dispose(); UnityEngine.Object.DestroyImmediate(obj); } +#endif } } diff --git a/Tests/Runtime/ContextTest.cs b/Tests/Runtime/ContextTest.cs index 2cf9710aee..82d54d84bd 100644 --- a/Tests/Runtime/ContextTest.cs +++ b/Tests/Runtime/ContextTest.cs @@ -2,6 +2,8 @@ using NUnit.Framework; using UnityEngine; +using Debug = UnityEngine.Debug; + namespace Unity.WebRTC.RuntimeTest { class ContextTest @@ -96,7 +98,11 @@ public void CreateAndDeleteVideoTrack() var rt = new UnityEngine.RenderTexture(width, height, 0, format); rt.Create(); var source = context.CreateVideoTrackSource(); +#if UNITY_WEBGL + var track = context.CreateVideoTrack(source, System.IntPtr.Zero, width, height); +#else var track = context.CreateVideoTrack("video", source); +#endif context.DeleteRefPtr(track); context.DeleteRefPtr(source); UnityEngine.Object.DestroyImmediate(rt); diff --git a/Tests/Runtime/DataChannelTest.cs b/Tests/Runtime/DataChannelTest.cs index 0e05898f28..a5e0f2e991 100644 --- a/Tests/Runtime/DataChannelTest.cs +++ b/Tests/Runtime/DataChannelTest.cs @@ -81,7 +81,7 @@ public void CreateDataChannelFailed() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator SendThrowsException() { byte[] message1 = { 1, 2, 3 }; @@ -126,7 +126,7 @@ public IEnumerator SendThrowsException() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator CreateDataChannelMultiple() { var test = new MonoBehaviourTest(); diff --git a/Tests/Runtime/IceCandidateTest.cs b/Tests/Runtime/IceCandidateTest.cs index 387a47b4f2..e305118df5 100644 --- a/Tests/Runtime/IceCandidateTest.cs +++ b/Tests/Runtime/IceCandidateTest.cs @@ -1,5 +1,7 @@ using System; using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; namespace Unity.WebRTC.RuntimeTest { @@ -11,7 +13,13 @@ public void Construct() Assert.Throws(() => new RTCIceCandidate()); } + [Test] + // TODO: Remove [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + // Fix test for WebGL platform. WebRTC returns null for relatedAddress when type is host + // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/relatedAddress - is null for host + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("IceCandidate")] public void ConstructWithOption() { var option = new RTCIceCandidateInit diff --git a/Tests/Runtime/MediaStreamTest.cs b/Tests/Runtime/MediaStreamTest.cs index b2a96b613a..753786488e 100644 --- a/Tests/Runtime/MediaStreamTest.cs +++ b/Tests/Runtime/MediaStreamTest.cs @@ -49,7 +49,8 @@ public void RegisterDelegate() [UnityTest] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] - [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.Android })] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.Android, RuntimePlatform.WebGLPlayer })] + [Category("MediaStream")] public IEnumerator VideoStreamAddTrackAndRemoveTrack() { var width = 256; @@ -118,6 +119,7 @@ public void AddAndRemoveAudioTrack() } [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] public IEnumerator CameraCaptureStream() @@ -140,6 +142,7 @@ public IEnumerator CameraCaptureStream() } [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] public IEnumerator CaptureStream() @@ -167,6 +170,7 @@ public IEnumerator CaptureStream() } [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] public IEnumerator SenderGetStats() @@ -214,6 +218,7 @@ public IEnumerator SenderGetStats() } [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] public IEnumerator ReceiverGetStats() @@ -257,6 +262,7 @@ public IEnumerator ReceiverGetStats() } [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] public IEnumerator SetParametersReturnNoError() @@ -387,7 +393,7 @@ public IEnumerator AddAndRemoveTrack() // todo::(kazuki) Test execution timed out on linux standalone [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.WebGLPlayer})] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] public IEnumerator OnAddTrackDelegatesWithEvent() { @@ -447,6 +453,7 @@ public IEnumerator OnAddTrackDelegatesWithEvent() [UnityTest] [Timeout(5000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer})] public IEnumerator ReceiverGetStreams() { var obj = new GameObject("audio"); diff --git a/Tests/Runtime/MediaStreamTrackTest.cs b/Tests/Runtime/MediaStreamTrackTest.cs index 956343f130..17b953046f 100644 --- a/Tests/Runtime/MediaStreamTrackTest.cs +++ b/Tests/Runtime/MediaStreamTrackTest.cs @@ -56,11 +56,18 @@ public void EqualIdWithAudioTrack() [Test] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] + // TODO: Remove [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + // Requires video refactoring + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] public void EqualIdWithVideoTrack() { var guid = Guid.NewGuid().ToString(); var source = new VideoTrackSource(); +#if UNITY_WEBGL + var track = new VideoStreamTrack(WebRTC.Context.CreateVideoTrack(source.self, IntPtr.Zero, 256, 256)); +#else var track = new VideoStreamTrack(WebRTC.Context.CreateVideoTrack(guid, source.self)); +#endif Assert.That(track, Is.Not.Null); Assert.That(track.Id, Is.EqualTo(guid)); track.Dispose(); @@ -85,6 +92,9 @@ public void AccessAfterDisposed() [Test] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] + // TODO: Remove [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + // Requires video refactoring + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] public void ConstructorThrowsExceptionWhenInvalidGraphicsFormat() { var width = 256; @@ -116,9 +126,9 @@ public void ConstructThrowsExceptionWhenSmallTexture() // todo(kazuki): Crash on windows standalone player [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.WindowsPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.WindowsPlayer, RuntimePlatform.WebGLPlayer})] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] - + [Category("MediaStreamTrack")] public IEnumerator VideoStreamTrackEnabled() { var width = 256; @@ -154,8 +164,9 @@ public IEnumerator VideoStreamTrackEnabled() // todo::(kazuki) Test execution timed out on linux standalone [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer , RuntimePlatform.WebGLPlayer})] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] + [Category("MediaStreamTrack")] public IEnumerator CaptureStreamTrack() { var camObj = new GameObject("Camera"); @@ -220,8 +231,9 @@ public void VideoStreamTrackDisposeImmediately() [UnityTest, LongRunning] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.WebGLPlayer })] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] + [Category("MediaStreamTrack")] public IEnumerator VideoStreamTrackInstantiateMultiple() { var width = 256; diff --git a/Tests/Runtime/NativeAPITest.cs b/Tests/Runtime/NativeAPITest.cs index 3b49ff7cdf..9b1abe71c5 100644 --- a/Tests/Runtime/NativeAPITest.cs +++ b/Tests/Runtime/NativeAPITest.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Runtime.InteropServices; +using Newtonsoft.Json; using NUnit.Framework; using UnityEditor; using UnityEngine; @@ -75,7 +76,13 @@ public void RestartIcePeerConnection() public void PeerConnectionGetReceivers() { var connection = NativeMethods.ContextCreatePeerConnection(context); +#if !UNITY_WEBGL IntPtr buf = NativeMethods.PeerConnectionGetReceivers(context, connection, out ulong length); +#else + IntPtr buf = NativeMethods.PeerConnectionGetReceivers(context, connection); + var arr = NativeMethods.ptrToIntPtrArray(buf); + var length = arr.Length; +#endif Assert.AreEqual(0, length); NativeMethods.ContextDeletePeerConnection(context, connection); } @@ -86,7 +93,12 @@ public void CreateAndDeleteDataChannel() var peer = NativeMethods.ContextCreatePeerConnection(context); var init = (RTCDataChannelInitInternal)new RTCDataChannelInit(); +#if !UNITY_WEBGL var channel = NativeMethods.ContextCreateDataChannel(context, peer, "test", ref init); +#else + var options = JsonConvert.SerializeObject(init); + var channel = NativeMethods.ContextCreateDataChannel(context, peer, "test", options); +#endif NativeMethods.ContextDeleteDataChannel(context, channel); NativeMethods.ContextDeletePeerConnection(context, peer); } @@ -127,7 +139,12 @@ public void AddAndRemoveVideoTrack() var renderTexture = CreateRenderTexture(width, height); var source = NativeMethods.ContextCreateVideoTrackSource(context); +#if !UNITY_WEBGL var track = NativeMethods.ContextCreateVideoTrack(context, "video", source); +#else + var dest = IntPtr.Zero; + var track = NativeMethods.ContextCreateVideoTrack(context, source, dest, width, height); +#endif NativeMethods.ContextDeleteRefPtr(context, track); NativeMethods.ContextDeleteRefPtr(context, source); UnityEngine.Object.DestroyImmediate(renderTexture); @@ -146,8 +163,18 @@ public void AddAndRemoveVideoTrackToPeerConnection() const int height = 720; var renderTexture = CreateRenderTexture(width, height); var source = NativeMethods.ContextCreateVideoTrackSource(context); +#if !UNITY_WEBGL var track = NativeMethods.ContextCreateVideoTrack(context, "video", source); var error = NativeMethods.PeerConnectionAddTrack(peer, track, streamId, out var sender); +#else + var srcPtr = IntPtr.Zero; + var destPtr = IntPtr.Zero; + var track = NativeMethods.ContextCreateVideoTrack(context, srcPtr, destPtr, width, height); + var buf = NativeMethods.PeerConnectionAddTrack(peer, track, stream); + IntPtr[] arr = NativeMethods.ptrToIntPtrArray(buf); + RTCErrorType error = (RTCErrorType) arr[0]; + IntPtr sender = arr[1]; +#endif Assert.That(error, Is.EqualTo(RTCErrorType.None)); var track2 = NativeMethods.SenderGetTrack(sender); @@ -170,18 +197,32 @@ public void SenderGetParameter() string streamId = NativeMethods.MediaStreamGetID(stream).AsAnsiStringWithFreeMem(); Assert.IsNotEmpty(streamId); var source = NativeMethods.ContextCreateVideoTrackSource(context); +#if !UNITY_WEBGL var track = NativeMethods.ContextCreateVideoTrack(context, "video", source); var error = NativeMethods.PeerConnectionAddTrack(peer, track, streamId, out var sender); +#else + var srcPtr = IntPtr.Zero; + var destPtr = IntPtr.Zero; + var track = NativeMethods.ContextCreateVideoTrack(context, srcPtr, destPtr, 256, 256); + var buf = NativeMethods.PeerConnectionAddTrack(peer, track, stream); + IntPtr[] arr = NativeMethods.ptrToIntPtrArray(buf); + RTCErrorType error = (RTCErrorType)arr[0]; + IntPtr sender = arr[1]; +#endif Assert.That(error, Is.EqualTo(RTCErrorType.None)); - +#if !UNITY_WEBGL NativeMethods.SenderGetParameters(sender, out var ptr); var parameters = Marshal.PtrToStructure(ptr); Marshal.FreeHGlobal(ptr); +#else + var ptr = NativeMethods.SenderGetParameters(sender); + var parameters = JsonConvert.DeserializeObject(ptr); +#endif Assert.AreNotEqual(IntPtr.Zero, parameters.encodings); Assert.AreNotEqual(IntPtr.Zero, parameters.transactionId); - Assert.That(NativeMethods.PeerConnectionRemoveTrack(peer, sender), Is.EqualTo(RTCErrorType.None)); + NativeMethods.ContextDeleteRefPtr(context, track); NativeMethods.ContextDeleteRefPtr(context, stream); NativeMethods.ContextDeleteRefPtr(context, source); @@ -200,11 +241,20 @@ public void AddAndRemoveVideoTrackToMediaStream() const int height = 720; var renderTexture = CreateRenderTexture(width, height); var source = NativeMethods.ContextCreateVideoTrackSource(context); +#if !UNITY_WEBGL var track = NativeMethods.ContextCreateVideoTrack(context, "video", source); - +#else + var destPtr = renderTexture.GetNativeTexturePtr(); + var track = NativeMethods.ContextCreateVideoTrack(context, source, destPtr, width, height); +#endif NativeMethods.MediaStreamAddTrack(stream, track); +#if !UNITY_WEBGL IntPtr buf = NativeMethods.MediaStreamGetVideoTracks(stream, out ulong length); +#else + IntPtr buf = NativeMethods.MediaStreamGetVideoTracks(stream); + var length = NativeMethods.ptrToIntPtrArray(buf).Length; +#endif Assert.AreNotEqual(buf, IntPtr.Zero); Assert.Greater(length, 0); @@ -233,11 +283,23 @@ public void AddAndRemoveAudioTrackToMediaStream() var track = NativeMethods.ContextCreateAudioTrack(context, "audio", source); NativeMethods.MediaStreamAddTrack(stream, track); - var trackNativePtr = NativeMethods.MediaStreamGetAudioTracks(stream, out ulong trackSize); - Assert.AreNotEqual(trackNativePtr, IntPtr.Zero); - Assert.Greater(trackSize, 0); - + // This seems to be the same test case as the next one. Just different variable names. +//#if !UNITY_WEBGL +// var trackNativePtr = NativeMethods.MediaStreamGetAudioTracks(stream, out ulong trackSize); +//#else +// var trackNativePtr = NativeMethods.MediaStreamGetAudioTracks(stream); +// var buf = NativeMethods.ptrToIntPtrArray(trackNativePtr); +// var trackSize = buf.Length; +//#endif +// Assert.AreNotEqual(trackNativePtr, IntPtr.Zero); +// Assert.Greater(trackSize, 0); + +#if !UNITY_WEBGL IntPtr buf = NativeMethods.MediaStreamGetAudioTracks(stream, out ulong length); +#else + IntPtr buf = NativeMethods.MediaStreamGetAudioTracks(stream); + var length = NativeMethods.ptrToIntPtrArray(buf).Length; +#endif Assert.AreNotEqual(buf, IntPtr.Zero); Assert.Greater(length, 0); @@ -264,7 +326,14 @@ public void AddAndRemoveAudioTrack() Assert.IsNotEmpty(streamId); var source = NativeMethods.ContextCreateAudioTrackSource(context); var track = NativeMethods.ContextCreateAudioTrack(context, "audio", source); +#if !UNITY_WEBGL var error = NativeMethods.PeerConnectionAddTrack(peer, track, streamId, out var sender); +#else + var buf = NativeMethods.PeerConnectionAddTrack(peer, track, stream); + IntPtr[] arr = NativeMethods.ptrToIntPtrArray(buf); + RTCErrorType error = (RTCErrorType)arr[0]; + IntPtr sender = arr[1]; +#endif Assert.That(error, Is.EqualTo(RTCErrorType.None)); NativeMethods.ContextDeleteRefPtr(context, track); @@ -293,7 +362,12 @@ public void AddAndRemoveVideoRendererToVideoTrack() const int height = 720; var renderTexture = CreateRenderTexture(width, height); var source = NativeMethods.ContextCreateVideoTrackSource(context); +#if !UNITY_WEBGL var track = NativeMethods.ContextCreateVideoTrack(context, "video", source); +#else + var destPtr = renderTexture.GetNativeTexturePtr(); + var track = NativeMethods.ContextCreateVideoTrack(context, source, destPtr, width, height); +#endif var renderer = NativeMethods.CreateVideoRenderer(context, OnVideoFrameResize, true); NativeMethods.VideoTrackAddOrUpdateSink(track, renderer); NativeMethods.VideoTrackRemoveSink(track, renderer); @@ -302,7 +376,7 @@ public void AddAndRemoveVideoRendererToVideoTrack() NativeMethods.ContextDeleteRefPtr(context, source); UnityEngine.Object.DestroyImmediate(renderTexture); } - +#if !UNITY_WEBGL [Test] public void CallGetBatchUpdateEventFunc() { @@ -394,6 +468,7 @@ public void CallGetUpdateTextureFunc() "VideoUpdateMethods.UpdateRendererTexture is not supported on Direct3D12.")] [ConditionalIgnoreMultiple(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer, RuntimePlatform.WebGLPlayer})] public IEnumerator CallVideoUpdateMethodsUpdateRenderer() { const int width = 1280; @@ -437,6 +512,7 @@ public IEnumerator CallVideoUpdateMethodsUpdateRenderer() UnityEngine.Object.DestroyImmediate(renderTexture); UnityEngine.Object.DestroyImmediate(receiveTexture); } +#endif } [UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)] diff --git a/Tests/Runtime/PeerConnectionTest.cs b/Tests/Runtime/PeerConnectionTest.cs index b21dc9b8a0..297ae7f159 100644 --- a/Tests/Runtime/PeerConnectionTest.cs +++ b/Tests/Runtime/PeerConnectionTest.cs @@ -386,7 +386,8 @@ public void CanTrickleIceCandidates() [UnityTest] [Timeout(1000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer, RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator CurrentDirection() { var config = GetDefaultConfiguration(); @@ -446,7 +447,7 @@ public IEnumerator CurrentDirection() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator TransceiverReturnsSender() { RTCConfiguration config = default; @@ -489,6 +490,8 @@ public IEnumerator TransceiverReturnsSender() [UnityTest] [Timeout(1000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator CreateOffer() { var config = GetDefaultConfiguration(); @@ -505,6 +508,8 @@ public IEnumerator CreateOffer() [UnityTest] [Timeout(1000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator CreateAnswerFailed() { var config = GetDefaultConfiguration(); @@ -524,6 +529,8 @@ public IEnumerator CreateAnswerFailed() [UnityTest] [Timeout(1000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator CreateAnswer() { var config = GetDefaultConfiguration(); @@ -553,6 +560,8 @@ public IEnumerator CreateAnswer() [UnityTest] [Timeout(1000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator SetLocalDescription() { var peer = new RTCPeerConnection(); @@ -576,6 +585,12 @@ public IEnumerator SetLocalDescription() } [Test] + // TODO: Remove [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + // Possible fixes: + // - For webgl, add an async method for NativeMethods.SetDescription which returns RTCErrorType + // - Wait for Unity 2021 to support ES6+ syntax, which allows to wait in javascript to return the RTCErrorType + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public void SetLocalDescriptionThrowException() { var peer = new RTCPeerConnection(); @@ -590,7 +605,8 @@ public void SetLocalDescriptionThrowException() [UnityTest] [Timeout(1000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer, RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator SetRemoteDescription() { var config = GetDefaultConfiguration(); @@ -728,6 +744,12 @@ public IEnumerator CreateDescriptionInParallel() } [Test] + // TODO: Remove [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + // Possible fixes: + // - For webgl, add an async method for NativeMethods.SetDescription which returns RTCErrorType + // - Wait for Unity 2021 to support ES6+ syntax, which allows to wait in javascript to return the RTCErrorType + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public void SetRemoteDescriptionThrowException() { var peer = new RTCPeerConnection(); @@ -743,6 +765,8 @@ public void SetRemoteDescriptionThrowException() [UnityTest] [Timeout(1000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator SetLocalDescriptionFailed() { var peer = new RTCPeerConnection(); @@ -776,6 +800,8 @@ public IEnumerator SetLocalDescriptionFailed() [UnityTest] [Timeout(1000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Category("PeerConnection")] public IEnumerator SetRemoteDescriptionFailed() { var config = GetDefaultConfiguration(); @@ -815,7 +841,7 @@ public IEnumerator SetRemoteDescriptionFailed() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator IceConnectionStateChange() { RTCConfiguration config = default; @@ -867,7 +893,7 @@ public IEnumerator IceConnectionStateChange() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator AddIceCandidate() { RTCConfiguration config = default; @@ -951,7 +977,7 @@ public IEnumerator AddIceCandidate() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator MediaStreamTrackThrowExceptionAfterPeerDisposed() { RTCConfiguration config = default; @@ -983,7 +1009,7 @@ public IEnumerator MediaStreamTrackThrowExceptionAfterPeerDisposed() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator PeerConnectionStateChange() { RTCConfiguration config = default; @@ -1057,6 +1083,7 @@ public IEnumerator PeerConnectionStateChange() [UnityTest] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] public IEnumerator GetStatsReturnsReport() { var go1 = new GameObject("Test1"); @@ -1102,7 +1129,7 @@ public IEnumerator GetStatsReturnsReport() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator RestartIceInvokeOnNegotiationNeeded() { RTCConfiguration config = default; @@ -1146,7 +1173,7 @@ public IEnumerator RestartIceInvokeOnNegotiationNeeded() [UnityTest] [Timeout(5000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer,RuntimePlatform.WebGLPlayer })] public IEnumerator RemoteOnRemoveTrack() { RTCConfiguration config = default; diff --git a/Tests/Runtime/StatsReportTest.cs b/Tests/Runtime/StatsReportTest.cs index ba3ef62da2..6bcae7dd52 100644 --- a/Tests/Runtime/StatsReportTest.cs +++ b/Tests/Runtime/StatsReportTest.cs @@ -62,7 +62,7 @@ public static void Test(RTCStats stats) Assert.NotNull(dataChannelStats); Assert.AreEqual(8, dataChannelStats.Dict.Count); Assert.IsNotEmpty(dataChannelStats.label); - Assert.IsNotEmpty(dataChannelStats.state); + Ignore.Pass(dataChannelStats.state); Ignore.Pass(dataChannelStats.protocol); Ignore.Pass(dataChannelStats.messagesSent); Ignore.Pass(dataChannelStats.messagesReceived); diff --git a/Tests/Runtime/TransceiverTest.cs b/Tests/Runtime/TransceiverTest.cs index 802967432d..173855693f 100644 --- a/Tests/Runtime/TransceiverTest.cs +++ b/Tests/Runtime/TransceiverTest.cs @@ -210,6 +210,7 @@ public IEnumerator ReceiverGetContributingSource() [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] [Timeout(5000)] [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")] public IEnumerator TransceiverStop() diff --git a/Tests/Runtime/Unity.WebRTC.RuntimeTests.asmdef b/Tests/Runtime/Unity.WebRTC.RuntimeTests.asmdef index 1baf8d06ec..a84e849fdd 100644 --- a/Tests/Runtime/Unity.WebRTC.RuntimeTests.asmdef +++ b/Tests/Runtime/Unity.WebRTC.RuntimeTests.asmdef @@ -12,13 +12,15 @@ "iOS", "LinuxStandalone64", "macOSStandalone", + "WebGL", "WindowsStandalone64" ], "excludePlatforms": [], "allowUnsafeCode": true, "overrideReferences": true, "precompiledReferences": [ - "nunit.framework.dll" + "nunit.framework.dll", + "Newtonsoft.Json.dll" ], "autoReferenced": false, "defineConstraints": [ diff --git a/Tests/Runtime/VideoReceiveTest.cs b/Tests/Runtime/VideoReceiveTest.cs index 7b3df6e841..012821e92e 100644 --- a/Tests/Runtime/VideoReceiveTest.cs +++ b/Tests/Runtime/VideoReceiveTest.cs @@ -46,6 +46,31 @@ public void SetUp() SetUpCodecCapability(); } + [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] + [Timeout(5000)] + public IEnumerator InitializeReceiver() + { + var peer = new RTCPeerConnection(); + var transceiver = peer.AddTransceiver(TrackKind.Video); + Assert.That(transceiver, Is.Not.Null); + RTCRtpReceiver receiver = transceiver.Receiver; + Assert.That(receiver, Is.Not.Null); + MediaStreamTrack track = receiver.Track; + Assert.That(track, Is.Not.Null); + Assert.AreEqual(TrackKind.Video, track.Kind); + Assert.That(track.Kind, Is.EqualTo(TrackKind.Video)); + var videoTrack = track as VideoStreamTrack; + Assert.That(videoTrack, Is.Not.Null); + var rt = videoTrack.Texture; + videoTrack.Dispose(); + // wait for disposing video track. + yield return 0; + + peer.Dispose(); + Object.DestroyImmediate(rt); + } + internal class TestValue { public int width; @@ -72,6 +97,7 @@ internal class TestValue // refer to https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-tests-parameterized.html [UnityTest, LongRunning] [Timeout(15000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })] [ConditionalIgnoreMultiple(ConditionalIgnore.UnsupportedPlatformVideoDecoder, "VideoStreamTrack.UpdateTexture is not supported on Direct3D12 for decoder")] [ConditionalIgnoreMultiple(ConditionalIgnore.UnsupportedPlatformOpenGL, diff --git a/Tests/Runtime/WebRTCTest.cs b/Tests/Runtime/WebRTCTest.cs index 74d2b11a1d..c0c6ee708f 100644 --- a/Tests/Runtime/WebRTCTest.cs +++ b/Tests/Runtime/WebRTCTest.cs @@ -50,6 +50,8 @@ public void WebCamTextureFormat() } #endif + // TODO replace with TestCaseSource +#if !UNITY_WEBGL [Test] [TestCase(256, 256)] [TestCase(640, 360)] @@ -94,7 +96,7 @@ public void ErrorOnValidateTextureSize(int width, int height) var error = WebRTC.ValidateTextureSize(width, height, platform); Assert.That(error.errorType, Is.EqualTo(RTCErrorType.InvalidRange)); } - +#endif [Test] public void ValidateGraphicsFormat() { @@ -102,6 +104,8 @@ public void ValidateGraphicsFormat() Assert.That(() => WebRTC.ValidateGraphicsFormat(format), Throws.Nothing); } + // TODO replace with TestCaseSource +#if !UNITY_WEBGL [Test] [TestCase((GraphicsFormat)87)] //LegacyARGB32_sRGB [TestCase((GraphicsFormat)88)] //LegacyARGB32_UNorm @@ -109,6 +113,7 @@ public void ValidateLegacyGraphicsFormat(GraphicsFormat format) { Assert.That(() => WebRTC.ValidateGraphicsFormat(format), Throws.Nothing); } +#endif [Test] public void EnableLogging() diff --git a/WebRTC~/ProjectSettings/EditorBuildSettings.asset b/WebRTC~/ProjectSettings/EditorBuildSettings.asset index c587a86c1a..7a378e512b 100644 --- a/WebRTC~/ProjectSettings/EditorBuildSettings.asset +++ b/WebRTC~/ProjectSettings/EditorBuildSettings.asset @@ -65,4 +65,7 @@ EditorBuildSettings: - enabled: 1 path: Assets/Samples/Encryption/Encryption.unity guid: f2cdbab4e4b588a4791756972d9ef355 + - enabled: 1 + path: Assets/Samples/WebGL/WebGL.unity + guid: 8566d76aec4c20e4e819f649f8c65945 m_configObjects: {} diff --git a/package.json b/package.json index 576d13e521..2172afc739 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "dependencies": { "com.unity.modules.jsonserialize": "1.0.0", "com.unity.editorcoroutines": "1.0.0", - "com.unity.modules.audio": "1.0.0" + "com.unity.modules.audio": "1.0.0", + "com.unity.nuget.newtonsoft-json": "2.0.0" }, "samples": [{ "displayName": "Example", From d440b1cdce6b1178cf7bbddfb80b9e6e39bc1517 Mon Sep 17 00:00:00 2001 From: Thaina Date: Sun, 12 Jan 2025 15:24:38 +0700 Subject: [PATCH 67/67] update 3.0 for webgl branch --- Runtime/Plugins/WebGL/Context.jslib | 102 +++++++++++++----- Runtime/WebGL.meta | 8 ++ Samples~/Unity.WebRTC.Samples.asmdef | 8 +- WebRTC~/Packages/manifest.json | 2 +- WebRTC~/Packages/packages-lock.json | 29 ++++- WebRTC~/ProjectSettings/ProjectSettings.asset | 11 +- .../UnityConnectSettings.asset | 2 + 7 files changed, 121 insertions(+), 41 deletions(-) create mode 100644 Runtime/WebGL.meta diff --git a/Runtime/Plugins/WebGL/Context.jslib b/Runtime/Plugins/WebGL/Context.jslib index 7621733e62..d704505bf9 100644 --- a/Runtime/Plugins/WebGL/Context.jslib +++ b/Runtime/Plugins/WebGL/Context.jslib @@ -7,6 +7,7 @@ var UnityWebRTCContext = { ContextCreate: function (uid, encodeType) { var context = { id: uid, + refPtr: new Set(), encodeType: UWEncoderType[encodeType] }; uwcom_addManageObj(context); @@ -15,10 +16,7 @@ var UnityWebRTCContext = { ContextDestroy: function (uid) { var contextPtrs = Object.keys(UWManaged).filter(function (contextPtr) { - if ('id' in UWManaged[contextPtr]) { - return UWManaged[contextPtr].id === uid; - } else - return false; + return 'id' in UWManaged[contextPtr] && UWManaged[contextPtr].id === uid; }); if (contextPtrs.length > 1) { console.error('ContextDestroy: multiple Contexts with the same id'); @@ -31,19 +29,27 @@ var UnityWebRTCContext = { }, SetCurrentContext: function (contextPtr) { - throw new Error("Not implemented"); + UWManaged["__currentContext__"] = UWManaged[contextPtr]; }, - + // TODO - ContextAddRefPtr: function (ptr){ - throw new Error("Not implemented"); + ContextAddRefPtr: function (contextPtr,ptr){ + if (!uwcom_existsCheck(contextPtr, "ContextDeleteRefPtr", "context")) return; + /** @type {{ refPtr: Set }} */ + var context = UWManaged[contextPtr]; + if(context && context.refPtr) + context.refPtr.add(ptr); }, - + // TODO - ContextDeleteRefPtr: function(ptr){ - throw new Error("Not implemented"); + ContextDeleteRefPtr: function(contextPtr,ptr){ + if (!uwcom_existsCheck(contextPtr, "ContextDeleteRefPtr", "context")) return; + /** @type {{ refPtr: Set }} */ + var context = UWManaged[contextPtr]; + if(context.refPtr) + context.refPtr.delete(ptr); }, - + // TODO ContextCreateAudioTrackSource: function(contextPtr){ if (!uwcom_existsCheck(contextPtr, "ContextCreateAudioTrackSource", "context")) return; @@ -51,7 +57,7 @@ var UnityWebRTCContext = { uwcom_addManageObj(audioTrackSource); return audioTrackSource.managePtr; }, - + // TODO ContextCreateVideoTrackSource: function(contextPtr){ if (!uwcom_existsCheck(contextPtr, "ContextCreateVideoTrackSource", "context")) return; @@ -59,7 +65,7 @@ var UnityWebRTCContext = { uwcom_addManageObj(videoTrackSource); return videoTrackSource.managePtr; }, - + ContextGetEncoderType: function (contextPtr) { if (!uwcom_existsCheck(contextPtr, 'ContextGetEncoderType', 'context')) return; var context = UWManaged[contextPtr]; @@ -97,7 +103,7 @@ var UnityWebRTCContext = { if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSetRemoteDescription', 'peer')) return 11; // OperationErrorWithData return _PeerConnectionSetDescription(peerPtr, typeIdx, sdpPtr, 'Remote'); }, - + PeerConnectionSetLocalDescriptionWithoutDescription: function (contextPtr, peerPtr) { if (!uwcom_existsCheck(contextPtr, 'PeerConnectionSetLocalDescriptionWithoutDescription', 'context')) return 11; // OperationErrorWithData if (!uwcom_existsCheck(peerPtr, 'PeerConnectionSetLocalDescriptionWithoutDescription', 'peer')) return 11; // OperationErrorWithData @@ -142,7 +148,7 @@ var UnityWebRTCContext = { ContextRegisterMediaStreamObserver: function (contextPtr, streamPtr) { if (!uwcom_existsCheck(contextPtr, 'MediaStreamRegisterOnAddTrack', 'context')) return; if (!uwcom_existsCheck(streamPtr, 'MediaStreamRegisterOnAddTrack', 'stream')) return; - + var stream = UWManaged[streamPtr]; stream.onaddtrack = (function(evt) { uwcom_addManageObj(evt.track); @@ -153,24 +159,50 @@ var UnityWebRTCContext = { Module.dynCall_vii(uwevt_MSOnRemoveTrack, stream.managePtr, evt.track.managePtr); }); }, - - ContextUnRegisterMediaStreamObserver: function (contextPtr, streamPtr) { + ContextUnRegisterMediaStreamObserver: function (contextPtr, streamPtr) { + if (!uwcom_existsCheck(contextPtr, 'MediaStreamRegisterOnAddTrack', 'context')) return; + if (!uwcom_existsCheck(streamPtr, 'MediaStreamRegisterOnAddTrack', 'stream')) return; + var context = UWManaged[contextPtr]; + var stream = UWManaged[streamPtr]; + throw new Error("Not implemented"); }, - + MediaStreamRegisterOnAddTrack: function (contextPtr, streamPtr, MediaStreamOnAddTrack) { if (!uwcom_existsCheck(contextPtr, 'MediaStreamRegisterOnAddTrack', 'context')) return; if (!uwcom_existsCheck(streamPtr, 'MediaStreamRegisterOnAddTrack', 'stream')) return; + var context = UWManaged[contextPtr]; + var stream = UWManaged[streamPtr]; uwevt_MSOnAddTrack = MediaStreamOnAddTrack; }, MediaStreamRegisterOnRemoveTrack: function (contextPtr, streamPtr, MediaStreamOnRemoveTrack) { if (!uwcom_existsCheck(contextPtr, 'MediaStreamRegisterOnRemoveTrack', 'context')) return; if (!uwcom_existsCheck(streamPtr, 'MediaStreamRegisterOnRemoveTrack', 'stream')) return; + var context = UWManaged[contextPtr]; + var stream = UWManaged[streamPtr]; uwevt_MSOnRemoveTrack = MediaStreamOnRemoveTrack; }, + StatsCollectorRegisterCallback: function(onCollectStatsCallback) { + throw new Error("Not implemented"); + }, + CreateSessionDescriptionObserverRegisterCallback: function(nativeCreateSessionDescCallback) { + throw new Error("Not implemented"); + }, + SetLocalDescriptionObserverRegisterCallback: function(setLocalDescriptionCallback) { + throw new Error("Not implemented"); + }, + SetRemoteDescriptionObserverRegisterCallback: function(setRemoteDescriptionCallback) { + throw new Error("Not implemented"); + }, + SetTransformedFrameRegisterCallback: function(transformedFrameCallback) { + throw new Error("Not implemented"); + }, + GetBatchUpdateEventFunc: function (contextPtr) { + if (!uwcom_existsCheck(contextPtr, 'GetBatchUpdateEventFunc', 'context')) return; + var context = UWManaged[contextPtr]; throw new Error("Not implemented"); return null; }, @@ -180,19 +212,31 @@ var UnityWebRTCContext = { return -1; }, - AudioTrackAddSink: function(track, sink) { + AudioTrackAddSink: function(trackPtr, sinkPtr) { + if (!uwcom_existsCheck(trackPtr, 'AudioTrackAddSink', 'track')) return; + if (!uwcom_existsCheck(sinkPtr, 'AudioTrackAddSink', 'sink')) return; + var track = UWManaged[trackPtr]; + var sink = UWManaged[sinkPtr]; throw new Error("Not implemented"); }, - AudioTrackRemoveSink: function(track, sink) { + AudioTrackRemoveSink: function(trackPtr, sinkPtr) { + if (!uwcom_existsCheck(trackPtr, 'AudioTrackRemoveSink', 'track')) return; + if (!uwcom_existsCheck(sinkPtr, 'AudioTrackRemoveSink', 'sink')) return; + var track = UWManaged[trackPtr]; + var sink = UWManaged[sinkPtr]; throw new Error("Not implemented"); }, - AudioTrackSinkProcessAudio: function(sink, data, length, channels, sampleRate) { + AudioTrackSinkProcessAudio: function(sinkPtr, data, length, channels, sampleRate) { + if (!uwcom_existsCheck(sinkPtr, 'AudioTrackSinkProcessAudio', 'sink')) return; + var sink = UWManaged[sinkPtr]; throw new Error("Not implemented"); }, GetUpdateTextureFunc: function (contextPtr) { + if (!uwcom_existsCheck(contextPtr, 'GetUpdateTextureFunc', 'context')) return; + var context = UWManaged[contextPtr]; throw new Error("Not implemented"); }, @@ -233,13 +277,21 @@ var UnityWebRTCContext = { }, ContextRegisterAudioReceiveCallback: function (contextPtr, trackPtr, AudioTrackOnReceive){ - + if (!uwcom_existsCheck(contextPtr, 'ContextRegisterAudioReceiveCallback', 'context')) return; + if (!uwcom_existsCheck(trackPtr, 'ContextRegisterAudioReceiveCallback', 'track')) return; + var context = UWManaged[contextPtr]; + var track = UWManaged[trackPtr]; + throw new Error("Not implemented"); }, ContextUnregisterAudioReceiveCallback: function (contextPtr, trackPtr){ - + if (!uwcom_existsCheck(contextPtr, 'ContextUnregisterAudioReceiveCallback', 'context')) return; + if (!uwcom_existsCheck(trackPtr, 'ContextUnregisterAudioReceiveCallback', 'track')) return; + var context = UWManaged[contextPtr]; + var track = UWManaged[trackPtr]; + throw new Error("Not implemented"); }, - + // CreateVideoRenderer: function(contextPtr) { // }, diff --git a/Runtime/WebGL.meta b/Runtime/WebGL.meta new file mode 100644 index 0000000000..c075fd3e0a --- /dev/null +++ b/Runtime/WebGL.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1f93366b4172b0b43801d09bd020b253 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Unity.WebRTC.Samples.asmdef b/Samples~/Unity.WebRTC.Samples.asmdef index 72ba932a03..04cc06fd73 100644 --- a/Samples~/Unity.WebRTC.Samples.asmdef +++ b/Samples~/Unity.WebRTC.Samples.asmdef @@ -1,9 +1,10 @@ { "name": "Unity.WebRTC.Samples", + "rootNamespace": "", "references": [ - "Unity.WebRTC" + "Unity.WebRTC", + "UnityWebGLMicrophone" ], - "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, @@ -11,5 +12,6 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [] + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/WebRTC~/Packages/manifest.json b/WebRTC~/Packages/manifest.json index 108ed58bce..2846804b52 100644 --- a/WebRTC~/Packages/manifest.json +++ b/WebRTC~/Packages/manifest.json @@ -1,10 +1,10 @@ { "dependencies": { + "com.bnco.unity-webgl-microphone": "https://github.com/bnco-dev/unity-webgl-microphone.git", "com.unity.editorcoroutines": "1.0.0", "com.unity.ext.nunit": "1.0.6", "com.unity.ide.rider": "3.0.26", "com.unity.ide.visualstudio": "2.0.22", - "com.unity.ide.vscode": "1.2.5", "com.unity.test-framework": "1.1.33", "com.unity.testtools.codecoverage": "1.2.4", "com.unity.ugui": "1.0.0", diff --git a/WebRTC~/Packages/packages-lock.json b/WebRTC~/Packages/packages-lock.json index 0ab2e3fd6a..5db6266e29 100644 --- a/WebRTC~/Packages/packages-lock.json +++ b/WebRTC~/Packages/packages-lock.json @@ -1,5 +1,12 @@ { "dependencies": { + "com.bnco.unity-webgl-microphone": { + "version": "https://github.com/bnco-dev/unity-webgl-microphone.git", + "depth": 0, + "source": "git", + "dependencies": {}, + "hash": "bad36244303305fcbdfcb764c30d5bc9be480676" + }, "com.unity.editorcoroutines": { "version": "1.0.0", "depth": 0, @@ -32,15 +39,15 @@ }, "url": "https://packages.unity.com" }, - "com.unity.ide.vscode": { - "version": "1.2.5", - "depth": 0, + "com.unity.nuget.newtonsoft-json": { + "version": "2.0.0", + "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.settings-manager": { - "version": "2.0.1", + "version": "1.0.1", "depth": 1, "source": "registry", "dependencies": {}, @@ -83,7 +90,8 @@ "dependencies": { "com.unity.modules.jsonserialize": "1.0.0", "com.unity.editorcoroutines": "1.0.0", - "com.unity.modules.audio": "1.0.0" + "com.unity.modules.audio": "1.0.0", + "com.unity.nuget.newtonsoft-json": "2.0.0" } }, "com.unity.modules.androidjni": { @@ -134,6 +142,17 @@ "version": "1.0.0", "depth": 0, "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.uielementsnative": "1.0.0" + } + }, + "com.unity.modules.uielementsnative": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", diff --git a/WebRTC~/ProjectSettings/ProjectSettings.asset b/WebRTC~/ProjectSettings/ProjectSettings.asset index 1448bf2436..1e60d85a27 100644 --- a/WebRTC~/ProjectSettings/ProjectSettings.asset +++ b/WebRTC~/ProjectSettings/ProjectSettings.asset @@ -145,7 +145,6 @@ PlayerSettings: enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 - enableOpenGLProfilerGPURecorders: 1 useHDRDisplay: 0 D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 @@ -218,7 +217,6 @@ PlayerSettings: iOSLaunchScreeniPadCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] - macOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 @@ -430,7 +428,7 @@ PlayerSettings: m_Automatic: 1 - m_BuildTarget: WebGLSupport m_APIs: 0b000000 - m_Automatic: 1 + m_Automatic: 0 m_BuildTargetVRSettings: - m_BuildTarget: Standalone m_Enabled: 0 @@ -448,7 +446,6 @@ PlayerSettings: m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] m_BuildTargetNormalMapEncoding: [] - m_BuildTargetDefaultTextureCompressionFormat: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 @@ -467,7 +464,6 @@ PlayerSettings: switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 switchUseGOLDLinker: 0 - switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: switchTitleNames_0: @@ -597,6 +593,7 @@ PlayerSettings: switchNetworkInterfaceManagerInitializeEnabled: 1 switchPlayerConnectionEnabled: 1 switchUseNewStyleFilepaths: 0 + switchUseLegacyFmodPriorities: 1 switchUseMicroSleepForYield: 1 switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 @@ -710,6 +707,7 @@ PlayerSettings: suppressCommonWarnings: 1 allowUnsafeCode: 0 useDeterministicCompilation: 1 + useReferenceAssemblies: 1 enableRoslynAnalyzers: 1 additionalIl2CppArgs: scriptingRuntimeVersion: 1 @@ -790,6 +788,7 @@ PlayerSettings: m_VersionName: apiCompatibilityLevel: 6 activeInputHandler: 0 + windowsGamepadBackendHint: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -797,6 +796,4 @@ PlayerSettings: organizationId: cloudEnabled: 0 legacyClampBlendShapeWeights: 0 - playerDataPath: - forceSRGBBlit: 1 virtualTexturingSupportEnabled: 0 diff --git a/WebRTC~/ProjectSettings/UnityConnectSettings.asset b/WebRTC~/ProjectSettings/UnityConnectSettings.asset index fa0b146579..a88bee0f15 100644 --- a/WebRTC~/ProjectSettings/UnityConnectSettings.asset +++ b/WebRTC~/ProjectSettings/UnityConnectSettings.asset @@ -9,6 +9,7 @@ UnityConnectSettings: m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com m_TestInitMode: 0 CrashReportingSettings: m_EventUrl: https://perf-events.cloud.unity3d.com @@ -22,6 +23,7 @@ UnityConnectSettings: m_Enabled: 0 m_TestMode: 0 m_InitializeOnStartup: 1 + m_PackageRequiringCoreStatsPresent: 0 UnityAdsSettings: m_Enabled: 0 m_InitializeOnStartup: 1