1This directory contains an example Unity native plugin for Windows OS and Android. 2 3The APIs use Platform Invoke (P/Invoke) technology as required by Unity native plugin. 4This plugin dll can also be used by Windows C# applications other than Unity. 5 6For detailed build instruction on Android, see ANDROID_INSTRUCTION 7 8An example of wrapping native plugin into a C# managed class in Unity is given as following: 9 10using System; 11using System.Collections.Generic; 12using System.Runtime.InteropServices; 13 14namespace SimplePeerConnectionM { 15 // A class for ice candidate. 16 public class IceCandidate { 17 public IceCandidate(string candidate, int sdpMlineIndex, string sdpMid) { 18 mCandidate = candidate; 19 mSdpMlineIndex = sdpMlineIndex; 20 mSdpMid = sdpMid; 21 } 22 string mCandidate; 23 int mSdpMlineIndex; 24 string mSdpMid; 25 26 public string Candidate { 27 get { return mCandidate; } 28 set { mCandidate = value; } 29 } 30 31 public int SdpMlineIndex { 32 get { return mSdpMlineIndex; } 33 set { mSdpMlineIndex = value; } 34 } 35 36 public string SdpMid { 37 get { return mSdpMid; } 38 set { mSdpMid = value; } 39 } 40 } 41 42 // A managed wrapper up class for the native c style peer connection APIs. 43 public class PeerConnectionM { 44 private const string dllPath = "webrtc_unity_plugin"; 45 46 //create a peerconnection with turn servers 47 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 48 private static extern int CreatePeerConnection(string[] turnUrls, int noOfUrls, 49 string username, string credential); 50 51 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 52 private static extern bool ClosePeerConnection(int peerConnectionId); 53 54 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 55 private static extern bool AddStream(int peerConnectionId, bool audioOnly); 56 57 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 58 private static extern bool AddDataChannel(int peerConnectionId); 59 60 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 61 private static extern bool CreateOffer(int peerConnectionId); 62 63 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 64 private static extern bool CreateAnswer(int peerConnectionId); 65 66 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 67 private static extern bool SendDataViaDataChannel(int peerConnectionId, string data); 68 69 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 70 private static extern bool SetAudioControl(int peerConnectionId, bool isMute, bool isRecord); 71 72 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 73 private delegate void LocalDataChannelReadyInternalDelegate(); 74 public delegate void LocalDataChannelReadyDelegate(int id); 75 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 76 private static extern bool RegisterOnLocalDataChannelReady( 77 int peerConnectionId, LocalDataChannelReadyInternalDelegate callback); 78 79 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 80 private delegate void DataFromDataChannelReadyInternalDelegate(string s); 81 public delegate void DataFromDataChannelReadyDelegate(int id, string s); 82 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 83 private static extern bool RegisterOnDataFromDataChannelReady( 84 int peerConnectionId, DataFromDataChannelReadyInternalDelegate callback); 85 86 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 87 private delegate void FailureMessageInternalDelegate(string msg); 88 public delegate void FailureMessageDelegate(int id, string msg); 89 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 90 private static extern bool RegisterOnFailure(int peerConnectionId, 91 FailureMessageInternalDelegate callback); 92 93 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 94 private delegate void AudioBusReadyInternalDelegate(IntPtr data, int bitsPerSample, 95 int sampleRate, int numberOfChannels, int numberOfFrames); 96 public delegate void AudioBusReadyDelegate(int id, IntPtr data, int bitsPerSample, 97 int sampleRate, int numberOfChannels, int numberOfFrames); 98 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 99 private static extern bool RegisterOnAudioBusReady(int peerConnectionId, 100 AudioBusReadyInternalDelegate callback); 101 102 // Video callbacks. 103 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 104 private delegate void I420FrameReadyInternalDelegate( 105 IntPtr dataY, IntPtr dataU, IntPtr dataV, 106 int strideY, int strideU, int strideV, 107 uint width, uint height); 108 public delegate void I420FrameReadyDelegate(int id, 109 IntPtr dataY, IntPtr dataU, IntPtr dataV, 110 int strideY, int strideU, int strideV, 111 uint width, uint height); 112 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 113 private static extern bool RegisterOnLocalI420FrameReady(int peerConnectionId, 114 I420FrameReadyInternalDelegate callback); 115 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 116 private static extern bool RegisterOnRemoteI420FrameReady(int peerConnectionId, 117 I420FrameReadyInternalDelegate callback); 118 119 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 120 private delegate void LocalSdpReadytoSendInternalDelegate(string type, string sdp); 121 public delegate void LocalSdpReadytoSendDelegate(int id, string type, string sdp); 122 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 123 private static extern bool RegisterOnLocalSdpReadytoSend(int peerConnectionId, 124 LocalSdpReadytoSendInternalDelegate callback); 125 126 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 127 private delegate void IceCandiateReadytoSendInternalDelegate( 128 string candidate, int sdpMlineIndex, string sdpMid); 129 public delegate void IceCandiateReadytoSendDelegate( 130 int id, string candidate, int sdpMlineIndex, string sdpMid); 131 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 132 private static extern bool RegisterOnIceCandiateReadytoSend( 133 int peerConnectionId, IceCandiateReadytoSendInternalDelegate callback); 134 135 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 136 private static extern bool SetRemoteDescription(int peerConnectionId, string type, string sdp); 137 138 [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] 139 private static extern bool AddIceCandidate(int peerConnectionId, string sdp, 140 int sdpMlineindex, string sdpMid); 141 142 public PeerConnectionM(List<string> turnUrls, string username, string credential) { 143 string[] urls = turnUrls != null ? turnUrls.ToArray() : null; 144 int length = turnUrls != null ? turnUrls.Count : 0; 145 mPeerConnectionId = CreatePeerConnection(urls, length, username, credential); 146 RegisterCallbacks(); 147 } 148 149 public void ClosePeerConnection() { 150 ClosePeerConnection(mPeerConnectionId); 151 mPeerConnectionId = -1; 152 } 153 154 // Return -1 if Peerconnection is not available. 155 public int GetUniqueId() { 156 return mPeerConnectionId; 157 } 158 159 public void AddStream(bool audioOnly) { 160 AddStream(mPeerConnectionId, audioOnly); 161 } 162 163 public void AddDataChannel() { 164 AddDataChannel(mPeerConnectionId); 165 } 166 167 public void CreateOffer() { 168 CreateOffer(mPeerConnectionId); 169 } 170 171 public void CreateAnswer() { 172 CreateAnswer(mPeerConnectionId); 173 } 174 175 public void SendDataViaDataChannel(string data) { 176 SendDataViaDataChannel(mPeerConnectionId, data); 177 } 178 179 public void SetAudioControl(bool isMute, bool isRecord) { 180 SetAudioControl(mPeerConnectionId, isMute, isRecord); 181 } 182 183 public void SetRemoteDescription(string type, string sdp) { 184 SetRemoteDescription(mPeerConnectionId, type, sdp); 185 } 186 187 public void AddIceCandidate(string candidate, int sdpMlineindex, string sdpMid) { 188 AddIceCandidate(mPeerConnectionId, candidate, sdpMlineindex, sdpMid); 189 } 190 191 private void RegisterCallbacks() { 192 localDataChannelReadyDelegate = new LocalDataChannelReadyInternalDelegate( 193 RaiseLocalDataChannelReady); 194 RegisterOnLocalDataChannelReady(mPeerConnectionId, localDataChannelReadyDelegate); 195 196 dataFromDataChannelReadyDelegate = new DataFromDataChannelReadyInternalDelegate( 197 RaiseDataFromDataChannelReady); 198 RegisterOnDataFromDataChannelReady(mPeerConnectionId, dataFromDataChannelReadyDelegate); 199 200 failureMessageDelegate = new FailureMessageInternalDelegate(RaiseFailureMessage); 201 RegisterOnFailure(mPeerConnectionId, failureMessageDelegate); 202 203 audioBusReadyDelegate = new AudioBusReadyInternalDelegate(RaiseAudioBusReady); 204 RegisterOnAudioBusReady(mPeerConnectionId, audioBusReadyDelegate); 205 206 localI420FrameReadyDelegate = new I420FrameReadyInternalDelegate( 207 RaiseLocalVideoFrameReady); 208 RegisterOnLocalI420FrameReady(mPeerConnectionId, localI420FrameReadyDelegate); 209 210 remoteI420FrameReadyDelegate = new I420FrameReadyInternalDelegate( 211 RaiseRemoteVideoFrameReady); 212 RegisterOnRemoteI420FrameReady(mPeerConnectionId, remoteI420FrameReadyDelegate); 213 214 localSdpReadytoSendDelegate = new LocalSdpReadytoSendInternalDelegate( 215 RaiseLocalSdpReadytoSend); 216 RegisterOnLocalSdpReadytoSend(mPeerConnectionId, localSdpReadytoSendDelegate); 217 218 iceCandiateReadytoSendDelegate = 219 new IceCandiateReadytoSendInternalDelegate(RaiseIceCandiateReadytoSend); 220 RegisterOnIceCandiateReadytoSend( 221 mPeerConnectionId, iceCandiateReadytoSendDelegate); 222 } 223 224 private void RaiseLocalDataChannelReady() { 225 if (OnLocalDataChannelReady != null) 226 OnLocalDataChannelReady(mPeerConnectionId); 227 } 228 229 private void RaiseDataFromDataChannelReady(string data) { 230 if (OnDataFromDataChannelReady != null) 231 OnDataFromDataChannelReady(mPeerConnectionId, data); 232 } 233 234 private void RaiseFailureMessage(string msg) { 235 if (OnFailureMessage != null) 236 OnFailureMessage(mPeerConnectionId, msg); 237 } 238 239 private void RaiseAudioBusReady(IntPtr data, int bitsPerSample, 240 int sampleRate, int numberOfChannels, int numberOfFrames) { 241 if (OnAudioBusReady != null) 242 OnAudioBusReady(mPeerConnectionId, data, bitsPerSample, sampleRate, 243 numberOfChannels, numberOfFrames); 244 } 245 246 private void RaiseLocalVideoFrameReady( 247 IntPtr dataY, IntPtr dataU, IntPtr dataV, 248 int strideY, int strideU, int strideV, 249 uint width, uint height) { 250 if (OnLocalVideoFrameReady != null) 251 OnLocalVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, strideY, strideU, strideV, 252 width, height); 253 } 254 255 private void RaiseRemoteVideoFrameReady( 256 IntPtr dataY, IntPtr dataU, IntPtr dataV, 257 int strideY, int strideU, int strideV, 258 uint width, uint height) { 259 if (OnRemoteVideoFrameReady != null) 260 OnRemoteVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, strideY, strideU, strideV, 261 width, height); 262 } 263 264 265 private void RaiseLocalSdpReadytoSend(string type, string sdp) { 266 if (OnLocalSdpReadytoSend != null) 267 OnLocalSdpReadytoSend(mPeerConnectionId, type, sdp); 268 } 269 270 private void RaiseIceCandiateReadytoSend(string candidate, int sdpMlineIndex, string sdpMid) { 271 if (OnIceCandiateReadytoSend != null) 272 OnIceCandiateReadytoSend(mPeerConnectionId, candidate, sdpMlineIndex, sdpMid); 273 } 274 275 public void AddQueuedIceCandidate(List<IceCandidate> iceCandidateQueue) { 276 if (iceCandidateQueue != null) { 277 foreach (IceCandidate ic in iceCandidateQueue) { 278 AddIceCandidate(mPeerConnectionId, ic.Candidate, ic.SdpMlineIndex, ic.SdpMid); 279 } 280 } 281 } 282 283 private LocalDataChannelReadyInternalDelegate localDataChannelReadyDelegate = null; 284 public event LocalDataChannelReadyDelegate OnLocalDataChannelReady; 285 286 private DataFromDataChannelReadyInternalDelegate dataFromDataChannelReadyDelegate = null; 287 public event DataFromDataChannelReadyDelegate OnDataFromDataChannelReady; 288 289 private FailureMessageInternalDelegate failureMessageDelegate = null; 290 public event FailureMessageDelegate OnFailureMessage; 291 292 private AudioBusReadyInternalDelegate audioBusReadyDelegate = null; 293 public event AudioBusReadyDelegate OnAudioBusReady; 294 295 private I420FrameReadyInternalDelegate localI420FrameReadyDelegate = null; 296 public event I420FrameReadyDelegate OnLocalVideoFrameReady; 297 298 private I420FrameReadyInternalDelegate remoteI420FrameReadyDelegate = null; 299 public event I420FrameReadyDelegate OnRemoteVideoFrameReady; 300 301 private LocalSdpReadytoSendInternalDelegate localSdpReadytoSendDelegate = null; 302 public event LocalSdpReadytoSendDelegate OnLocalSdpReadytoSend; 303 304 private IceCandiateReadytoSendInternalDelegate iceCandiateReadytoSendDelegate = null; 305 public event IceCandiateReadytoSendDelegate OnIceCandiateReadytoSend; 306 307 private int mPeerConnectionId = -1; 308 } 309} 310