using System; using System.Linq; using System.Collections.Generic; using System.Reflection; using System.Threading; using UnityEngine; using Unity.WebRTC; using Unity.RenderStreaming.Signaling; #if UNITY_EDITOR using UnityEditor; #endif namespace Unity.RenderStreaming { [AddComponentMenu("Render Streaming/Signaling Manager")] public sealed class SignalingManager : MonoBehaviour { #pragma warning disable 0649 [SerializeField] 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 handlers = new List(); /// /// /// [SerializeField, Tooltip("Automatically started when called Awake method.")] public bool runOnAwake = true; /// /// /// [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; 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); } /// /// Use settings in Project Settings. /// public bool useDefaultSettings { get { return m_useDefault; } set { m_useDefault = value; } } /// /// /// /// /// 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; } /// /// /// /// public SignalingSettings GetSignalingSettings() { return signalingSettings; } /// /// /// /// 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); } /// /// /// /// public void RemoveSignalingHandler(SignalingHandlerBase handlerBase) { handlers.Remove(handlerBase); if (!m_running) { return; } handlerBase.SetHandler(null); m_provider.Unsubscribe(handlerBase); } /// /// /// /// /// public void Run( ISignaling signaling = null, SignalingHandlerBase[] handlers = null) { Debug.Log("RunRunRunRunRunRunRunRunRunRun"); _Run(null, signaling, handlers); } /// /// /// /// /// /// /// To use this method, Need to depend WebRTC package 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; #if !UNITY_EDITOR var arguments = Environment.GetCommandLineArgs(); if (evaluateCommandlineArguments && arguments.Length > 1) { if (!EvaluateCommandlineArguments(ref settings, arguments)) { Debug.LogError("Command line arguments are invalid."); } } #endif RTCIceServer[] iceServers = settings.iceServers.OfType().ToArray(); 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.GetTypesDerivedFrom(); Dictionary map = types.Where(type => type.GetCustomAttribute() != null) .ToDictionary(type => type.GetCustomAttribute().typename, type => type); if (map.ContainsKey(signalingTypeName)) { var type = map[signalingTypeName]; settings = (SignalingSettings)Activator.CreateInstance(type); } } return settings.ParseArguments(arguments); } /// /// /// public void Stop() { m_instance?.Dispose(); m_instance = null; m_running = false; } void Awake() { if (!runOnAwake || m_running || handlers.Count == 0) return; var settings = m_useDefault ? RenderStreaming.GetSignalingSettings() : signalingSettings; RTCIceServer[] iceServers = settings.iceServers.OfType().ToArray(); RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers }; ISignaling signaling = CreateSignaling(settings, SynchronizationContext.Current); _Run(conf, signaling, handlers.ToArray()); } void OnDestroy() { Stop(); } } }