// %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.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 data channel used by the MLWebRTC API.
///
public partial class DataChannel
{
///
/// Native bindings for the MLWebRTC.DataChannel class.
///
internal class NativeBindings : MagicLeapNativeBindings
{
///
/// A delegate that describes the requirements of the OnOpened callback.
///
/// Pointer to a context object.
public delegate void OnOpenedDelegate(IntPtr context);
///
/// A delegate that describes the requirements of the OnClosed callback.
///
/// Pointer to a context object.
public delegate void OnClosedDelegate(IntPtr context);
///
/// A delegate that describes the requirements of the OnMessage callback.
///
/// The native message object received.
/// Pointer to a context object.
public delegate void OnMessageDelegate(in MLWebRTCDataChannelMessage message, IntPtr context);
///
/// Creates a data channel.
///
/// The connection to associate the data channel with.
/// The label to give the data channel.
/// The handle of the data channel after it is created.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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 MLWebRTCDataChannelCreate(ulong connectionHandle, [MarshalAs(UnmanagedType.LPStr)] string label, out ulong dataChannelHandle);
///
/// Gets the label of a data channel, call MLWebRTCDataChannelReleaseLabelMemory after.
///
/// The handle of the data channel.
/// Pointer to the unmanaged label string.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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 MLWebRTCDataChannelGetLabel(ulong dataChannelHandle, out IntPtr label);
///
/// Releases the memory created when calling MLWebRTCDataChannelGetLabel.
///
/// The handle of the data channel.
/// Pointer to the unmanaged label string.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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 MLWebRTCDataChannelReleaseLabelMemory(ulong dataChannelHandle, IntPtr label);
///
/// Gets if a data channel is open.
///
/// The handle of the data channel.
/// True if the data channel is open.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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 MLWebRTCDataChannelIsOpen(ulong dataChannelHandle, [MarshalAs(UnmanagedType.I1)] out bool isOpen);
///
/// Sets the callbacks for a data channel.
///
/// The handle of the data channel.
/// Native callbacks object to set the data channel with.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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 MLWebRTCDataChannelSetEventCallbacks(ulong dataChannelHandle, in MLWebRTCDataChannelEventCallbacks callbacks);
///
/// Sends a message to a data channel.
///
/// The handle of the data channel.
/// Native message object to send to the data channel.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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 MLWebRTCDataChannelSendMessage(ulong dataChannelHandle, in MLWebRTCDataChannelMessage message);
///
/// Destroys a data channel.
///
/// The handle of the associated connection.
/// The handle of the data channel.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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 MLWebRTCDataChannelDestroy(ulong connectionHandle, ulong dataChannelHandle);
///
/// Sets the callbacks of a data channel.
///
/// The data channel to reference for the callbacks.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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.
///
public static MLResult.Code SetCallbacks(MLWebRTC.DataChannel dataChannel)
{
dataChannel.gcHandle = GCHandle.Alloc(dataChannel);
IntPtr gcHandlePtr = GCHandle.ToIntPtr(dataChannel.gcHandle);
NativeBindings.MLWebRTCDataChannelEventCallbacks callbacks = NativeBindings.MLWebRTCDataChannelEventCallbacks.Create(gcHandlePtr);
MLResult.Code resultCode = NativeBindings.MLWebRTCDataChannelSetEventCallbacks(dataChannel.Handle, in callbacks);
if (!MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWebRTCDataChannelSetEventCallbacks)))
{
dataChannel.gcHandle.Free();
}
return resultCode;
}
///
/// Sends a string message to a data channel.
///
/// Data channel to send the message to.
/// The string to send.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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.
///
public static MLResult.Code SendMessageToDataChannel(MLWebRTC.DataChannel dataChannel, string message)
{
NativeBindings.MLWebRTCDataChannelMessage messageNative = NativeBindings.MLWebRTCDataChannelMessage.Create(message);
MLResult.Code resultCode = NativeBindings.MLWebRTCDataChannelSendMessage(dataChannel.Handle, in messageNative);
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWebRTCDataChannelSendMessage));
Marshal.FreeHGlobal(messageNative.Data);
return resultCode;
}
///
/// Sends a string message to a data channel.
///
/// Data channel to send the message to.
/// The byte array to send.
///
/// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful.
/// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was 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.
///
public static MLResult.Code SendMessageToDataChannel(MLWebRTC.DataChannel dataChannel, T[] message)
{
NativeBindings.MLWebRTCDataChannelMessage messageNative = NativeBindings.MLWebRTCDataChannelMessage.Create(message);
MLResult.Code resultCode = NativeBindings.MLWebRTCDataChannelSendMessage(dataChannel.Handle, in messageNative);
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWebRTCDataChannelSendMessage));
Marshal.FreeHGlobal(messageNative.Data);
return resultCode;
}
///
/// Native callback that is invoked when a data channel opens.
///
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(OnOpenedDelegate))]
private static void OnOpened(IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
DataChannel dataChannel = gcHandle.Target as DataChannel;
dataChannel?.OnOpened?.Invoke(dataChannel);
});
}
///
/// Native callback that is invoked when a data channel closes.
///
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(OnClosedDelegate))]
private static void OnClosed(IntPtr context)
{
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
DataChannel dataChannel = gcHandle.Target as DataChannel;
dataChannel?.OnClosed?.Invoke(dataChannel);
});
}
///
/// Native callback that is invoked when a data channel receives a message.
///
/// Message object received.
/// Pointer to a context object.
[AOT.MonoPInvokeCallback(typeof(OnMessageDelegate))]
private static void OnMessage(in NativeBindings.MLWebRTCDataChannelMessage message, IntPtr context)
{
object messageObject = null;
bool isBinary = message.IsBinary;
if (isBinary)
{
byte[] bytes = new byte[message.DataSize];
Marshal.Copy(message.Data, bytes, 0, (int)message.DataSize);
messageObject = bytes;
}
else
{
string chars = Marshal.PtrToStringAnsi(message.Data, (int)message.DataSize);
messageObject = chars;
}
MLThreadDispatch.ScheduleMain(() =>
{
GCHandle gcHandle = GCHandle.FromIntPtr(context);
DataChannel dataChannel = gcHandle.Target as DataChannel;
if (isBinary)
{
dataChannel?.OnMessageBinary?.Invoke(dataChannel, messageObject as byte[]);
}
else
{
dataChannel?.OnMessageText?.Invoke(dataChannel, messageObject as string);
}
});
}
///
/// The native representation of the MLWebRTC data channel message.
///
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCDataChannelMessage
{
///
/// Version of the struct.
///
public uint Version;
///
/// Pointer to the message in unmanaged memory.
///
public IntPtr Data;
///
/// Describes how large the unmanaged data is.
///
public ulong DataSize;
///
/// Determines if the message is a string or byte array.
///
[MarshalAs(UnmanagedType.I1)]
public bool IsBinary;
///
/// Creates an initialized MLWebRTCDataChannelMessage object.
///
/// The string message to send.
/// An initialized MLWebRTCDataChannelMessage object.
public static MLWebRTCDataChannelMessage Create(string message)
{
MLWebRTCDataChannelMessage channelMessage = new MLWebRTCDataChannelMessage();
channelMessage.Version = 1;
channelMessage.Data = Marshal.StringToHGlobalAnsi(message);
channelMessage.DataSize = (ulong)message.Length;
channelMessage.IsBinary = false;
return channelMessage;
}
///
/// Creates an initialized MLWebRTCDataChannelMessage object.
///
/// The byte array message to send.
/// An initialized MLWebRTCDataChannelMessage object.
public static MLWebRTCDataChannelMessage Create(T[] message)
{
MLWebRTCDataChannelMessage channelMessage = new MLWebRTCDataChannelMessage();
channelMessage.Version = 1;
int typeSize = Marshal.SizeOf(typeof(T));
int unmanagedByteArrayLength = (typeSize * message.Length);
channelMessage.Data = Marshal.AllocHGlobal(unmanagedByteArrayLength);
IntPtr next = channelMessage.Data;
for (int i = 0; i < message.Length; ++i)
{
Marshal.StructureToPtr(message[i], next, false);
next = IntPtr.Add(next, typeSize);
}
channelMessage.DataSize = (ulong)unmanagedByteArrayLength;
channelMessage.IsBinary = true;
return channelMessage;
}
}
///
/// The native representation of the MLWebRTC data channel callback events.
///
[StructLayout(LayoutKind.Sequential)]
public struct MLWebRTCDataChannelEventCallbacks
{
///
/// Version of the struct.
///
public uint Version;
///
/// Context pointer used with the dataChannelCounter field to determine which data channel that callbacks are associated with.
///
public IntPtr Context;
///
/// Native OnOpen event.
///
public OnOpenedDelegate OnOpen;
///
/// Native OnClosed event.
///
public OnClosedDelegate OnClosed;
///
/// Native OnMessage event.
///
public OnMessageDelegate OnMessage;
///
/// Creates an initialized MLWebRTCDataChannelEventCallbacks object.
///
/// The context object to use.
/// An initialized MLWebRTCDataChannelEventCallbacks object.
public static MLWebRTCDataChannelEventCallbacks Create(IntPtr context)
{
MLWebRTCDataChannelEventCallbacks callbacks = new MLWebRTCDataChannelEventCallbacks();
callbacks.Version = 1;
callbacks.OnOpen = DataChannel.NativeBindings.OnOpened;
callbacks.OnClosed = DataChannel.NativeBindings.OnClosed;
callbacks.OnMessage = DataChannel.NativeBindings.OnMessage;
callbacks.Context = context;
return callbacks;
}
}
}
}
}
}