// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
// Copyright (c) (2018-2022) Magic Leap, Inc. All Rights Reserved.
// Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2
// Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein.
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%
namespace UnityEngine.XR.MagicLeap
{
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine.XR.MagicLeap.Native;
///
/// MLWebRTC class contains the API to interface with the
/// WebRTC C API.
///
public partial class MLWebRTC
{
///
/// Class that represents a connection used by the MLWebRTC API.
///
public partial class PeerConnection
{
///
/// Native bindings for the MLWebRTC.PeerConnection class.
///
internal class NativeBindings : MagicLeapNativeBindings
{
///
/// A delegate that describes the requirements of the OnError callback.
///
/// The error message.
/// Pointer to a context object.
public delegate void OnErrorDelegate([MarshalAs(UnmanagedType.LPStr)] string message, IntPtr context);
///
/// A delegate that describes the requirements of the OnConnectedDelegate callback.
///
/// Pointer to a context object.
public delegate void OnConnectedDelegate(IntPtr context);
///
/// A delegate that describes the requirements of the OnDisconnectedDelegate callback.
///
/// Pointer to a context object.
public delegate void OnDisconnectedDelegate(IntPtr context);
///
/// A delegate that describes the requirements of the OnTrackAddedDelegate callback.
///
/// The native source object that was added.
/// The number of streams this track belongs to.
/// The names of the streams this track belongs to.
/// Pointer to a context object.
public delegate void OnTrackAddedDelegate(in MLWebRTC.Source.NativeBindings.MLWebRTCSource sourceNative, uint numStreamIds, IntPtr streamIdsPtr, IntPtr context);
///
/// A delegate that describes the requirements of the OnTrackRemovedDelegate callback.
///
/// The handle to the removed source.
/// Pointer to a context object.
public delegate void OnTrackRemovedDelegate(ulong remoteSourceHandle, IntPtr context);
///
/// A delegate that describes the requirements of the OnDataChannelReceivedDelegate callback.
///
/// The handle to the newly received data channel.
/// Pointer to a context object.
public delegate void OnDataChannelReceivedDelegate(ulong remoteDataChanelHandle, IntPtr context);
///
/// A delegate that describes the requirements of the OnSendOfferDelegate callback.
///
/// The json formatted offer string.
/// Pointer to a context object.
public delegate void OnLocalOfferCreatedDelegate([MarshalAs(UnmanagedType.LPStr)] string offer, IntPtr context);
///
/// A delegate that describes the requirements of the OnSendAnswer callback.
///
/// The json formatted answer string.
/// Pointer to a context object.
public delegate void OnLocalAnswerCreatedDelegate([MarshalAs(UnmanagedType.LPStr)] string answer, IntPtr context);
///
/// A delegate that describes the requirements of the OnSendIceCandidate callback.
///
/// The ice candidate object that was sent.
/// Pointer to a context object.
public delegate void OnLocalIceCandidateFoundDelegate(in MLWebRTCConnectionIceCandidate iceCandidate, IntPtr context);
///
/// A delegate that describes the requirements of the OnIceGatheringCompleted callback.
///
/// Pointer to a context object.
public delegate void OnIceGatheringCompletedDelegate(IntPtr context);
private static MLResult.Code CreateRemoteConnection(MLWebRTC.IceServer[] iceServers, IntPtr proxyConfig, out PeerConnection connection)
{
connection = new PeerConnection();
// Converts into native ice server structs
MLWebRTCConnectionIceServer[] nativeIceServers = new MLWebRTCConnectionIceServer[iceServers.Length];
for (int i = 0; i < iceServers.Length; ++i)
{
nativeIceServers[i].Data = iceServers[i];
}
IntPtr arrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf() * nativeIceServers.Length);
IntPtr walkPtr = arrayPtr;
for (int i = 0; i < nativeIceServers.Length; ++i)
{
Marshal.StructureToPtr(nativeIceServers[i], walkPtr, false);
walkPtr = new IntPtr(walkPtr.ToInt64() + Marshal.SizeOf());
}
NativeBindings.MLWebRTCConnectionConfig config = new NativeBindings.MLWebRTCConnectionConfig();
config.IceServers = arrayPtr;
config.NumIceServers = (uint)nativeIceServers.Length;
config.ProxyConfig = proxyConfig;
NativeBindings.MLWebRTCConnectionEventCallbacks callbacks = new NativeBindings.MLWebRTCConnectionEventCallbacks();
callbacks.OnError = NativeBindings.OnError;
callbacks.OnConnected = NativeBindings.OnConnected;
callbacks.OnDisconnected = NativeBindings.OnDisconnected;
callbacks.OnLocalOfferCreated = NativeBindings.OnLocalOfferCreated;
callbacks.OnLocalAnswerCreated = NativeBindings.OnLocalAnswerCreated;
callbacks.OnLocalIceCandidateFound = NativeBindings.OnLocalIceCandidateFound;
callbacks.OnIceGatheringCompleted = NativeBindings.OnIceGatheringCompleted;
callbacks.OnTrackRemoved = NativeBindings.OnTrackRemoved;
callbacks.OnTrackAdded = NativeBindings.OnTrackAdded;
callbacks.OnDataChannelReceived = NativeBindings.OnDataChannelReceived;
connection.gcHandle = GCHandle.Alloc(connection);
IntPtr gcHandlePtr = GCHandle.ToIntPtr(connection.gcHandle);
callbacks.Context = gcHandlePtr;
ulong connectionHandle = MagicLeapNativeBindings.InvalidHandle;
MLResult.Code resultCode = NativeBindings.MLWebRTCConnectionCreate(in config, in callbacks, out connectionHandle);
Marshal.FreeHGlobal(arrayPtr);
if (!MLResult.IsOK(resultCode))
{
connection.gcHandle.Free();
return resultCode;
}
connection.Handle = connectionHandle;
return resultCode;
}
///
/// Creates a remote connection and sets up the native callbacks.
///
/// The ice servers to create the connection with.
/// The created connection object.
///
/// MLResult.Result will be MLResult.Code.Ok if the connection was successfully created.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
public static MLResult.Code CreateRemoteConnection(MLWebRTC.IceServer[] iceServers, out PeerConnection connection)
{
return CreateRemoteConnection(iceServers, IntPtr.Zero, out connection);
}
///
/// Creates a remote connection with a forward proxy configuration and sets up the native callbacks.
///
/// The ice servers to create the connection with.
/// The forward proxy configuration.
/// The created connection object.
///
/// MLResult.Result will be MLResult.Code.Ok if the connection was successfully created.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
public static MLResult.Code CreateRemoteConnection(MLWebRTC.IceServer[] iceServers, MLWebRTC.ProxyConfig proxyConfig, out PeerConnection connection)
{
NativeBindings.MLWebRTCConnectionProxyConfig nativeProxyConfig = new MLWebRTCConnectionProxyConfig
{
Type = proxyConfig.Type,
HostAddress = proxyConfig.HostAddress,
HostPort = proxyConfig.HostPort,
Username = proxyConfig.Username,
Password = proxyConfig.Password,
AutoDetect = proxyConfig.AutoDetect,
AutoConfigUrl = proxyConfig.AutoConfigUrl,
BypassList = proxyConfig.BypassList
};
IntPtr proxyConfigPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
Marshal.StructureToPtr(nativeProxyConfig, proxyConfigPtr, false);
MLResult.Code result = CreateRemoteConnection(iceServers, proxyConfigPtr, out connection);
Marshal.FreeHGlobal(proxyConfigPtr);
return result;
}
///
/// Creates a WebRTC connection.
///
/// The configuration that holds the ice servers.
/// The object that holds all the callbacks that will be invoked natively.
/// The handle of the connection to return to the caller.
///
/// MLResult.Result will be MLResult.Code.Ok if the connection was successfully created.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionCreate(in MLWebRTCConnectionConfig config, in MLWebRTCConnectionEventCallbacks callbacks, out ulong connectionHandle);
///
/// Processes events of a connection, responsible for callbacks being invoked natively.
///
/// The handle to the connection to process events from.
///
/// MLResult.Result will be MLResult.Code.Ok if the connection had it's events successfully processed.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionProcessEvents(ulong connectionHandle);
///
/// Creates an offer with a connection.
///
/// The handle to the connection creating the offer.
///
/// MLResult.Result will be MLResult.Code.Ok if the offer was successfully created.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionCreateOffer(ulong connectionHandle);
///
/// Sets an offer from a remote connection.
///
/// The handle to the connection responding to the offer.
/// The offer string.
///
/// MLResult.Result will be MLResult.Code.Ok if the offer was successfully set.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionSetRemoteOffer(ulong connectionHandle, [MarshalAs(UnmanagedType.LPStr)] string offer);
///
/// Sets the answer to an offer from a remote connection.
///
/// The handle to the connection answering to the offer.
/// The json formatted answer string to set.
///
/// MLResult.Result will be MLResult.Code.Ok if the answer was successfully set.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionSetRemoteAnswer(ulong connectionHandle, [MarshalAs(UnmanagedType.LPStr)] string answer);
///
/// Adds a remote ice candidate from a connection.
///
/// The handle to the connection to send the ice candidate to.
/// The ice candidate to send.
///
/// MLResult.Result will be MLResult.Code.Ok if the ice candidate was successfully sent.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionAddRemoteIceCandidate(ulong connectionHandle, in MLWebRTCConnectionIceCandidate candidate);
///
/// Gets if a connection is currently connected.
///
/// The handle to the connection in question.
/// Used to return to the user to determine if the connection is connected or not.
///
/// MLResult.Result will be MLResult.Code.Ok if the connection status was successfully queried.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionIsConnected(ulong connectionHandle, [MarshalAs(UnmanagedType.I1)] out bool isConnected);
///
/// Gets if a connection has failed
///
/// The handle to the connection in question.
/// Used to return to the user to determine if the connection has failed or not.
///
/// MLResult.Result will be MLResult.Code.Ok if the connection status was successfully queried.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionHasFailed(ulong connectionHandle, [MarshalAs(UnmanagedType.I1)] out bool hasFailed);
///
/// Add a local source as a media track to the connection.
///
/// The handle to the connection to set the local source to.
/// The handle to the local source to set onto the connection.
/// Information about the track to be added
///
/// MLResult.Result will be MLResult.Code.Ok if the source was successfully set on the connection.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionAddLocalSourceTrackEx(ulong connectionHandle, ulong localSourceHandle, [In] ref MLWebRTCTrackInfo trackInfo);
///
/// Sets the local source of a connection.
///
/// The handle to the connection to set the local source to.
/// The handle to the local source to set onto the connection.
///
/// MLResult.Result will be MLResult.Code.Ok if the source was successfully set on the connection.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionRemoveLocalSourceTrack(ulong connectionHandle, ulong localSourceHandle);
///
/// Destroys a WebRTC connection.
///
/// The handle to the connection to destroy.
///
/// MLResult.Result will be MLResult.Code.Ok if the connection was successfully destroyed.
/// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing.
/// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error.
///
[DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern MLResult.Code MLWebRTCConnectionDestroy(ulong connectionHandle);
///
/// Callback that is invoked when an error is made.
///
/// The error message.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnErrorDelegate))]
private static void OnError([MarshalAs(UnmanagedType.LPStr)] string message, IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
connection?.OnError?.Invoke(connection, message);
});
}
///
/// Callback that is invoked when a connection is established.
///
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnConnectedDelegate))]
private static void OnConnected(IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
connection?.OnConnected?.Invoke(connection);
});
}
///
/// Callback that is for OnDisconnected.
///
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnDisconnectedDelegate))]
private static void OnDisconnected(IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
connection?.OnDisconnected?.Invoke(connection);
});
}
///
/// Callback that is for OnTrackAdded.
///
/// The native source object that was added.
/// The number of streams this track belongs to.
/// The names of the streams this track belongs to.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnTrackAddedDelegate))]
private static void OnTrackAdded(in MLWebRTC.Source.NativeBindings.MLWebRTCSource sourceNative, uint numStreamIds, IntPtr streamIdsPtr, IntPtr context)
{
if (sourceNative.Handle == MagicLeapNativeBindings.InvalidHandle)
{
Debug.LogError("Received invalid source handle in NativeBindings.OnTrackAdded() callback");
return;
}
MLWebRTC.MediaStream.Track addedTrack = sourceNative.Data;
const string defaultStreamId = "unknown_remote";
int ptrSize = Marshal.SizeOf(typeof(IntPtr));
// TODO : marshal directly as a string array instead of IntPtr.
string[] streamIds = new string[numStreamIds];
for (int i = 0; i < numStreamIds; ++i)
{
IntPtr ptr = Marshal.ReadIntPtr(streamIdsPtr, i * ptrSize);
streamIds[i] = (ptr != IntPtr.Zero) ? Marshal.PtrToStringAnsi(ptr) : defaultStreamId;
}
if (streamIds.Length == 0)
{
streamIds = new string[1];
streamIds[0] = defaultStreamId;
}
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
addedTrack.ParentConnection = connection;
List mediaStreams = new List();
foreach (string streamId in streamIds)
{
MLWebRTC.MediaStream mediaStream = null;
if (!connection.remoteMediaStreams.ContainsKey(streamId))
{
mediaStream = MLWebRTC.MediaStream.Create(connection, streamId);
connection.remoteMediaStreams.Add(mediaStream.Id, mediaStream);
}
else
{
mediaStream = connection.remoteMediaStreams[streamId];
}
mediaStream.Tracks.Add(addedTrack);
// Mark this track as "selected" if this is the first track of its type in this stream.
if (addedTrack.TrackType == MediaStream.Track.Type.Audio && mediaStream.ActiveAudioTrack == null)
{
mediaStream.SelectTrack(addedTrack);
}
else if (addedTrack.TrackType == MediaStream.Track.Type.Video && mediaStream.ActiveVideoTrack == null)
{
mediaStream.SelectTrack(addedTrack);
}
mediaStreams.Add(mediaStream);
}
connection.OnTrackAddedMultipleStreams?.Invoke(mediaStreams, addedTrack);
});
}
///
/// Callback that is for OnTrackRemoved.
///
/// The handle to the removed source.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnTrackRemovedDelegate))]
private static void OnTrackRemoved(ulong remoteSourceHandle, IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
if (!MagicLeapNativeBindings.MLHandleIsValid(remoteSourceHandle) || connection == null)
{
return;
}
List mediaStreams = new List();
MLWebRTC.MediaStream.Track removedTrack = null;
foreach (MLWebRTC.MediaStream remoteMediaStream in connection.remoteMediaStreams.Values)
{
foreach (MLWebRTC.MediaStream.Track track in remoteMediaStream.Tracks)
{
if (track.Handle == remoteSourceHandle)
{
removedTrack = track;
remoteMediaStream.UnSelectTrack(removedTrack);
remoteMediaStream.Tracks.Remove(removedTrack);
mediaStreams.Add(remoteMediaStream);
break;
}
}
}
removedTrack.Cleanup();
connection.OnTrackRemovedMultipleStreams?.Invoke(mediaStreams, removedTrack);
});
}
///
/// Callback that is for OnDataChannelReceived.
///
/// The handle to the newly received data channel.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnDataChannelReceivedDelegate))]
private static void OnDataChannelReceived(ulong dataChannelHandle, IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
if (connection == null)
{
return;
}
if (!MagicLeapNativeBindings.MLHandleIsValid(dataChannelHandle))
{
return;
}
MLWebRTC.DataChannel dataChannel = new MLWebRTC.DataChannel(dataChannelHandle)
{
ParentConnection = connection
};
MLResult.Code resultCode = MLWebRTC.DataChannel.NativeBindings.SetCallbacks(dataChannel);
if (!MLResult.IsOK(resultCode))
{
return;
}
IntPtr labelPtr = IntPtr.Zero;
resultCode = MLWebRTC.DataChannel.NativeBindings.MLWebRTCDataChannelGetLabel(dataChannelHandle, out labelPtr);
if (!MLResult.IsOK(resultCode))
{
return;
}
if (labelPtr != IntPtr.Zero)
{
dataChannel.Label = Marshal.PtrToStringAnsi(labelPtr);
}
resultCode = MLWebRTC.DataChannel.NativeBindings.MLWebRTCDataChannelReleaseLabelMemory(dataChannelHandle, labelPtr);
connection.remoteDataChannels.Add(dataChannel);
connection.OnDataChannelReceived?.Invoke(connection, dataChannel);
});
}
///
/// Callback that is invoked when an offer is sent.
///
/// The sdp string that was sent.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnLocalOfferCreatedDelegate))]
private static void OnLocalOfferCreated([MarshalAs(UnmanagedType.LPStr)] string sendSdp, IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
connection?.OnLocalOfferCreated?.Invoke(connection, sendSdp);
});
}
///
/// Callback that is invoked when an answer is sent.
///
/// The answer string that was sent.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnLocalAnswerCreatedDelegate))]
private static void OnLocalAnswerCreated([MarshalAs(UnmanagedType.LPStr)] string sendAnswer, IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
connection?.OnLocalAnswerCreated?.Invoke(connection, sendAnswer);
});
}
///
/// Callback that is invoked when an ice candidate is sent.
///
/// The native ice candidate object that was sent.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnLocalIceCandidateFoundDelegate))]
private static void OnLocalIceCandidateFound(in NativeBindings.MLWebRTCConnectionIceCandidate nativeIceCandidate, IntPtr context)
{
MLWebRTC.IceCandidate iceCandidate = nativeIceCandidate.Data;
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
connection?.OnLocalIceCandidateFound?.Invoke(connection, iceCandidate);
});
}
///
/// Callback that is invoked when an ice candidate is sent.
///
/// The native ice candidate object that was sent.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(NativeBindings.OnIceGatheringCompletedDelegate))]
private static void OnIceGatheringCompleted(IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
PeerConnection connection = gcHandle.Target as PeerConnection;
connection?.OnIceGatheringCompleted?.Invoke(connection);
});
}
///
/// The native representation of the MLWebRTC connection configuration.
///
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCConnectionConfig
{
///
/// Version of the struct.
///
public uint Version;
///
/// Pointer to the array of ice servers.
///
public IntPtr IceServers;
///
/// Number of ice servers the above pointer can step through.
///
public uint NumIceServers;
///
/// (Optional) Config for a forward proxy
///
public IntPtr ProxyConfig;
}
///
/// The native representation of the MLWebRTC callback events.
///
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCConnectionEventCallbacks
{
///
/// Version of the struct.
///
public uint Version;
///
/// Context pointer.
///
public IntPtr Context;
///
/// OnError event.
///
public OnErrorDelegate OnError;
///
/// OnConnected event.
///
public OnConnectedDelegate OnConnected;
///
/// OnDisconnected event.
///
public OnDisconnectedDelegate OnDisconnected;
///
/// OnTrackAdded event.
///
public OnTrackAddedDelegate OnTrackAdded;
///
/// OnTrackRemoved event.
///
public OnTrackRemovedDelegate OnTrackRemoved;
///
/// OnDataChannelReceived event.
///
public OnDataChannelReceivedDelegate OnDataChannelReceived;
///
/// OnSendOffer event.
///
public OnLocalOfferCreatedDelegate OnLocalOfferCreated;
///
/// OnSendAnswer event.
///
public OnLocalAnswerCreatedDelegate OnLocalAnswerCreated;
///
/// OnSendIceCandidate event.
///
public OnLocalIceCandidateFoundDelegate OnLocalIceCandidateFound;
///
/// OnIceGatheringCompleted event.
///
public OnIceGatheringCompletedDelegate OnIceGatheringCompleted;
}
///
/// The native representation of an ice server.
///
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCConnectionIceServer
{
///
/// Version of the struct.
///
public uint Version;
///
/// Uri of the ice server.
///
[MarshalAs(UnmanagedType.LPStr)]
public string Uri;
///
/// Username of the ice server.
///
[MarshalAs(UnmanagedType.LPStr)]
public string UserName;
///
/// Password to the ice server.
///
[MarshalAs(UnmanagedType.LPStr)]
public string Password;
///
/// Sets the data of this object from another MLWebRTC.IceServer object.
///
public MLWebRTC.IceServer Data
{
set
{
this.Uri = value.Uri;
this.UserName = value.UserName;
this.Password = value.Password;
}
}
}
///
/// The native representation of an ice candidate.
///
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCConnectionIceCandidate
{
///
/// Version of the struct.
///
public uint Version;
///
/// The id of the ice candidate.
///
[MarshalAs(UnmanagedType.LPStr)]
public string Candidate;
///
/// The sdp mid of the ice candidate.
///
[MarshalAs(UnmanagedType.LPStr)]
public string SdpMid;
///
/// The sdp mline index of the ice candidate.
///
public int SdpMlineIndex;
///
/// Gets or sets data from an MLWebRTC.IceCandidate object.
///
public MLWebRTC.IceCandidate Data
{
get
{
return MLWebRTC.IceCandidate.Create(this.Candidate, this.SdpMid, this.SdpMlineIndex);
}
set
{
this.Candidate = value.Candidate;
this.SdpMid = value.SdpMid;
this.SdpMlineIndex = value.SdpMLineIndex;
}
}
}
///
/// Native representation of proxy config
///
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCConnectionProxyConfig
{
///
/// Type of proxy
///
public MLWebRTC.ProxyType Type;
///
/// Proxy server address
///
[MarshalAs(UnmanagedType.LPStr)]
public string HostAddress;
///
/// Proxy server port
///
public int HostPort;
///
/// Proxy server auth username
///
[MarshalAs(UnmanagedType.LPStr)]
public string Username;
///
/// Proxy server auth password
///
[MarshalAs(UnmanagedType.LPStr)]
public string Password;
///
/// Attempt to automatically detect proxy
///
[MarshalAs(UnmanagedType.I1)]
public bool AutoDetect;
///
/// Url to use to download the proxy config
///
[MarshalAs(UnmanagedType.LPStr)]
public string AutoConfigUrl;
///
/// List of urls that should bypass the proxy
///
[MarshalAs(UnmanagedType.LPStr)]
public string BypassList;
}
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCTrackInfo
{
///
/// Struct version
///
private readonly uint version;
///
/// The names of each stream this track belongs to
///
private readonly uint streamCount;
///
/// Names of the streams this track belongs to.
///
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]
private IntPtr[] streams;
///
/// Id of the track.
///
[MarshalAs(UnmanagedType.LPStr)]
private readonly string trackId;
public MLWebRTCTrackInfo(string[] streams, string trackId)
{
this.version = 1;
this.streamCount = (streams != null) ? (uint)streams.Length : 0;
this.trackId = trackId ?? string.Empty;
// [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] doesnt work when char** is in a struct instead of a func param.
this.streams = (streams != null) ? new IntPtr[streams.Length] : null;
for (int i = 0; i < streamCount; ++i)
{
this.streams[i] = Marshal.StringToHGlobalAnsi(streams[i]);
}
}
public void FreeUnmanagedMemory()
{
foreach (IntPtr ptr in streams)
{
Marshal.FreeHGlobal(ptr);
}
streams = null;
}
}
}
}
}
}