| This directory contains an example Unity native plugin for Windows OS and Android. |
| |
| The APIs use Platform Invoke (P/Invoke) technology as required by Unity native plugin. |
| This plugin dll can also be used by Windows C# applications other than Unity. |
| |
| For detailed build instruction on Android, see ANDROID_INSTRUCTION |
| |
| An example of wrapping native plugin into a C# managed class in Unity is given as following: |
| |
| using System; |
| using System.Collections.Generic; |
| using System.Runtime.InteropServices; |
| |
| namespace SimplePeerConnectionM { |
| // A class for ice candidate. |
| public class IceCandidate { |
| public IceCandidate(string candidate, int sdpMlineIndex, string sdpMid) { |
| mCandidate = candidate; |
| mSdpMlineIndex = sdpMlineIndex; |
| mSdpMid = sdpMid; |
| } |
| string mCandidate; |
| int mSdpMlineIndex; |
| string mSdpMid; |
| |
| public string Candidate { |
| get { return mCandidate; } |
| set { mCandidate = value; } |
| } |
| |
| public int SdpMlineIndex { |
| get { return mSdpMlineIndex; } |
| set { mSdpMlineIndex = value; } |
| } |
| |
| public string SdpMid { |
| get { return mSdpMid; } |
| set { mSdpMid = value; } |
| } |
| } |
| |
| // A managed wrapper up class for the native c style peer connection APIs. |
| public class PeerConnectionM { |
| private const string dllPath = "webrtc_unity_plugin"; |
| |
| //create a peerconnection with turn servers |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern int CreatePeerConnection(string[] turnUrls, int noOfUrls, |
| string username, string credential); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool ClosePeerConnection(int peerConnectionId); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool AddStream(int peerConnectionId, bool audioOnly); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool AddDataChannel(int peerConnectionId); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool CreateOffer(int peerConnectionId); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool CreateAnswer(int peerConnectionId); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool SendDataViaDataChannel(int peerConnectionId, string data); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool SetAudioControl(int peerConnectionId, bool isMute, bool isRecord); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| private delegate void LocalDataChannelReadyInternalDelegate(); |
| public delegate void LocalDataChannelReadyDelegate(int id); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnLocalDataChannelReady( |
| int peerConnectionId, LocalDataChannelReadyInternalDelegate callback); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| private delegate void DataFromDataChannelReadyInternalDelegate(string s); |
| public delegate void DataFromDataChannelReadyDelegate(int id, string s); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnDataFromDataChannelReady( |
| int peerConnectionId, DataFromDataChannelReadyInternalDelegate callback); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| private delegate void FailureMessageInternalDelegate(string msg); |
| public delegate void FailureMessageDelegate(int id, string msg); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnFailure(int peerConnectionId, |
| FailureMessageInternalDelegate callback); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| private delegate void AudioBusReadyInternalDelegate(IntPtr data, int bitsPerSample, |
| int sampleRate, int numberOfChannels, int numberOfFrames); |
| public delegate void AudioBusReadyDelegate(int id, IntPtr data, int bitsPerSample, |
| int sampleRate, int numberOfChannels, int numberOfFrames); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnAudioBusReady(int peerConnectionId, |
| AudioBusReadyInternalDelegate callback); |
| |
| // Video callbacks. |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| private delegate void I420FrameReadyInternalDelegate( |
| IntPtr dataY, IntPtr dataU, IntPtr dataV, |
| int strideY, int strideU, int strideV, |
| uint width, uint height); |
| public delegate void I420FrameReadyDelegate(int id, |
| IntPtr dataY, IntPtr dataU, IntPtr dataV, |
| int strideY, int strideU, int strideV, |
| uint width, uint height); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnLocalI420FrameReady(int peerConnectionId, |
| I420FrameReadyInternalDelegate callback); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnRemoteI420FrameReady(int peerConnectionId, |
| I420FrameReadyInternalDelegate callback); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| private delegate void LocalSdpReadytoSendInternalDelegate(string type, string sdp); |
| public delegate void LocalSdpReadytoSendDelegate(int id, string type, string sdp); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnLocalSdpReadytoSend(int peerConnectionId, |
| LocalSdpReadytoSendInternalDelegate callback); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| private delegate void IceCandidateReadytoSendInternalDelegate( |
| string candidate, int sdpMlineIndex, string sdpMid); |
| public delegate void IceCandidateReadytoSendDelegate( |
| int id, string candidate, int sdpMlineIndex, string sdpMid); |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool RegisterOnIceCandidateReadytoSend( |
| int peerConnectionId, IceCandidateReadytoSendInternalDelegate callback); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool SetRemoteDescription(int peerConnectionId, string type, string sdp); |
| |
| [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] |
| private static extern bool AddIceCandidate(int peerConnectionId, string sdp, |
| int sdpMlineindex, string sdpMid); |
| |
| public PeerConnectionM(List<string> turnUrls, string username, string credential) { |
| string[] urls = turnUrls != null ? turnUrls.ToArray() : null; |
| int length = turnUrls != null ? turnUrls.Count : 0; |
| mPeerConnectionId = CreatePeerConnection(urls, length, username, credential); |
| RegisterCallbacks(); |
| } |
| |
| public void ClosePeerConnection() { |
| ClosePeerConnection(mPeerConnectionId); |
| mPeerConnectionId = -1; |
| } |
| |
| // Return -1 if Peerconnection is not available. |
| public int GetUniqueId() { |
| return mPeerConnectionId; |
| } |
| |
| public void AddStream(bool audioOnly) { |
| AddStream(mPeerConnectionId, audioOnly); |
| } |
| |
| public void AddDataChannel() { |
| AddDataChannel(mPeerConnectionId); |
| } |
| |
| public void CreateOffer() { |
| CreateOffer(mPeerConnectionId); |
| } |
| |
| public void CreateAnswer() { |
| CreateAnswer(mPeerConnectionId); |
| } |
| |
| public void SendDataViaDataChannel(string data) { |
| SendDataViaDataChannel(mPeerConnectionId, data); |
| } |
| |
| public void SetAudioControl(bool isMute, bool isRecord) { |
| SetAudioControl(mPeerConnectionId, isMute, isRecord); |
| } |
| |
| public void SetRemoteDescription(string type, string sdp) { |
| SetRemoteDescription(mPeerConnectionId, type, sdp); |
| } |
| |
| public void AddIceCandidate(string candidate, int sdpMlineindex, string sdpMid) { |
| AddIceCandidate(mPeerConnectionId, candidate, sdpMlineindex, sdpMid); |
| } |
| |
| private void RegisterCallbacks() { |
| localDataChannelReadyDelegate = new LocalDataChannelReadyInternalDelegate( |
| RaiseLocalDataChannelReady); |
| RegisterOnLocalDataChannelReady(mPeerConnectionId, localDataChannelReadyDelegate); |
| |
| dataFromDataChannelReadyDelegate = new DataFromDataChannelReadyInternalDelegate( |
| RaiseDataFromDataChannelReady); |
| RegisterOnDataFromDataChannelReady(mPeerConnectionId, dataFromDataChannelReadyDelegate); |
| |
| failureMessageDelegate = new FailureMessageInternalDelegate(RaiseFailureMessage); |
| RegisterOnFailure(mPeerConnectionId, failureMessageDelegate); |
| |
| audioBusReadyDelegate = new AudioBusReadyInternalDelegate(RaiseAudioBusReady); |
| RegisterOnAudioBusReady(mPeerConnectionId, audioBusReadyDelegate); |
| |
| localI420FrameReadyDelegate = new I420FrameReadyInternalDelegate( |
| RaiseLocalVideoFrameReady); |
| RegisterOnLocalI420FrameReady(mPeerConnectionId, localI420FrameReadyDelegate); |
| |
| remoteI420FrameReadyDelegate = new I420FrameReadyInternalDelegate( |
| RaiseRemoteVideoFrameReady); |
| RegisterOnRemoteI420FrameReady(mPeerConnectionId, remoteI420FrameReadyDelegate); |
| |
| localSdpReadytoSendDelegate = new LocalSdpReadytoSendInternalDelegate( |
| RaiseLocalSdpReadytoSend); |
| RegisterOnLocalSdpReadytoSend(mPeerConnectionId, localSdpReadytoSendDelegate); |
| |
| iceCandidateReadytoSendDelegate = |
| new IceCandidateReadytoSendInternalDelegate(RaiseIceCandidateReadytoSend); |
| RegisterOnIceCandidateReadytoSend( |
| mPeerConnectionId, iceCandidateReadytoSendDelegate); |
| } |
| |
| private void RaiseLocalDataChannelReady() { |
| if (OnLocalDataChannelReady != null) |
| OnLocalDataChannelReady(mPeerConnectionId); |
| } |
| |
| private void RaiseDataFromDataChannelReady(string data) { |
| if (OnDataFromDataChannelReady != null) |
| OnDataFromDataChannelReady(mPeerConnectionId, data); |
| } |
| |
| private void RaiseFailureMessage(string msg) { |
| if (OnFailureMessage != null) |
| OnFailureMessage(mPeerConnectionId, msg); |
| } |
| |
| private void RaiseAudioBusReady(IntPtr data, int bitsPerSample, |
| int sampleRate, int numberOfChannels, int numberOfFrames) { |
| if (OnAudioBusReady != null) |
| OnAudioBusReady(mPeerConnectionId, data, bitsPerSample, sampleRate, |
| numberOfChannels, numberOfFrames); |
| } |
| |
| private void RaiseLocalVideoFrameReady( |
| IntPtr dataY, IntPtr dataU, IntPtr dataV, |
| int strideY, int strideU, int strideV, |
| uint width, uint height) { |
| if (OnLocalVideoFrameReady != null) |
| OnLocalVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, strideY, strideU, strideV, |
| width, height); |
| } |
| |
| private void RaiseRemoteVideoFrameReady( |
| IntPtr dataY, IntPtr dataU, IntPtr dataV, |
| int strideY, int strideU, int strideV, |
| uint width, uint height) { |
| if (OnRemoteVideoFrameReady != null) |
| OnRemoteVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, strideY, strideU, strideV, |
| width, height); |
| } |
| |
| |
| private void RaiseLocalSdpReadytoSend(string type, string sdp) { |
| if (OnLocalSdpReadytoSend != null) |
| OnLocalSdpReadytoSend(mPeerConnectionId, type, sdp); |
| } |
| |
| private void RaiseIceCandidateReadytoSend(string candidate, int sdpMlineIndex, string sdpMid) { |
| if (OnIceCandidateReadytoSend != null) |
| OnIceCandidateReadytoSend(mPeerConnectionId, candidate, sdpMlineIndex, sdpMid); |
| } |
| |
| public void AddQueuedIceCandidate(List<IceCandidate> iceCandidateQueue) { |
| if (iceCandidateQueue != null) { |
| foreach (IceCandidate ic in iceCandidateQueue) { |
| AddIceCandidate(mPeerConnectionId, ic.Candidate, ic.SdpMlineIndex, ic.SdpMid); |
| } |
| } |
| } |
| |
| private LocalDataChannelReadyInternalDelegate localDataChannelReadyDelegate = null; |
| public event LocalDataChannelReadyDelegate OnLocalDataChannelReady; |
| |
| private DataFromDataChannelReadyInternalDelegate dataFromDataChannelReadyDelegate = null; |
| public event DataFromDataChannelReadyDelegate OnDataFromDataChannelReady; |
| |
| private FailureMessageInternalDelegate failureMessageDelegate = null; |
| public event FailureMessageDelegate OnFailureMessage; |
| |
| private AudioBusReadyInternalDelegate audioBusReadyDelegate = null; |
| public event AudioBusReadyDelegate OnAudioBusReady; |
| |
| private I420FrameReadyInternalDelegate localI420FrameReadyDelegate = null; |
| public event I420FrameReadyDelegate OnLocalVideoFrameReady; |
| |
| private I420FrameReadyInternalDelegate remoteI420FrameReadyDelegate = null; |
| public event I420FrameReadyDelegate OnRemoteVideoFrameReady; |
| |
| private LocalSdpReadytoSendInternalDelegate localSdpReadytoSendDelegate = null; |
| public event LocalSdpReadytoSendDelegate OnLocalSdpReadytoSend; |
| |
| private IceCandidateReadytoSendInternalDelegate iceCandidateReadytoSendDelegate = null; |
| public event IceCandidateReadytoSendDelegate OnIceCandidateReadytoSend; |
| |
| private int mPeerConnectionId = -1; |
| } |
| } |