/// <summary> /// <para>Events:</para> /// <para>@emits died - (error: Error)</para> /// <para>@emits @success</para> /// <para>@emits @failure - (error: Error)</para> /// <para>Observer events:</para> /// <para>@emits close</para> /// <para>@emits newrouter - (router: Router)</para> /// </summary> /// <param name="loggerFactory"></param> /// <param name="hostEnvironment"></param> /// <param name="mediasoupOptions"></param> public Worker(ILoggerFactory loggerFactory, MediasoupOptions mediasoupOptions) { _loggerFactory = loggerFactory; _logger = loggerFactory.CreateLogger <Worker>(); _closeLock.Set(); var workerPath = mediasoupOptions.MediasoupStartupSettings.WorkerPath; if (workerPath.IsNullOrWhiteSpace()) { // 见:https://docs.microsoft.com/en-us/dotnet/core/rid-catalog string rid; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { rid = "linux"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { rid = "osx"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { rid = "win"; } else { throw new NotSupportedException("Unsupported platform"); } var location = Assembly.GetEntryAssembly().Location; var directory = Path.GetDirectoryName(location); workerPath = Path.Combine(directory, "runtimes", rid, "native", "mediasoup-worker"); } var workerSettings = mediasoupOptions.MediasoupSettings.WorkerSettings; AppData = workerSettings.AppData; var env = new[] { $"MEDIASOUP_VERSION={mediasoupOptions.MediasoupStartupSettings.MediasoupVersion}" }; var args = new List <string> { workerPath }; if (workerSettings.LogLevel.HasValue) { args.Add($"--logLevel={workerSettings.LogLevel.GetEnumStringValue()}"); } if (!workerSettings.LogTags.IsNullOrEmpty()) { workerSettings.LogTags.ForEach(m => args.Add($"--logTag={m.GetEnumStringValue()}")); } if (workerSettings.RtcMinPort.HasValue) { args.Add($"--rtcMinPort={workerSettings.RtcMinPort}"); } if (workerSettings.RtcMaxPort.HasValue) { args.Add($"--rtcMaxPort={workerSettings.RtcMaxPort}"); } if (!workerSettings.DtlsCertificateFile.IsNullOrWhiteSpace()) { args.Add($"--dtlsCertificateFile={workerSettings.DtlsCertificateFile}"); } if (!workerSettings.DtlsPrivateKeyFile.IsNullOrWhiteSpace()) { args.Add($"--dtlsPrivateKeyFile={workerSettings.DtlsPrivateKeyFile}"); } _logger.LogDebug($"Worker() | Spawning worker process: {args.Join(" ")}"); _pipes = new Pipe[StdioCount]; // fd 0 (stdin) : Just ignore it. (忽略标准输入) // fd 1 (stdout) : Pipe it for 3rd libraries that log their own stuff. // fd 2 (stderr) : Same as stdout. // fd 3 (channel) : Producer Channel fd. // fd 4 (channel) : Consumer Channel fd. // fd 5 (channel) : Producer PayloadChannel fd. // fd 6 (channel) : Consumer PayloadChannel fd. for (var i = 1; i < StdioCount; i++) { _pipes[i] = new Pipe() { Writeable = true, Readable = true }; } try { // 和 Node.js 不同,_child 没有 error 事件。不过,Process.Spawn 可抛出异常。 _child = Process.Spawn(new ProcessOptions() { File = workerPath, Arguments = args.ToArray(), Environment = env, Detached = false, Streams = _pipes, }, OnExit); ProcessId = _child.Id; } catch (Exception ex) { _child = null; CloseAsync().ConfigureAwait(false).GetAwaiter().GetResult(); if (!_spawnDone) { _spawnDone = true; _logger.LogError(ex, $"Worker() | Worker process failed [pid:{ProcessId}]"); Emit("@failure", ex); } else { // 执行到这里的可能性? _logger.LogError(ex, $"Worker() | Worker process error [pid:{ProcessId}]"); Emit("died", ex); } } _channel = new Channel(_loggerFactory.CreateLogger <Channel>(), _pipes[3], _pipes[4], ProcessId); _channel.MessageEvent += OnChannelMessage; _payloadChannel = new PayloadChannel(_loggerFactory.CreateLogger <PayloadChannel>(), _pipes[5], _pipes[6], ProcessId); _pipes.ForEach(m => m?.Resume()); }
/// <summary> /// <para>Events:</para> /// <para>@emits died - (error: Error)</para> /// <para>@emits @success</para> /// <para>@emits @failure - (error: Error)</para> /// <para>Observer events:</para> /// <para>@emits close</para> /// <para>@emits newrouter - (router: Router)</para> /// </summary> /// <param name="loggerFactory"></param> /// <param name="mediasoupOptions"></param> public Worker(ILoggerFactory loggerFactory, MediasoupOptions mediasoupOptions) { _loggerFactory = loggerFactory; _logger = loggerFactory.CreateLogger <Worker>(); var workerSettings = mediasoupOptions.MediasoupSettings.WorkerSettings; AppData = workerSettings.AppData; var env = new[] { $"MEDIASOUP_VERSION={mediasoupOptions.MediasoupStartupSettings.MediasoupVersion}" }; var args = new List <string> { mediasoupOptions.MediasoupStartupSettings.WorkerPath }; if (workerSettings.LogLevel.HasValue) { args.Add($"--logLevel={Enum.GetName(typeof(WorkerLogLevel), workerSettings.LogLevel).ToLowerInvariant()}"); } if (!workerSettings.LogTags.IsNullOrEmpty()) { workerSettings.LogTags.ForEach(m => args.Add($"--logTag={m}")); } if (workerSettings.RtcMinPort.HasValue) { args.Add($"--rtcMinPort={workerSettings.RtcMinPort}"); } if (workerSettings.RtcMaxPort.HasValue) { args.Add($"--rtcMaxPort={workerSettings.RtcMaxPort}"); } if (!workerSettings.DtlsCertificateFile.IsNullOrWhiteSpace()) { args.Add($"--dtlsCertificateFile={workerSettings.DtlsCertificateFile}"); } if (!workerSettings.DtlsPrivateKeyFile.IsNullOrWhiteSpace()) { args.Add($"--dtlsPrivateKeyFile={workerSettings.DtlsPrivateKeyFile}"); } _logger.LogDebug($"Worker() | spawning worker process: {args.ToArray().Join(" ")}"); _pipes = new Pipe[StdioCount]; // 备注:忽略标准输入 for (var i = 1; i < StdioCount; i++) { _pipes[i] = new Pipe() { Writeable = true, Readable = true }; } try { // 备注:和 Node.js 不同,_child 没有 error 事件。不过,Process.Spawn 可抛出异常。 _child = Process.Spawn(new ProcessOptions() { File = mediasoupOptions.MediasoupStartupSettings.WorkerPath, Arguments = args.ToArray(), Environment = env, Detached = false, Streams = _pipes, }, OnExit); ProcessId = _child.Id; } catch (Exception ex) { _child = null; Close(); if (!_spawnDone) { _spawnDone = true; _logger.LogError($"Worker() | worker process failed [pid:{ProcessId}]: {ex.Message}"); Emit("@failure", ex); } else { // 执行到这里的可能性? _logger.LogError($"Worker() | worker process error [pid:{ProcessId}]: {ex.Message}"); Emit("died", ex); } } _channel = new Channel(_loggerFactory.CreateLogger <Channel>(), _pipes[3], _pipes[4], ProcessId); _channel.RunningEvent += OnChannelRunning; _pipes.ForEach(m => m?.Resume()); }