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