-
Notifications
You must be signed in to change notification settings - Fork 410
Expand file tree
/
Copy pathSignalingManager.cs
More file actions
341 lines (306 loc) · 12.2 KB
/
SignalingManager.cs
File metadata and controls
341 lines (306 loc) · 12.2 KB
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using Unity.RenderStreaming.Signaling;
using Unity.WebRTC;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Unity.RenderStreaming
{
/// <summary>
/// Manages the signaling process for Unity RenderStreaming.
/// </summary>
/// <seealso cref="ISignaling"/>
/// <seealso cref="SignalingSettings"/>
/// <seealso cref="SignalingHandlerBase"/>
[AddComponentMenu("Render Streaming/Signaling Manager")]
public sealed class SignalingManager : MonoBehaviour
{
internal const string UseDefaultPropertyName = nameof(m_useDefault);
internal const string SignalingSettingsObjectPropertyName = nameof(signalingSettingsObject);
internal const string SignalingSettingsPropertyName = nameof(signalingSettings);
internal const string HandlersPropertyName = nameof(handlers);
internal const string RunOnAwakePropertyName = nameof(runOnAwake);
internal const string EvaluateCommandlineArgumentsPropertyName = nameof(evaluateCommandlineArguments);
#pragma warning disable 0649
[SerializeField, Tooltip("Use settings in Project Settings Window.")]
private bool m_useDefault = true;
[SerializeField]
internal SignalingSettingsObject signalingSettingsObject;
[SerializeReference, SignalingSettings]
private SignalingSettings signalingSettings = new WebSocketSignalingSettings();
[SerializeField, Tooltip("List of handlers of signaling process.")]
private List<SignalingHandlerBase> handlers = new List<SignalingHandlerBase>();
/// <summary>
/// Indicates whether the signaling process should automatically start when the Awake method is called.
/// </summary>
[SerializeField, Tooltip("Automatically started when called Awake method.")]
public bool runOnAwake = true;
/// <summary>
/// Indicates whether to evaluate command line arguments if launching runtime on the command line.
/// </summary>
[SerializeField, Tooltip("Evaluate commandline arguments if launching runtime on the command line.")]
public bool evaluateCommandlineArguments = true;
#pragma warning restore 0649
private SignalingManagerInternal m_instance;
private SignalingEventProvider m_provider;
private bool m_running;
/// <summary>
/// Gets a value indicating whether the signaling process is running.
/// </summary>
public bool Running => m_running;
static ISignaling CreateSignaling(SignalingSettings settings, SynchronizationContext context)
{
if (settings.signalingClass == null)
{
throw new ArgumentException($"Signaling type is undefined. {settings.signalingClass}");
}
object[] args = { settings, context };
return (ISignaling)Activator.CreateInstance(settings.signalingClass, args);
}
/// <summary>
/// Use settings in Project Settings.
/// </summary>
public bool useDefaultSettings
{
get { return m_useDefault; }
set { m_useDefault = value; }
}
/// <summary>
/// Sets the signaling settings.
/// </summary>
/// <example>
/// <code>
/// var settings = new WebSocketSignalingSettings("ws://example.com", new[]
/// {
/// new IceServer (urls: new[] {"stun:stun.l.google.com:19302"})
/// });
/// signalingManager.SetSignalingSettings(settings);
///</code>
/// </example>
/// <param name="settings">The signaling settings.</param>
/// <exception cref="InvalidOperationException">Thrown when the signaling process has already started.</exception>
/// <exception cref="ArgumentNullException">Thrown when the settings are null.</exception>
public void SetSignalingSettings(SignalingSettings settings)
{
if (m_running)
throw new InvalidOperationException("The Signaling process has already started.");
if (settings == null)
throw new ArgumentNullException("settings");
signalingSettings = settings;
}
/// <summary>
/// Gets the signaling settings.
/// </summary>
/// <example>
/// <code>
/// var settings = signalingManager.GetSignalingSettings();
/// if (settings is WebSocketSignalingSettings webSocketSettings)
/// {
/// Debug.Log($"WebSocket URL: {webSocketSettings.url}");
/// }
///</code>
/// </example>
/// <returns>The signaling settings.</returns>
public SignalingSettings GetSignalingSettings()
{
return signalingSettings;
}
/// <summary>
/// Adds a signaling handler.
/// </summary>
/// <example>
/// <code>
/// var handler = instance.GetComponent<Multiplay>();
/// signalingManager.AddSignalingHandler(handler);
///</code>
/// </example>
/// <param name="handlerBase">The signaling handler to add.</param>
public void AddSignalingHandler(SignalingHandlerBase handlerBase)
{
if (handlers.Contains(handlerBase))
{
return;
}
handlers.Add(handlerBase);
if (!m_running)
{
return;
}
handlerBase.SetHandler(m_instance);
m_provider.Subscribe(handlerBase);
}
/// <summary>
/// Removes a signaling handler.
/// </summary>
/// <example>
/// <code>
/// var handler = instance.GetComponent<Multiplay>();
/// signalingManager.RemoveSignalingHandler(handler);
///</code>
/// </example>
/// <param name="handlerBase">The signaling handler to remove.</param>
public void RemoveSignalingHandler(SignalingHandlerBase handlerBase)
{
handlers.Remove(handlerBase);
if (!m_running)
{
return;
}
handlerBase.SetHandler(null);
m_provider.Unsubscribe(handlerBase);
}
/// <summary>
/// Runs the signaling process.
/// </summary>
/// <param name="signaling">The signaling instance to use. If null, a new instance will be created.</param>
/// <param name="handlers">The signaling handlers to use. If null, the existing handlers will be used.</param>
/// <example>
/// <code>
/// signalingManager.Run();
///</code>
/// </example>
public void Run(
ISignaling signaling = null,
SignalingHandlerBase[] handlers = null)
{
_Run(null, signaling, handlers);
}
/// <summary>
/// Runs the signaling process with the specified RTC configuration.
/// </summary>
/// <example>
/// <code>
/// var rtcConfig = new RTCConfiguration
/// {
/// iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }
/// };
/// signalingManager.Run(rtcConfig);
///</code>
/// </example>
/// <param name="conf">The RTC configuration.</param>
/// <param name="signaling">The signaling instance to use. If null, a new instance will be created.</param>
/// <param name="handlers">The signaling handlers to use. If null, the existing handlers will be used.</param>
/// <remarks>To use this method, the WebRTC package is required.</remarks>
public void Run(
RTCConfiguration conf,
ISignaling signaling = null,
SignalingHandlerBase[] handlers = null
)
{
_Run(conf, signaling, handlers);
}
#if UNITY_EDITOR
bool IsValidSignalingSettingsObject(SignalingSettingsObject asset)
{
if (asset == null)
return false;
if (AssetDatabase.GetAssetPath(asset).IndexOf("Assets", StringComparison.Ordinal) != 0)
return false;
return true;
}
#endif
private void _Run(
RTCConfiguration? conf = null,
ISignaling signaling = null,
SignalingHandlerBase[] handlers = null
)
{
var settings = m_useDefault ? RenderStreaming.GetSignalingSettings<SignalingSettings>() : signalingSettings;
#if !UNITY_EDITOR
var arguments = Environment.GetCommandLineArgs();
if (evaluateCommandlineArguments && arguments.Length > 1)
{
if (!EvaluateCommandlineArguments(ref settings, arguments))
{
RenderStreaming.Logger.Log(LogType.Error, "Command line arguments are invalid.");
}
}
#endif
int i = 0;
RTCIceServer[] iceServers = new RTCIceServer[settings.iceServers.Count()];
foreach (var iceServer in settings.iceServers)
{
iceServers[i] = (RTCIceServer)iceServer;
i++;
}
RTCConfiguration _conf =
conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers });
ISignaling _signaling = signaling ?? CreateSignaling(settings, SynchronizationContext.Current);
RenderStreamingDependencies dependencies = new RenderStreamingDependencies
{
config = _conf,
signaling = _signaling,
startCoroutine = StartCoroutine,
stopCoroutine = StopCoroutine,
resentOfferInterval = 5.0f,
};
var _handlers = (handlers ?? this.handlers.AsEnumerable()).Where(_ => _ != null);
if (_handlers.Count() == 0)
throw new InvalidOperationException("Handler list is empty.");
m_instance = new SignalingManagerInternal(ref dependencies);
m_provider = new SignalingEventProvider(m_instance);
foreach (var handler in _handlers)
{
handler.SetHandler(m_instance);
m_provider.Subscribe(handler);
}
m_running = true;
}
internal static bool EvaluateCommandlineArguments(ref SignalingSettings settings, string[] arguments)
{
if (!CommandLineParser.TryParse(arguments))
return false;
string signalingTypeName = null;
if (CommandLineParser.SignalingType.Value != null)
{
signalingTypeName = CommandLineParser.SignalingType;
}
else if (CommandLineParser.ImportJson.Value != null)
{
signalingTypeName = CommandLineParser.ImportJson.Value.Value.signalingType;
}
if (signalingTypeName != null)
{
Type[] types = RuntimeTypeCache<SignalingSettings>.GetTypesDerivedFrom();
Dictionary<string, Type> map =
types.Where(type => type.GetCustomAttribute<SignalingTypeAttribute>() != null)
.ToDictionary(type => type.GetCustomAttribute<SignalingTypeAttribute>().typename, type => type);
if (map.ContainsKey(signalingTypeName))
{
var type = map[signalingTypeName];
settings = (SignalingSettings)Activator.CreateInstance(type);
}
}
return settings.ParseArguments(arguments);
}
/// <summary>
/// Stops the signaling process.
/// </summary>
/// <example>
/// <code>
/// signalingManager.Stop();
///</code>
/// </example>
public void Stop()
{
m_instance?.Dispose();
m_instance = null;
m_running = false;
}
void Awake()
{
if (!runOnAwake || m_running || handlers.Count == 0)
return;
_Run(null, null, handlers.ToArray());
}
void OnDestroy()
{
Stop();
}
}
}