holopy3/Assets/Normal/Realtime/Native/Plugins/Plugin.cs
2020-12-10 15:25:12 +01:00

363 lines
No EOL
20 KiB
C#

using System;
using System.Runtime.InteropServices;
using AOT;
namespace Normal.Realtime.Native {
// Types
[StructLayout(LayoutKind.Sequential)]
public struct NetworkInfo {
public float roundTripTime;
public float percentOfPacketsLost;
public float sentBandwidth;
public float receivedBandwidth;
public float ackedBandwidth;
public ulong numberOfPacketsSent;
public ulong numberOfPacketsReceived;
public ulong numberOfPacketsAcked;
}
[StructLayout(LayoutKind.Sequential)]
public struct PingResult {
public string address;
public int time;
public PingResult(string address, int time) {
this.address = address;
this.time = time;
}
}
public static class Plugin {
#if UNITY_IOS && !UNITY_EDITOR
private const string realtimeDLLName = "__Internal";
#else
private const string realtimeDLLName = "RealtimeClient";
#endif
static Plugin() {
SetUpLogging();
}
// Logging
public static LogLevel logLevel = LogLevel.LogLevelError;
static void SetUpLogging() {
IntPtr logFunction = Marshal.GetFunctionPointerForDelegate((LogDelegate)Log);
SetLogFunction(logFunction);
}
[DllImport(realtimeDLLName, EntryPoint = "SetLogFunction")]
public static extern void SetLogFunction(IntPtr logFunction);
public enum LogLevel {
LogLevelInfo = 0,
LogLevelWarning = 1,
LogLevelError = 2,
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int LogDelegate(LogLevel logLevel, string message);
[MonoPInvokeCallback(typeof(LogDelegate))]
static int Log(LogLevel level, string message) {
if ((int)level < (int)logLevel)
return message.Length;
#if UNITY_EDITOR
switch (level) {
default:
case LogLevel.LogLevelInfo:
UnityEngine.Debug.Log("Realtime (native): " + message);
break;
case LogLevel.LogLevelWarning:
UnityEngine.Debug.LogWarning("Realtime (native): " + message);
break;
case LogLevel.LogLevelError:
UnityEngine.Debug.LogError("Realtime (native): " + message);
break;
}
return message.Length;
#else
switch (logLevel) {
default:
case LogLevel.LogLevelInfo:
System.Console.WriteLine("Log: " + message);
break;
case LogLevel.LogLevelWarning:
System.Console.WriteLine("Log: [Warning] " + message);
break;
case LogLevel.LogLevelError:
System.Console.WriteLine("Log: [Error]" + message);
break;
}
return message.Length;
#endif
}
// Matcher
[DllImport(realtimeDLLName, EntryPoint = "MatcherCreate")]
public static extern IntPtr MatcherCreate();
[DllImport(realtimeDLLName, EntryPoint = "MatcherDelete")]
public static extern void MatcherDelete(IntPtr matcher);
[DllImport(realtimeDLLName, EntryPoint = "MatcherConnect")]
public static extern void MatcherConnect(IntPtr matcher, string roomName, string appKey, UInt64 clientIdentifier);
[DllImport(realtimeDLLName, EntryPoint = "MatcherTick")]
public static extern int MatcherTick(IntPtr matcher);
[DllImport(realtimeDLLName, EntryPoint = "MatcherDisconnect")]
public static extern void MatcherDisconnect(IntPtr matcher);
[DllImport(realtimeDLLName, EntryPoint = "MatcherGetNumberOfIPsToPing")]
public static extern int MatcherGetNumberOfIPsToPing(IntPtr matcher);
[DllImport(realtimeDLLName, CharSet = CharSet.Auto, EntryPoint = "MatcherGetAddressToPingAtIndex")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string MatcherGetAddressToPingAtIndex(IntPtr matcher, int index);
[DllImport(realtimeDLLName, EntryPoint = "MatcherContinueConnectionWithPingTimes")]
public static extern void MatcherContinueConnectionWithPingTimes(IntPtr matcher, PingResult[] pingResults, int pingResultsLength);
[DllImport(realtimeDLLName, CharSet = CharSet.Auto, EntryPoint = "MatcherGetServerError")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string MatcherGetServerError(IntPtr matcher);
[DllImport(realtimeDLLName, EntryPoint = "MatcherGetConnectTokenLength")]
public static extern int MatcherGetConnectTokenLength(IntPtr matcher);
[DllImport(realtimeDLLName, EntryPoint = "MatcherGetConnectToken")]
public static extern IntPtr MatcherGetConnectToken(IntPtr matcher);
// Client
[DllImport(realtimeDLLName, EntryPoint = "ClientSetUpNetworkStack")]
public static extern void ClientSetUpNetworkStack();
[DllImport(realtimeDLLName, EntryPoint = "ClientTearDownNetworkStack")]
public static extern void ClientTearDownNetworkStack();
[DllImport(realtimeDLLName, EntryPoint = "ClientGenerateSecureClientIdentifier")]
public static extern UInt64 ClientGenerateSecureClientIdentifier();
[DllImport(realtimeDLLName, EntryPoint = "ClientCreate")]
public static extern IntPtr ClientCreate();
[DllImport(realtimeDLLName, EntryPoint = "ClientDelete")]
public static extern void ClientDelete(IntPtr client);
[DllImport(realtimeDLLName, EntryPoint = "ClientGetNetworkStatistics")]
public extern static void ClientGetNetworkStatistics(IntPtr client, [Out] out NetworkInfo result);
[DllImport(realtimeDLLName, EntryPoint = "ClientConnect")]
public static extern void ClientConnect(IntPtr client, UInt64 clientIdentifier, byte[] connectToken, int connectTokenLength);
[DllImport(realtimeDLLName, EntryPoint = "ClientDisconnect")]
public static extern void ClientDisconnect(IntPtr client);
[DllImport(realtimeDLLName, EntryPoint = "ClientTick")]
public static extern int ClientTick(IntPtr client);
[DllImport(realtimeDLLName, EntryPoint = "ClientGetConnecting")]
public static extern bool ClientGetConnecting(IntPtr client);
[DllImport(realtimeDLLName, EntryPoint = "ClientGetConnected")]
public static extern bool ClientGetConnected(IntPtr client);
[DllImport(realtimeDLLName, EntryPoint = "ClientGetDisconnected")]
public static extern bool ClientGetDisconnected(IntPtr client);
[DllImport(realtimeDLLName, EntryPoint = "ClientGetClientIndex")]
public static extern int ClientGetClientIndex(IntPtr client);
// Info
[DllImport(realtimeDLLName, EntryPoint = "ClientGetRoomTime")]
public static extern double ClientGetRoomTime(IntPtr client);
// Persistence
[DllImport(realtimeDLLName, EntryPoint = "ClientSendPersistenceMessage")]
public static extern bool ClientSendPersistenceMessage(IntPtr client, byte[] data, int dataLength, bool reliable);
// TODO: Possibly move this delegate to the Client/AudioStream? So the native interface calls a static method on the class, which fires this event that instances listen to, instead of keeping track of this in the plugin.
public delegate void ClientPersistenceMessageReceived(IntPtr nativeClient, int sender, byte[] data, bool reliable);
public static event ClientPersistenceMessageReceived clientPersistenceMessageReceived;
public static void ClientRegisterPersistenceMessageReceivedCallback(IntPtr client) {
_ClientSetReceivedPersistenceMessageCallback(client, Marshal.GetFunctionPointerForDelegate((PersistenceMessageReceivedFunction)PersistenceMessageReceived));
}
public static void ClientUnregisterPersistenceMessageReceivedCallback(IntPtr client) {
_ClientSetReceivedPersistenceMessageCallback(client, IntPtr.Zero);
}
// TODO: I use byte here instead of bool for reliable because marshalling has trouble with c++ bool vs c# bool vs c BOOL. Fix this at some point.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void PersistenceMessageReceivedFunction(IntPtr client, int sender, IntPtr data, int dataLength, byte reliable);
[MonoPInvokeCallback(typeof(PersistenceMessageReceivedFunction))]
static void PersistenceMessageReceived(IntPtr client, int sender, IntPtr data, int dataLength, byte reliable) {
// Note: This compiles down to a memcpy in mono. I wish I could just use the data directly, but the
// only way to avoid the memcpy is to use the pointer directly. And I don't want the deserializer
// to be worrying about overflows in C#.
byte[] managedData = new byte[dataLength];
Marshal.Copy(data, managedData, 0, dataLength);
if (clientPersistenceMessageReceived != null)
clientPersistenceMessageReceived(client, sender, managedData, reliable != 0);
}
[DllImport(realtimeDLLName, EntryPoint = "ClientSetReceivedPersistenceMessageCallback")]
public static extern IntPtr _ClientSetReceivedPersistenceMessageCallback(IntPtr client, IntPtr persistenceMessageReceivedCallback);
// RPC
[DllImport(realtimeDLLName, EntryPoint = "ClientSendRPCMessage")]
public static extern bool ClientSendRPCMessage(IntPtr client, byte[] data, int dataLength, bool reliable);
// TODO: Possibly move this delegate to the Client/AudioStream? So the native interface calls a static method on the class, which fires this event that instances listen to, instead of keeping track of this in the plugin.
public delegate void ClientRPCMessageReceived(IntPtr nativeClient, int sender, byte[] data, bool reliable);
public static event ClientRPCMessageReceived clientRPCMessageReceived;
public static void ClientRegisterRPCMessageReceivedCallback(IntPtr client) {
_ClientSetReceivedRPCMessageCallback(client, Marshal.GetFunctionPointerForDelegate((RPCMessageReceivedFunction)RPCMessageReceived));
}
public static void ClientUnregisterRPCMessageReceivedCallback(IntPtr client) {
_ClientSetReceivedRPCMessageCallback(client, IntPtr.Zero);
}
// TODO: I use byte here instead of bool for reliable because marshalling has trouble with c++ bool vs c# bool vs c BOOL. Fix this at some point.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void RPCMessageReceivedFunction(IntPtr client, int sender, IntPtr data, int dataLength, byte reliable);
[MonoPInvokeCallback(typeof(RPCMessageReceivedFunction))]
static void RPCMessageReceived(IntPtr client, int sender, IntPtr data, int dataLength, byte reliable) {
// Note: This compiles down to a memcpy in mono. I wish I could just use the data directly, but the
// only way to avoid the memcpy is to use the pointer directly. And I don't want the deserializer
// to be worrying about overflows in C#.
byte[] managedData = new byte[dataLength];
Marshal.Copy(data, managedData, 0, dataLength);
if (clientRPCMessageReceived != null)
clientRPCMessageReceived(client, sender, managedData, reliable != 0);
}
[DllImport(realtimeDLLName, EntryPoint = "ClientSetReceivedRPCMessageCallback")]
public static extern IntPtr _ClientSetReceivedRPCMessageCallback(IntPtr client, IntPtr rpcMessageReceivedCallback);
// Audio
[DllImport(realtimeDLLName, EntryPoint = "ClientCreateAudioInputStream")]
public static extern IntPtr ClientCreateAudioInputStream(IntPtr client, bool voice, int sampleRate, int channels);
[DllImport(realtimeDLLName, EntryPoint = "ClientDeleteAudioInputStream")]
public static extern void ClientDeleteAudioInputStream(IntPtr audioInputStream);
// TODO: Possibly move this delegate to the Client/AudioStream? So the native interface calls a static method on the class, which fires this event that instances listen to, instead of keeping track of this in the plugin.
public delegate void ClientAudioOutputStreamCreated(IntPtr nativeClient, IntPtr nativeAudioOutputStream, IntPtr nativeAudioOutputStreamIdentifier);
public static event ClientAudioOutputStreamCreated clientAudioOutputStreamCreated;
public static void ClientRegisterAudioOutputStreamCreatedCallback(IntPtr client) {
_ClientSetAudioOutputStreamCreatedCallback(client, Marshal.GetFunctionPointerForDelegate((AudioOutputStreamCreatedFunction)AudioOutputStreamCreated));
}
public static void ClientUnregisterAudioOutputStreamCreatedCallback(IntPtr client) {
_ClientSetAudioOutputStreamCreatedCallback(client, IntPtr.Zero);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void AudioOutputStreamCreatedFunction(IntPtr client, IntPtr audioOutputStream, IntPtr audioOutputStreamIdentifier);
[MonoPInvokeCallback(typeof(AudioOutputStreamCreatedFunction))]
static void AudioOutputStreamCreated(IntPtr client, IntPtr audioOutputStream, IntPtr audioOutputStreamIdentifier) {
if (clientAudioOutputStreamCreated != null)
clientAudioOutputStreamCreated(client, audioOutputStream, audioOutputStreamIdentifier);
else
UnityEngine.Debug.LogError("Received AudioOutputStreamCreated callback, but no clients are listening. This will leak memory.");
}
[DllImport(realtimeDLLName, EntryPoint = "ClientSetAudioOutputStreamCreatedCallback")]
public static extern IntPtr _ClientSetAudioOutputStreamCreatedCallback(IntPtr client, IntPtr audioStreamCreatedCallback);
[DllImport(realtimeDLLName, EntryPoint = "ClientDeleteAudioOutputStream")]
public static extern void ClientDeleteAudioOutputStream(IntPtr audioOutputStream);
// TODO: Possibly move this delegate to the Client/AudioStream? So the native interface calls a static method on the class, which fires this event that instances listen to, instead of keeping track of this in the plugin.
public delegate void ClientAudioOutputStreamClosed(IntPtr nativeClient, IntPtr nativeAudioOutputStreamIdentifier);
public static event ClientAudioOutputStreamClosed clientAudioOutputStreamClosed;
public static void ClientRegisterAudioOutputStreamClosedCallback(IntPtr client) {
_ClientSetAudioOutputStreamClosedCallback(client, Marshal.GetFunctionPointerForDelegate((AudioOutputStreamClosedFunction)AudioOutputStreamClosed));
}
public static void ClientUnregisterAudioOutputStreamClosedCallback(IntPtr client) {
_ClientSetAudioOutputStreamClosedCallback(client, IntPtr.Zero);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void AudioOutputStreamClosedFunction(IntPtr client, IntPtr audioOutputStreamIdentifier);
[MonoPInvokeCallback(typeof(AudioOutputStreamClosedFunction))]
static void AudioOutputStreamClosed(IntPtr client, IntPtr audioOutputStreamIdentifier) {
if (clientAudioOutputStreamClosed != null)
clientAudioOutputStreamClosed(client, audioOutputStreamIdentifier);
}
[DllImport(realtimeDLLName, EntryPoint = "ClientSetAudioOutputStreamClosedCallback")]
public static extern IntPtr _ClientSetAudioOutputStreamClosedCallback(IntPtr client, IntPtr audioStreamClosedCallback);
// Audio Input Stream
[DllImport(realtimeDLLName, EntryPoint = "AudioInputStreamGetClientID")]
public static extern int AudioInputStreamGetClientID(IntPtr audioInputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioInputStreamGetStreamID")]
public static extern int AudioInputStreamGetStreamID(IntPtr audioInputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioInputStreamClose")]
public static extern void AudioInputStreamClose(IntPtr audioInputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioInputStreamSendRawAudioData")]
public static extern bool AudioInputStreamSendRawAudioData(IntPtr audioInputStream, float[] audioData, int audioDataLength);
// Audio Output Stream
[DllImport(realtimeDLLName, EntryPoint = "AudioOutputStreamGetClientID")]
public static extern int AudioOutputStreamGetClientID(IntPtr audioOutputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioOutputStreamGetStreamID")]
public static extern int AudioOutputStreamGetStreamID(IntPtr audioOutputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioOutputStreamGetSampleRate")]
public static extern int AudioOutputStreamGetSampleRate(IntPtr audioOutputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioOutputStreamSetSampleRate")]
public static extern void AudioOutputStreamSetSampleRate(IntPtr audioOutputStream, int sampleRate);
[DllImport(realtimeDLLName, EntryPoint = "AudioOutputStreamGetChannels")]
public static extern int AudioOutputStreamGetChannels(IntPtr audioOutputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioOutputStreamGetIsOpen")]
public static extern int AudioOutputStreamGetIsOpen(IntPtr audioOutputStream);
[DllImport(realtimeDLLName, EntryPoint = "AudioOutputStreamGetAudioData")]
public static extern int AudioOutputStreamGetAudioData(IntPtr audioOutputStream, float[] audioData, int audioDataLength);
// Audio Preprocessor
[DllImport(realtimeDLLName, EntryPoint = "AudioPreprocessorCreate")]
public static extern IntPtr AudioPreprocessorCreate(int recordSampleRate, int recordFrameSize, bool automaticGainControl, bool noiseSuppression, bool reverbSuppression, bool echoCancellation, int playbackSampleRate, int playbackChannels, float tail);
[DllImport(realtimeDLLName, EntryPoint = "AudioPreprocessorDelete")]
public static extern void AudioPreprocessorDelete(IntPtr audioPreprocessor);
[DllImport(realtimeDLLName, EntryPoint = "AudioPreprocessorProcessRecordFrame")]
public static extern bool AudioPreprocessorProcessRecordFrame(IntPtr audioPreprocessor, float[] audioData, int audioDataLength);
[DllImport(realtimeDLLName, EntryPoint = "AudioPreprocessorProcessPlaybackFrame")]
public static extern bool AudioPreprocessorProcessPlaybackFrame(IntPtr audioPreprocessor, float[] audioData, int audioDataLength);
// Microphone
[DllImport(realtimeDLLName, EntryPoint = "MicrophonePlatformSupported")]
public static extern bool MicrophonePlatformSupported();
[DllImport(realtimeDLLName, EntryPoint = "MicrophoneCreate")]
public static extern IntPtr MicrophoneCreate();
[DllImport(realtimeDLLName, EntryPoint = "MicrophoneDelete")]
public static extern void MicrophoneDelete(IntPtr microphone);
[DllImport(realtimeDLLName, EntryPoint = "MicrophoneStart")]
public static extern bool MicrophoneStart(IntPtr microphone);
[DllImport(realtimeDLLName, EntryPoint = "MicrophoneStop")]
public static extern void MicrophoneStop(IntPtr microphone);
[DllImport(realtimeDLLName, EntryPoint = "MicrophoneGetSampleRate")]
public static extern int MicrophoneGetSampleRate(IntPtr microphone);
[DllImport(realtimeDLLName, EntryPoint = "MicrophoneGetChannels")]
public static extern int MicrophoneGetChannels(IntPtr microphone);
[DllImport(realtimeDLLName, EntryPoint = "MicrophoneGetAudioData")]
public static extern bool MicrophoneGetAudioData(IntPtr microphone, float[] audioData, int audioDataLength);
}
}