/// <summary> /// 开启 /// </summary> /// <param name="user"></param> /// <returns></returns> private static async Task <StartLiveDataInfo> StartLive(User user, LiveSetting liveSetting) { try { //先停止历史直播 if (await LiveApi.StopLive(user)) { GlobalSettings.Logger.LogInfo("尝试关闭历史直播...成功!"); } //修改直播间名称 if (await LiveApi.UpdateLiveRoomName(user, liveSetting.LiveRoomName)) { GlobalSettings.Logger.LogInfo($"成功修改直播间名称。直播间名称:{liveSetting.LiveRoomName}"); } StartLiveDataInfo liveInfo = await LiveApi.StartLive(user, liveSetting.LiveCategory); if (liveInfo != null) { GlobalSettings.Logger.LogInfo("开启直播成功!"); GlobalSettings.Logger.LogInfo($"我的直播间地址:http://live.bilibili.com/{liveInfo.RoomId}"); GlobalSettings.Logger.LogInfo($"推流地址:{liveInfo.Rtmp.Addr}{liveInfo.Rtmp.Code}"); } return(liveInfo); } catch (Exception ex) { GlobalSettings.Logger.LogError($"开启直播失败!错误:{ex.Message}"); return(null); } }
/// <summary> /// 开启 /// </summary> /// <param name="user"></param> /// <returns></returns> private async Task <StartLiveDataInfo> StartLive() { try { //修改直播间名称 if (await _liveApi.UpdateLiveRoomName(this._account, _config.LiveSetting.LiveRoomName)) { _logger.LogInformation($"成功修改直播间名称,直播间名称:{_config.LiveSetting.LiveRoomName}"); } //更新分类 if (await _liveApi.UpdateLiveCategory(this._account, _config.LiveSetting.LiveCategory)) { _logger.LogInformation($"成功修改直播间分类,直播间分类ID:{_config.LiveSetting.LiveCategory}"); } StartLiveDataInfo liveInfo = await _liveApi.StartLive(this._account, _config.LiveSetting.LiveCategory); if (liveInfo == null) { throw new Exception("获取直播信息失败!"); } _logger.LogInformation("开启直播成功!"); _logger.LogInformation($"我的直播间地址:http://live.bilibili.com/{liveInfo.RoomId}"); _logger.LogInformation($"推流地址:{liveInfo.Rtmp.Addr}{liveInfo.Rtmp.Code}"); return(liveInfo); } catch (Exception ex) { _logger.LogError($"开启直播失败!错误:{ex.Message}", ex); return(null); } }
/// <summary> /// 开启 /// </summary> /// <param name="user"></param> /// <returns></returns> private static async Task <bool> StartLive(User user) { try { //加载配置文件 LiveSetting liveSetting = LoadLiveSettingConfig(); //先停止历史直播 if (await LiveApi.StopLive(user)) { GlobalSettings.Logger.LogInfo("尝试关闭历史直播...成功!"); } //修改直播间名称 if (await LiveApi.UpdateLiveRoomName(user, liveSetting.LiveRoomName)) { GlobalSettings.Logger.LogInfo($"成功修改直播间名称。直播间名称:{liveSetting.LiveRoomName}"); } StartLiveDataInfo liveInfo = await LiveApi.StartLive(user, liveSetting.LiveCategory); if (liveInfo != null) { GlobalSettings.Logger.LogInfo("开启直播成功!"); GlobalSettings.Logger.LogInfo($"我的直播间地址:http://live.bilibili.com/{liveInfo.RoomId}"); GlobalSettings.Logger.LogInfo($"推流地址:{liveInfo.Rtmp.Addr}{liveInfo.Rtmp.Code}"); } //开始使用ffmpeg推流直播 StartPublish(liveSetting, $"{liveInfo.Rtmp.Addr}{liveInfo.Rtmp.Code}"); return(true); } catch (Exception ex) { GlobalSettings.Logger.LogError($"开启直播失败!错误:{ex.Message}"); return(false); } }
/// <summary> /// 开始推流 /// </summary> /// <param name="setting"></param> /// <param name="url"></param> /// <returns></returns> private static async Task <bool> StartPublish(User user, LiveSetting setting, string url) { try { if (string.IsNullOrEmpty(setting.CmdString)) { throw new Exception("CMD string can not null."); } if (setting.CmdString.IndexOf("[[URL]]") < 0) { throw new Exception("Cmd args cannot find '[[URL]]' mark."); } setting.CmdString = setting.CmdString.Replace("[[URL]]", $"\"{url}\""); int firstNullChar = setting.CmdString.IndexOf((char)32); if (firstNullChar < 0) { throw new Exception("Cannot find cmd process name(look like 'ping 127.0.0.1','ping' is process name)."); } string cmdName = setting.CmdString.Substring(0, firstNullChar); string cmdArgs = setting.CmdString.Substring(firstNullChar); if (string.IsNullOrEmpty(cmdArgs)) { throw new Exception("Cmd args cannot null."); } var psi = new ProcessStartInfo { FileName = cmdName, Arguments = cmdArgs, RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = RuntimeInformation.IsOSPlatform(OSPlatform.Linux), }; bool isAutoRestart = true; while (isAutoRestart) { isAutoRestart = setting.AutoRestart; while (!await NetworkTools.NetworkCheck()) { GlobalSettings.Logger.LogInfo($"Wait for network..."); await Task.Delay(3000); } if (!await LiveRoomStateCheck(user)) { GlobalSettings.Logger.LogInfo($"Start live failed..."); return(false); } StartLiveDataInfo liveInfo = await StartLive(user, setting); if (liveInfo == null) { GlobalSettings.Logger.LogInfo($"Start live failed..."); return(false); } //启动 var proc = Process.Start(psi); if (proc == null) { throw new Exception("Can not exec set cmd."); } else { //开始读取命令输出 using (var sr = proc.StandardOutput) { while (!sr.EndOfStream) { GlobalSettings.Logger.LogInfo(sr.ReadLine()); } if (!proc.HasExited) { proc.Kill(); } } //退出检测 if (!Console.IsInputRedirected) { Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs eventArgs) => { isAutoRestart = false; }; } if (isAutoRestart) { GlobalSettings.Logger.LogInfo($"Cmd exited. Auto restart."); } else { GlobalSettings.Logger.LogInfo($"Cmd exited."); } } if (isAutoRestart) { GlobalSettings.Logger.LogInfo($"Wait for restart..."); //如果开启了自动重试,那么等待60s后再次尝试 await Task.Delay(60000); } } return(true); } catch (Exception ex) { GlobalSettings.Logger.LogError(ex.Message); return(false); } }
/// <summary> /// 开始推流 /// </summary> /// <param name="setting"></param> /// <param name="url"></param> /// <returns></returns> private async Task <bool> UseFFmpegLive(string url) { try { if (string.IsNullOrEmpty(_config.LiveSetting.FFmpegCmd)) { throw new Exception("推流命令不能为空。"); } int markIndex = _config.LiveSetting.FFmpegCmd.IndexOf("[[URL]]"); if (markIndex < 5) { throw new Exception("在填写的命令中未找到 '[[URL]]'标记。"); } if (_config.LiveSetting.FFmpegCmd[markIndex - 1] == '\"') { throw new Exception(" '[[URL]]'标记前后无需“\"”。"); } string newCmd = _config.LiveSetting.FFmpegCmd.Replace("[[URL]]", $"\"{url}\""); int firstNullChar = newCmd.IndexOf((char)32); if (firstNullChar < 0) { throw new Exception("无法获取命令执行名称,比如在命令ffmpeg -version中,无法获取ffmpeg。"); } string cmdName = newCmd.Substring(0, firstNullChar); string cmdArgs = newCmd.Substring(firstNullChar); if (string.IsNullOrEmpty(cmdArgs)) { throw new Exception("命令参数不能为空!"); } var psi = new ProcessStartInfo { FileName = cmdName, Arguments = cmdArgs, RedirectStandardOutput = true, RedirectStandardError = false, UseShellExecute = false, CreateNoWindow = RuntimeInformation.IsOSPlatform(OSPlatform.Linux), }; bool isAutoRestart = true; while (isAutoRestart) { isAutoRestart = _config.LiveSetting.AutoRestart; //check network while (!await NetworkUtil.NetworkCheck()) { _logger.LogWarning($"网络连接已断开,将在10秒后重新检查网络连接..."); await Task.Delay(10000); } //check token is available if (!ByPassword.IsTokenAvailable(_account.AccessToken)) { await LoginToBilibili(); } //start live StartLiveDataInfo liveInfo = await StartLive(); if (liveInfo == null) { _logger.LogError($"开启B站直播间失败,无法获取推流地址。"); return(false); } _logger.LogInformation("正在初始化推流指令..."); //启动 using (var proc = Process.Start(psi)) { if (proc == null || proc.Id <= 0) { throw new Exception("无法执行指定的推流指令,请检查FFmpegCmd是否填写正确。"); } //退出检测 if (!Console.IsInputRedirected) { Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs eventArgs) => { isAutoRestart = false; }; } await proc.WaitForExitAsync(); if (isAutoRestart) { _logger.LogWarning($"FFmpeg异常退出,将在60秒后重试推流。"); } else { _logger.LogWarning($"FFmpeg异常退出。"); } } if (isAutoRestart) { _logger.LogWarning($"等待重新推流..."); //如果开启了自动重试,那么等待60s后再次尝试 await Task.Delay(60000); } } return(true); } catch (Exception ex) { _logger.LogError(ex.Message, ex); return(false); } }