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