1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
|
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;
}
}
|