Core: Improve recording stability

This commit is contained in:
Genteure 2021-04-23 19:15:20 +08:00
parent 2e353da446
commit 539b0b95a4
4 changed files with 59 additions and 24 deletions

View File

@ -23,8 +23,6 @@ namespace BililiveRecorder.Core.Recording
public override void SplitOutput() { }
public override void RequestStop() => this.cts.Cancel();
protected override void StartRecordingLoop(Stream stream)
{
var (fullPath, relativePath) = this.CreateFileName();
@ -79,10 +77,8 @@ namespace BililiveRecorder.Core.Recording
}
finally
{
this.logger.Debug("录制退出");
this.timer.Stop();
this.cts.Cancel();
this.RequestStop();
try
{
@ -104,10 +100,12 @@ namespace BililiveRecorder.Core.Recording
this.logger.Warning(ex, "Error calling OnRecordFileClosed");
}
stream.Dispose();
file.Dispose();
stream.Dispose();
this.OnRecordSessionEnded(EventArgs.Empty);
this.logger.Debug("录制退出");
}
}
}

View File

@ -30,6 +30,7 @@ namespace BililiveRecorder.Core.Recording
protected readonly IApiClient apiClient;
protected bool started = false;
protected bool timeoutTriggered = false;
private readonly object fillerStatsLock = new object();
protected int fillerDownloadedBytes;
@ -85,6 +86,18 @@ namespace BililiveRecorder.Core.Recording
this.fillerStatsLastTrigger = DateTimeOffset.UtcNow;
this.durationSinceNoDataReceived = TimeSpan.Zero;
this.ct.Register(state => Task.Run(async () =>
{
try
{
await Task.Delay(1000);
if (((WeakReference<Stream>)state).TryGetTarget(out var weakStream))
weakStream.Dispose();
}
catch (Exception)
{ }
}), state: new WeakReference<Stream>(stream), useSynchronizationContext: false);
this.StartRecordingLoop(stream);
}
@ -118,8 +131,9 @@ namespace BililiveRecorder.Core.Recording
Mbps = mbps
});
if (this.durationSinceNoDataReceived.TotalMilliseconds > this.room.RoomConfig.TimingWatchdogTimeout)
if ((!this.timeoutTriggered) && (this.durationSinceNoDataReceived.TotalMilliseconds > this.room.RoomConfig.TimingWatchdogTimeout))
{
this.timeoutTriggered = true;
this.logger.Warning("直播服务器未断开连接但停止发送直播数据,将会主动断开连接");
this.RequestStop();
}

View File

@ -111,6 +111,7 @@ namespace BililiveRecorder.Core.Recording
{
const int minimumBufferSize = 1024;
this.timer.Start();
Exception? exception = null;
try
{
@ -188,15 +189,15 @@ namespace BililiveRecorder.Core.Recording
}
finally
{
this.logger.Debug("录制退出");
this.reader?.Dispose();
this.reader = null;
this.writer?.Dispose();
this.writer = null;
this.cts.Cancel();
this.RequestStop();
this.OnRecordSessionEnded(EventArgs.Empty);
this.logger.Debug("录制退出");
}
}

View File

@ -127,7 +127,7 @@ namespace BililiveRecorder.Core
}
catch (Exception ex)
{
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Debug : LogEventLevel.Warning, ex, "尝试开始录制时出错");
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "尝试开始录制时出错");
}
});
}
@ -159,7 +159,7 @@ namespace BililiveRecorder.Core
}
catch (Exception ex)
{
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Debug : LogEventLevel.Warning, ex, "刷新房间信息时出错");
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "刷新房间信息时出错");
}
}
@ -220,13 +220,13 @@ namespace BililiveRecorder.Core
}
catch (Exception ex)
{
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Debug : LogEventLevel.Warning, ex, "启动录制出错");
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "启动录制出错");
this.recordTask = null;
this.OnPropertyChanged(nameof(this.Recording));
// 请求直播流出错时的重试逻辑
_ = Task.Run(async () => await this.RestartAfterRecordTaskFailedAsync().ConfigureAwait(false));
_ = Task.Run(this.RestartAfterRecordTaskFailedAsync);
return;
}
@ -253,6 +253,11 @@ namespace BililiveRecorder.Core
{
await Task.Delay((int)this.RoomConfig.TimingStreamRetry, this.ct).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
// 房间已经被删除
return;
}
finally
{
this.recordRetryDelaySemaphoreSlim.Release();
@ -266,12 +271,10 @@ namespace BililiveRecorder.Core
if (this.Streaming && this.AutoRecordForThisSession)
this.CreateAndStartNewRecordTask();
}
catch (TaskCanceledException) { }
catch (ExecutionRejectedException) { }
catch (Exception ex)
{
this.logger.Write(LogEventLevel.Warning, ex, "重试开始录制时出错");
_ = Task.Run(async () => await this.RestartAfterRecordTaskFailedAsync().ConfigureAwait(false));
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "重试开始录制时出错");
_ = Task.Run(this.RestartAfterRecordTaskFailedAsync);
}
}
@ -282,14 +285,21 @@ namespace BililiveRecorder.Core
try
{
if (delay)
try
{
await Task.Delay((int)this.RoomConfig.TimingDanmakuRetry, this.ct).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
// 房间已被删除
return;
}
await this.danmakuClient.ConnectAsync(this.RoomConfig.RoomId, this.ct).ConfigureAwait(false);
}
catch (TaskCanceledException) { }
catch (Exception ex)
{
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Debug : LogEventLevel.Warning, ex, "连接弹幕服务器时出错");
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Verbose : LogEventLevel.Warning, ex, "连接弹幕服务器时出错");
if (!this.ct.IsCancellationRequested)
this.StartDamakuConnection();
@ -357,13 +367,23 @@ namespace BililiveRecorder.Core
_ = Task.Run(async () =>
{
// 录制结束退出后的重试逻辑
// 比 RestartAfterRecordTaskFailedAsync 少了等待时间
if (!this.Streaming || !this.AutoRecordForThisSession)
return;
try
{
await this.FetchRoomInfoAsync().ConfigureAwait(false);
if (this.Streaming && this.AutoRecordForThisSession)
this.CreateAndStartNewRecordTask();
}
catch (Exception ex)
{
this.logger.Write(LogEventLevel.Warning, ex, "重试开始录制时出错");
_ = Task.Run(this.RestartAfterRecordTaskFailedAsync);
}
});
}
@ -423,6 +443,8 @@ namespace BililiveRecorder.Core
{
_ = Task.Run(async () =>
{
// 定时主动检查不需要错误重试
await this.FetchRoomInfoAsync().ConfigureAwait(false);
if (this.Streaming && this.AutoRecordForThisSession && this.RoomConfig.AutoRecord)