// %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 source used by the MLWebRTC API. /// public partial class AppDefinedAudioSource { /// /// Native bindings for the MLWebRTC.AppDefinedAudioSource class. /// internal new class NativeBindings : MagicLeapNativeBindings { /// /// Initialized a given AppDefinedAudioSource object and sets it's callbacks. /// /// The AppDefinedAudioSource object to initialize. /// /// 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 InitializeAppDefinedAudioSource(MLWebRTC.AppDefinedAudioSource appDefinedAudioSource) { appDefinedAudioSource.TrackType = Type.Audio; appDefinedAudioSource.IsLocal = true; appDefinedAudioSource.gcHandle = GCHandle.Alloc(appDefinedAudioSource); IntPtr gcHandlePtr = GCHandle.ToIntPtr(appDefinedAudioSource.gcHandle); AppDefinedSource.NativeBindings.MLWebRTCAppDefinedSourceEventCallbacks callbacks = AppDefinedSource.NativeBindings.MLWebRTCAppDefinedSourceEventCallbacks.Create(gcHandlePtr, OnSetEnabled, OnDestroyed); ulong appDefinedAudioSourceHandle = MagicLeapNativeBindings.InvalidHandle; var sourceParams = Source.NativeBindings.MLWebRTCAppDefinedSourceParams.Create(appDefinedAudioSource.Id, callbacks); MLResult.Code resultCode = Source.NativeBindings.MLWebRTCSourceCreateAppDefinedAudioSourceEx(ref sourceParams, out appDefinedAudioSourceHandle); appDefinedAudioSource.Handle = appDefinedAudioSourceHandle; if (!MLResult.DidNativeCallSucceed(resultCode, nameof(Source.NativeBindings.MLWebRTCSourceCreateAppDefinedAudioSourceEx))) { appDefinedAudioSource.gcHandle.Free(); Marshal.FreeHGlobal(sourceParams.Callbacks); } return resultCode; } public static MLResult.Code PushData(ulong sourceHandle, byte[] audioBuffer, in MLAudioOutput.BufferFormat audioBufferFormat) { MLAudioOutput.NativeBindings.MLAudioBuffer audioBufferNative = new MLAudioOutput.NativeBindings.MLAudioBuffer(); audioBufferNative.Size = (uint)audioBuffer.Length; // TODO : pool this memory since audio buffers should be expected to be submitted every frame? audioBufferNative.Ptr = Marshal.AllocHGlobal(audioBuffer.Length); Marshal.Copy(audioBuffer, 0, audioBufferNative.Ptr, audioBuffer.Length); MLAudioOutput.NativeBindings.MLAudioBufferFormat audioBufferFormatNative = new MLAudioOutput.NativeBindings.MLAudioBufferFormat(audioBufferFormat); MLResult.Code resultCode = MLWebRTCSourceAppDefinedAudioSourcePushData(sourceHandle, in audioBufferNative, in audioBufferFormatNative); MLResult.DidNativeCallSucceed(resultCode, nameof(MLWebRTCSourceAppDefinedAudioSourcePushData)); Marshal.FreeHGlobal(audioBufferNative.Ptr); return resultCode; } public static MLResult.Code PushData(ulong sourceHandle, float[] audioBuffer, in MLAudioOutput.BufferFormat audioBufferFormat) { MLAudioOutput.NativeBindings.MLAudioBuffer audioBufferNative = new MLAudioOutput.NativeBindings.MLAudioBuffer(); int numBytes = (audioBuffer.Length * sizeof(float)); audioBufferNative.Size = (uint)numBytes; // TODO : pool this memory since audio buffers should be expected to be submitted every frame? audioBufferNative.Ptr = Marshal.AllocHGlobal(numBytes); Marshal.Copy(audioBuffer, 0, audioBufferNative.Ptr, audioBuffer.Length); MLAudioOutput.NativeBindings.MLAudioBufferFormat audioBufferFormatNative = new MLAudioOutput.NativeBindings.MLAudioBufferFormat(audioBufferFormat); MLResult.Code resultCode = MLWebRTCSourceAppDefinedAudioSourcePushData(sourceHandle, in audioBufferNative, in audioBufferFormatNative); MLResult.DidNativeCallSucceed(resultCode, nameof(MLWebRTCSourceAppDefinedAudioSourcePushData)); Marshal.FreeHGlobal(audioBufferNative.Ptr); return resultCode; } /// /// Callback that is invoked when the source has been enabled or disabled. This callback will be called on the main thread. /// /// True if the source was enabled. /// Pointer to a context object. [AOT.MonoPInvokeCallback(typeof(AppDefinedSource.NativeBindings.OnSetEnabledDelegate))] private static void OnSetEnabled(bool enabled, IntPtr context) { GCHandle gcHandle = GCHandle.FromIntPtr(context); AppDefinedAudioSource audioSource = gcHandle.Target as AppDefinedAudioSource; audioSource.isEnabled = enabled; audioSource?.OnSourceSetEnabled(enabled); } /// /// Callback that is invoked when the source has been destroyed. This callback will be called on the main thread. /// /// Pointer to a context object. [AOT.MonoPInvokeCallback(typeof(AppDefinedSource.NativeBindings.OnDestroyedDelegate))] private static void OnDestroyed(IntPtr context) { GCHandle gcHandle = GCHandle.FromIntPtr(context); AppDefinedAudioSource audioSource = gcHandle.Target as AppDefinedAudioSource; audioSource?.OnSourceDestroy(); } /// /// Creates the local source that links to the user's camera and mic. /// /// The handle to the local source to return to the caller. /// /// MLResult.Result will be MLResult.Code.Ok if the local source 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 MLWebRTCSourceAppDefinedAudioSourcePushData(ulong sourceHandle, in MLAudioOutput.NativeBindings.MLAudioBuffer audioBuffer, in MLAudioOutput.NativeBindings.MLAudioBufferFormat audioBufferFormat); } } } }