2018-03-21 20:56:56 +08:00
|
|
|
|
using System;
|
2019-01-17 00:28:09 +08:00
|
|
|
|
using System.Collections.Generic;
|
2018-03-12 18:57:20 +08:00
|
|
|
|
using System.ComponentModel;
|
2018-03-18 18:55:28 +08:00
|
|
|
|
using System.IO;
|
2018-03-13 13:21:01 +08:00
|
|
|
|
using System.Linq;
|
2018-03-13 14:23:53 +08:00
|
|
|
|
using System.Net;
|
2018-12-11 18:03:06 +08:00
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Net.Http.Headers;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2021-01-01 14:46:27 +08:00
|
|
|
|
using BililiveRecorder.Core.Callback;
|
|
|
|
|
using BililiveRecorder.Core.Config.V2;
|
|
|
|
|
using BililiveRecorder.FlvProcessor;
|
|
|
|
|
using NLog;
|
2018-03-12 18:57:20 +08:00
|
|
|
|
|
|
|
|
|
namespace BililiveRecorder.Core
|
|
|
|
|
{
|
2018-10-24 14:33:05 +08:00
|
|
|
|
public class RecordedRoom : IRecordedRoom
|
2018-03-12 18:57:20 +08:00
|
|
|
|
{
|
2018-03-21 20:56:56 +08:00
|
|
|
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
2018-11-02 00:14:18 +08:00
|
|
|
|
private static readonly Random random = new Random();
|
2020-08-14 17:01:04 +08:00
|
|
|
|
private static readonly Version VERSION_1_0 = new Version(1, 0);
|
2018-03-15 21:55:01 +08:00
|
|
|
|
|
2021-01-08 18:54:50 +08:00
|
|
|
|
#nullable enable
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
private int _shortRoomid;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
private string _streamerName;
|
2020-04-24 22:04:34 +08:00
|
|
|
|
private string _title;
|
2021-01-08 18:54:50 +08:00
|
|
|
|
private string _parentAreaName = string.Empty;
|
|
|
|
|
private string _areaName = string.Empty;
|
2020-12-12 23:19:47 +08:00
|
|
|
|
private bool _isStreaming;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
|
2019-08-22 01:26:18 +08:00
|
|
|
|
public int ShortRoomId
|
2018-10-31 06:22:38 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get => this._shortRoomid;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
private set
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (value == this._shortRoomid) { return; }
|
|
|
|
|
this._shortRoomid = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.ShortRoomId));
|
2018-10-31 06:22:38 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-22 01:26:18 +08:00
|
|
|
|
public int RoomId
|
2018-10-31 06:22:38 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get => this.RoomConfig.RoomId;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
private set
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (value == this.RoomConfig.RoomId) { return; }
|
|
|
|
|
this.RoomConfig.RoomId = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.RoomId));
|
2018-10-31 06:22:38 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public string StreamerName
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get => this._streamerName;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
private set
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (value == this._streamerName) { return; }
|
|
|
|
|
this._streamerName = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.StreamerName));
|
2018-10-31 06:22:38 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-24 22:04:34 +08:00
|
|
|
|
public string Title
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get => this._title;
|
2020-04-24 22:04:34 +08:00
|
|
|
|
private set
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (value == this._title) { return; }
|
|
|
|
|
this._title = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.Title));
|
2020-04-24 22:04:34 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-08 18:54:50 +08:00
|
|
|
|
public string ParentAreaName
|
|
|
|
|
{
|
|
|
|
|
get => this._parentAreaName;
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (value == this._parentAreaName) { return; }
|
|
|
|
|
this._parentAreaName = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.ParentAreaName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public string AreaName
|
|
|
|
|
{
|
|
|
|
|
get => this._areaName;
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (value == this._areaName) { return; }
|
|
|
|
|
this._areaName = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.AreaName));
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-24 22:04:34 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
public bool IsMonitoring => this.StreamMonitor.IsMonitoring;
|
|
|
|
|
public bool IsRecording => !(this.StreamDownloadTask?.IsCompleted ?? true);
|
|
|
|
|
public bool IsDanmakuConnected => this.StreamMonitor.IsDanmakuConnected;
|
2020-12-12 23:19:47 +08:00
|
|
|
|
public bool IsStreaming
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get => this._isStreaming;
|
2020-12-12 23:19:47 +08:00
|
|
|
|
private set
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (value == this._isStreaming) { return; }
|
|
|
|
|
this._isStreaming = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.IsStreaming));
|
2020-12-12 23:19:47 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-12 18:57:20 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
public RoomConfig RoomConfig { get; }
|
|
|
|
|
|
2021-01-08 18:54:50 +08:00
|
|
|
|
#nullable restore
|
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
private RecordEndData recordEndData;
|
|
|
|
|
public event EventHandler<RecordEndData> RecordEnded;
|
|
|
|
|
|
2020-11-23 17:35:42 +08:00
|
|
|
|
private readonly IBasicDanmakuWriter basicDanmakuWriter;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
private readonly Func<IFlvStreamProcessor> newIFlvStreamProcessor;
|
2018-11-03 07:45:56 +08:00
|
|
|
|
private IFlvStreamProcessor _processor;
|
2018-10-31 06:23:01 +08:00
|
|
|
|
public IFlvStreamProcessor Processor
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get => this._processor;
|
2018-10-31 06:23:01 +08:00
|
|
|
|
private set
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (value == this._processor) { return; }
|
|
|
|
|
this._processor = value;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.Processor));
|
2018-10-31 06:23:01 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-24 09:48:06 +08:00
|
|
|
|
|
2020-11-27 18:51:02 +08:00
|
|
|
|
private BililiveAPI BililiveAPI { get; }
|
2018-11-03 07:45:56 +08:00
|
|
|
|
public IStreamMonitor StreamMonitor { get; }
|
2018-10-31 06:22:38 +08:00
|
|
|
|
|
2018-12-17 21:24:52 +08:00
|
|
|
|
private bool _retry = true;
|
2018-12-11 18:03:06 +08:00
|
|
|
|
private HttpResponseMessage _response;
|
2018-11-03 07:45:56 +08:00
|
|
|
|
private Stream _stream;
|
2018-10-31 06:23:01 +08:00
|
|
|
|
private Task StartupTask = null;
|
2019-06-04 15:43:07 +08:00
|
|
|
|
private readonly object StartupTaskLock = new object();
|
2018-10-31 06:23:01 +08:00
|
|
|
|
public Task StreamDownloadTask = null;
|
|
|
|
|
public CancellationTokenSource cancellationTokenSource = null;
|
2018-03-13 13:21:01 +08:00
|
|
|
|
|
2018-11-03 07:45:56 +08:00
|
|
|
|
private double _DownloadSpeedPersentage = 0;
|
2019-11-24 09:08:29 +08:00
|
|
|
|
private double _DownloadSpeedMegaBitps = 0;
|
2018-11-03 07:45:56 +08:00
|
|
|
|
private long _lastUpdateSize = 0;
|
|
|
|
|
private int _lastUpdateTimestamp = 0;
|
|
|
|
|
public DateTime LastUpdateDateTime { get; private set; } = DateTime.Now;
|
|
|
|
|
public double DownloadSpeedPersentage
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get { return this._DownloadSpeedPersentage; }
|
|
|
|
|
private set { if (value != this._DownloadSpeedPersentage) { this._DownloadSpeedPersentage = value; this.TriggerPropertyChanged(nameof(this.DownloadSpeedPersentage)); } }
|
2018-11-03 07:45:56 +08:00
|
|
|
|
}
|
2019-11-24 09:08:29 +08:00
|
|
|
|
public double DownloadSpeedMegaBitps
|
2018-03-26 06:14:01 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
get { return this._DownloadSpeedMegaBitps; }
|
|
|
|
|
private set { if (value != this._DownloadSpeedMegaBitps) { this._DownloadSpeedMegaBitps = value; this.TriggerPropertyChanged(nameof(this.DownloadSpeedMegaBitps)); } }
|
2018-03-26 06:14:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-05 18:30:04 +08:00
|
|
|
|
public Guid Guid { get; } = Guid.NewGuid();
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
// TODO: 重构 DI
|
|
|
|
|
public RecordedRoom(Func<RoomConfig, IBasicDanmakuWriter> newBasicDanmakuWriter,
|
|
|
|
|
Func<RoomConfig, IStreamMonitor> newIStreamMonitor,
|
2018-10-31 06:22:38 +08:00
|
|
|
|
Func<IFlvStreamProcessor> newIFlvStreamProcessor,
|
2020-11-27 18:51:02 +08:00
|
|
|
|
BililiveAPI bililiveAPI,
|
2021-01-01 14:46:27 +08:00
|
|
|
|
RoomConfig roomConfig)
|
2018-03-13 13:21:01 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.RoomConfig = roomConfig;
|
|
|
|
|
this.StreamerName = "获取中...";
|
|
|
|
|
|
|
|
|
|
this.BililiveAPI = bililiveAPI;
|
|
|
|
|
|
2018-10-25 19:20:23 +08:00
|
|
|
|
this.newIFlvStreamProcessor = newIFlvStreamProcessor;
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.basicDanmakuWriter = newBasicDanmakuWriter(this.RoomConfig);
|
2018-03-20 00:12:32 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamMonitor = newIStreamMonitor(this.RoomConfig);
|
|
|
|
|
this.StreamMonitor.RoomInfoUpdated += this.StreamMonitor_RoomInfoUpdated;
|
|
|
|
|
this.StreamMonitor.StreamStarted += this.StreamMonitor_StreamStarted;
|
|
|
|
|
this.StreamMonitor.ReceivedDanmaku += this.StreamMonitor_ReceivedDanmaku;
|
|
|
|
|
this.StreamMonitor.PropertyChanged += this.StreamMonitor_PropertyChanged;
|
2020-11-23 17:35:42 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.PropertyChanged += this.RecordedRoom_PropertyChanged;
|
2018-03-15 21:55:01 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamMonitor.FetchRoomInfoAsync();
|
2019-03-01 18:00:20 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.RoomConfig.AutoRecord)
|
|
|
|
|
this.Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RecordedRoom_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
switch (e.PropertyName)
|
|
|
|
|
{
|
|
|
|
|
case nameof(this.IsMonitoring):
|
|
|
|
|
this.RoomConfig.AutoRecord = this.IsMonitoring;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-08-21 23:57:54 +08:00
|
|
|
|
}
|
2018-03-21 20:56:56 +08:00
|
|
|
|
|
2020-12-15 19:38:35 +08:00
|
|
|
|
private void StreamMonitor_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
switch (e.PropertyName)
|
|
|
|
|
{
|
|
|
|
|
case nameof(IStreamMonitor.IsDanmakuConnected):
|
2021-01-01 14:46:27 +08:00
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.IsDanmakuConnected)));
|
2020-12-15 19:38:35 +08:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-23 17:35:42 +08:00
|
|
|
|
private void StreamMonitor_ReceivedDanmaku(object sender, ReceivedDanmakuArgs e)
|
|
|
|
|
{
|
2020-12-12 23:19:47 +08:00
|
|
|
|
switch (e.Danmaku.MsgType)
|
|
|
|
|
{
|
|
|
|
|
case MsgTypeEnum.LiveStart:
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.IsStreaming = true;
|
2020-12-12 23:19:47 +08:00
|
|
|
|
break;
|
|
|
|
|
case MsgTypeEnum.LiveEnd:
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.IsStreaming = false;
|
2020-12-12 23:19:47 +08:00
|
|
|
|
break;
|
2021-01-08 18:54:50 +08:00
|
|
|
|
case MsgTypeEnum.RoomChange:
|
|
|
|
|
this.Title = e.Danmaku.Title ?? string.Empty;
|
|
|
|
|
this.ParentAreaName = e.Danmaku.ParentAreaName ?? string.Empty;
|
|
|
|
|
this.AreaName = e.Danmaku.AreaName ?? string.Empty;
|
|
|
|
|
break;
|
2020-12-12 23:19:47 +08:00
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.basicDanmakuWriter.Write(e.Danmaku);
|
2020-11-23 17:35:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 23:57:54 +08:00
|
|
|
|
private void StreamMonitor_RoomInfoUpdated(object sender, RoomInfoUpdatedArgs e)
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
// TODO: StreamMonitor 里的 RoomInfoUpdated Handler 也会设置一次 RoomId
|
|
|
|
|
// 暂时保持不变,此处的 RoomId 需要触发 PropertyChanged 事件
|
|
|
|
|
this.RoomId = e.RoomInfo.RoomId;
|
|
|
|
|
this.ShortRoomId = e.RoomInfo.ShortRoomId;
|
2021-01-08 18:54:50 +08:00
|
|
|
|
this.IsStreaming = e.RoomInfo.IsStreaming;
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamerName = e.RoomInfo.UserName;
|
|
|
|
|
this.Title = e.RoomInfo.Title;
|
2021-01-08 18:54:50 +08:00
|
|
|
|
this.ParentAreaName = e.RoomInfo.ParentAreaName;
|
|
|
|
|
this.AreaName = e.RoomInfo.AreaName;
|
2018-03-21 20:56:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Start()
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
// TODO: 重构: 删除 Start() Stop() 通过 RoomConfig.AutoRecord 控制监控状态和逻辑
|
|
|
|
|
if (this.disposedValue) throw new ObjectDisposedException(nameof(RecordedRoom));
|
2018-11-24 23:45:08 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
var r = this.StreamMonitor.Start();
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.IsMonitoring));
|
2018-03-21 20:56:56 +08:00
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
// TODO: 见 Start()
|
|
|
|
|
if (this.disposedValue) throw new ObjectDisposedException(nameof(RecordedRoom));
|
2018-11-24 23:45:08 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamMonitor.Stop();
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.IsMonitoring));
|
2018-03-13 14:23:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 23:57:54 +08:00
|
|
|
|
public void RefreshRoomInfo()
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.disposedValue) throw new ObjectDisposedException(nameof(RecordedRoom));
|
|
|
|
|
this.StreamMonitor.FetchRoomInfoAsync();
|
2019-08-21 23:57:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void StreamMonitor_StreamStarted(object sender, StreamStartedArgs e)
|
2018-03-20 00:12:32 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
lock (this.StartupTaskLock)
|
|
|
|
|
if (!this.IsRecording && (this.StartupTask?.IsCompleted ?? true))
|
|
|
|
|
this.StartupTask = this._StartRecordAsync();
|
2018-03-13 14:23:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-15 21:55:01 +08:00
|
|
|
|
public void StartRecord()
|
2018-03-13 14:23:53 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.disposedValue) throw new ObjectDisposedException(nameof(RecordedRoom));
|
|
|
|
|
this.StreamMonitor.Check(TriggerType.Manual);
|
2018-03-13 13:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-23 06:57:22 +08:00
|
|
|
|
public void StopRecord()
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.disposedValue) throw new ObjectDisposedException(nameof(RecordedRoom));
|
2018-11-24 23:45:08 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this._retry = false;
|
2018-11-02 21:02:25 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.cancellationTokenSource != null)
|
2018-11-02 21:02:25 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.cancellationTokenSource.Cancel();
|
|
|
|
|
if (!(this.StreamDownloadTask?.Wait(TimeSpan.FromSeconds(2)) ?? true))
|
2018-11-02 21:02:25 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Warn, "停止录制超时,尝试强制关闭连接,请检查网络连接是否稳定");
|
2018-11-02 21:02:25 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this._stream?.Close();
|
|
|
|
|
this._stream?.Dispose();
|
|
|
|
|
this._response?.Dispose();
|
|
|
|
|
this.StreamDownloadTask?.Wait();
|
2018-11-02 21:02:25 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
2018-03-23 06:57:22 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Warn, "在尝试停止录制时发生错误,请检查网络连接是否稳定", ex);
|
2018-03-23 06:57:22 +08:00
|
|
|
|
}
|
2018-12-17 21:24:52 +08:00
|
|
|
|
finally
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this._retry = true;
|
2018-12-17 21:24:52 +08:00
|
|
|
|
}
|
2018-03-23 06:57:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 21:24:52 +08:00
|
|
|
|
private async Task _StartRecordAsync()
|
2018-03-15 21:55:01 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.IsRecording)
|
2018-03-15 21:55:01 +08:00
|
|
|
|
{
|
2020-12-19 17:27:45 +08:00
|
|
|
|
// TODO: 这里逻辑可能有问题,StartupTask 会变成当前这个已经结束的
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Warn, "已经在录制中了");
|
2018-03-21 20:56:56 +08:00
|
|
|
|
return;
|
2018-03-15 21:55:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.cancellationTokenSource = new CancellationTokenSource();
|
|
|
|
|
var token = this.cancellationTokenSource.Token;
|
2018-03-21 20:56:56 +08:00
|
|
|
|
try
|
2018-03-15 21:55:01 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
var flv_path = await this.BililiveAPI.GetPlayUrlAsync(this.RoomId);
|
2020-12-19 17:27:45 +08:00
|
|
|
|
if (string.IsNullOrWhiteSpace(flv_path))
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this._retry)
|
2020-12-19 17:27:45 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamMonitor.Check(TriggerType.HttpApiRecheck, (int)this.RoomConfig.TimingStreamRetry);
|
2020-12-19 17:27:45 +08:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-10-31 21:04:08 +08:00
|
|
|
|
|
|
|
|
|
unwrap_redir:
|
|
|
|
|
|
|
|
|
|
using (var client = new HttpClient(new HttpClientHandler
|
2018-12-11 18:03:06 +08:00
|
|
|
|
{
|
2019-10-31 21:04:08 +08:00
|
|
|
|
AllowAutoRedirect = false,
|
|
|
|
|
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
|
|
|
|
}))
|
|
|
|
|
{
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
client.Timeout = TimeSpan.FromMilliseconds(this.RoomConfig.TimingStreamConnect);
|
2018-12-11 18:03:06 +08:00
|
|
|
|
client.DefaultRequestHeaders.Accept.Clear();
|
|
|
|
|
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
|
|
|
|
|
client.DefaultRequestHeaders.UserAgent.Clear();
|
|
|
|
|
client.DefaultRequestHeaders.UserAgent.ParseAdd(Utils.UserAgent);
|
|
|
|
|
client.DefaultRequestHeaders.Referrer = new Uri("https://live.bilibili.com");
|
|
|
|
|
client.DefaultRequestHeaders.Add("Origin", "https://live.bilibili.com");
|
|
|
|
|
|
2019-10-31 21:04:08 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Info, "连接直播服务器 " + new Uri(flv_path).Host);
|
|
|
|
|
logger.Log(this.RoomId, LogLevel.Debug, "直播流地址: " + flv_path);
|
2018-12-11 18:03:06 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this._response = await client.GetAsync(flv_path, HttpCompletionOption.ResponseHeadersRead);
|
2018-12-11 18:03:06 +08:00
|
|
|
|
}
|
2018-03-18 18:55:28 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this._response.StatusCode == HttpStatusCode.Redirect || this._response.StatusCode == HttpStatusCode.Moved)
|
2019-10-31 21:04:08 +08:00
|
|
|
|
{
|
|
|
|
|
// workaround for missing Referrer
|
2021-01-01 14:46:27 +08:00
|
|
|
|
flv_path = this._response.Headers.Location.OriginalString;
|
|
|
|
|
this._response.Dispose();
|
2019-10-31 21:04:08 +08:00
|
|
|
|
goto unwrap_redir;
|
|
|
|
|
}
|
2021-01-01 14:46:27 +08:00
|
|
|
|
else if (this._response.StatusCode != HttpStatusCode.OK)
|
2018-03-18 18:55:28 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Info, string.Format("尝试下载直播流时服务器返回了 ({0}){1}", this._response.StatusCode, this._response.ReasonPhrase));
|
2018-12-11 18:03:06 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamMonitor.Check(TriggerType.HttpApiRecheck, (int)this.RoomConfig.TimingStreamRetry);
|
2018-12-18 00:16:24 +08:00
|
|
|
|
|
2018-12-11 18:03:06 +08:00
|
|
|
|
_CleanupFlvRequest();
|
2018-03-21 20:56:56 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Processor = this.newIFlvStreamProcessor().Initialize(this.GetStreamFilePath, this.GetClipFilePath, this.RoomConfig.EnabledFeature, this.RoomConfig.CuttingMode);
|
|
|
|
|
this.Processor.ClipLengthFuture = this.RoomConfig.ClipLengthFuture;
|
|
|
|
|
this.Processor.ClipLengthPast = this.RoomConfig.ClipLengthPast;
|
|
|
|
|
this.Processor.CuttingNumber = this.RoomConfig.CuttingNumber;
|
|
|
|
|
this.Processor.StreamFinalized += (sender, e) => { this.basicDanmakuWriter.Disable(); };
|
|
|
|
|
this.Processor.FileFinalized += (sender, size) =>
|
2020-12-20 20:56:40 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.recordEndData is null) return;
|
|
|
|
|
var data = this.recordEndData;
|
|
|
|
|
this.recordEndData = null;
|
2020-12-20 20:56:40 +08:00
|
|
|
|
|
|
|
|
|
data.EndRecordTime = DateTimeOffset.Now;
|
|
|
|
|
data.FileSize = size;
|
|
|
|
|
RecordEnded?.Invoke(this, data);
|
|
|
|
|
};
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Processor.OnMetaData += (sender, e) =>
|
2019-01-17 00:28:09 +08:00
|
|
|
|
{
|
|
|
|
|
e.Metadata["BililiveRecorder"] = new Dictionary<string, object>()
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
"starttime",
|
|
|
|
|
DateTime.UtcNow
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"version",
|
2019-11-24 09:38:31 +08:00
|
|
|
|
BuildInfo.Version + " " + BuildInfo.HeadShaShort
|
2019-01-17 00:28:09 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"roomid",
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.RoomId.ToString()
|
2019-01-17 00:28:09 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"streamername",
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamerName
|
2019-01-17 00:28:09 +08:00
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
};
|
2018-03-21 20:56:56 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this._stream = await this._response.Content.ReadAsStreamAsync();
|
2020-08-14 17:01:04 +08:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this._response.Headers.ConnectionClose == false || (this._response.Headers.ConnectionClose is null && this._response.Version != VERSION_1_0))
|
|
|
|
|
this._stream.ReadTimeout = 3 * 1000;
|
2020-08-14 17:01:04 +08:00
|
|
|
|
}
|
|
|
|
|
catch (InvalidOperationException) { }
|
2018-03-21 20:56:56 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamDownloadTask = Task.Run(_ReadStreamLoop);
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.IsRecording));
|
2018-03-18 18:55:28 +08:00
|
|
|
|
}
|
2018-03-21 20:56:56 +08:00
|
|
|
|
}
|
2018-12-11 18:03:06 +08:00
|
|
|
|
catch (TaskCanceledException)
|
|
|
|
|
{
|
|
|
|
|
// client.GetAsync timed out
|
|
|
|
|
// useless exception message :/
|
|
|
|
|
|
|
|
|
|
_CleanupFlvRequest();
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Warn, "连接直播服务器超时。");
|
|
|
|
|
this.StreamMonitor.Check(TriggerType.HttpApiRecheck, (int)this.RoomConfig.TimingStreamRetry);
|
2018-12-11 18:03:06 +08:00
|
|
|
|
}
|
2018-03-21 20:56:56 +08:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_CleanupFlvRequest();
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Error, "启动直播流下载出错。" + (this._retry ? "将重试启动。" : ""), ex);
|
|
|
|
|
if (this._retry)
|
2018-10-24 14:33:05 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamMonitor.Check(TriggerType.HttpApiRecheck, (int)this.RoomConfig.TimingStreamRetry);
|
2018-10-24 14:33:05 +08:00
|
|
|
|
}
|
2018-03-21 20:56:56 +08:00
|
|
|
|
}
|
2018-12-11 18:03:06 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
async Task _ReadStreamLoop()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
const int BUF_SIZE = 1024 * 8;
|
|
|
|
|
byte[] buffer = new byte[BUF_SIZE];
|
|
|
|
|
while (!token.IsCancellationRequested)
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
int bytesRead = await this._stream.ReadAsync(buffer, 0, BUF_SIZE, token);
|
2018-12-11 18:03:06 +08:00
|
|
|
|
_UpdateDownloadSpeed(bytesRead);
|
|
|
|
|
if (bytesRead != 0)
|
|
|
|
|
{
|
|
|
|
|
if (bytesRead != BUF_SIZE)
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Processor.AddBytes(buffer.Take(bytesRead).ToArray());
|
2018-12-11 18:03:06 +08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Processor.AddBytes(buffer);
|
2018-12-11 18:03:06 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-17 21:24:52 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Info,
|
2019-11-24 08:54:33 +08:00
|
|
|
|
(token.IsCancellationRequested ? "本地操作结束当前录制。" : "服务器关闭直播流,可能是直播已结束。")
|
2021-01-01 14:46:27 +08:00
|
|
|
|
+ (this._retry ? "将重试启动。" : ""));
|
|
|
|
|
if (this._retry)
|
2018-12-17 21:24:52 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.StreamMonitor.Check(TriggerType.HttpApiRecheck, (int)this.RoomConfig.TimingStreamRetry);
|
2018-12-17 21:24:52 +08:00
|
|
|
|
}
|
2018-12-11 18:03:06 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
2018-12-17 21:24:52 +08:00
|
|
|
|
if (e is ObjectDisposedException && token.IsCancellationRequested) { return; }
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Warn, "录播发生错误", e);
|
2018-12-11 18:03:06 +08:00
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_CleanupFlvRequest();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-31 06:22:38 +08:00
|
|
|
|
void _CleanupFlvRequest()
|
2018-03-21 20:56:56 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.Processor != null)
|
2018-10-31 06:22:38 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Processor.FinallizeFile();
|
|
|
|
|
this.Processor.Dispose();
|
|
|
|
|
this.Processor = null;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
}
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this._stream?.Dispose();
|
|
|
|
|
this._stream = null;
|
|
|
|
|
this._response?.Dispose();
|
|
|
|
|
this._response = null;
|
|
|
|
|
|
|
|
|
|
this._lastUpdateTimestamp = 0;
|
|
|
|
|
this.DownloadSpeedMegaBitps = 0d;
|
|
|
|
|
this.DownloadSpeedPersentage = 0d;
|
|
|
|
|
this.TriggerPropertyChanged(nameof(this.IsRecording));
|
2018-03-15 21:55:01 +08:00
|
|
|
|
}
|
2018-10-31 06:22:38 +08:00
|
|
|
|
void _UpdateDownloadSpeed(int bytesRead)
|
2018-03-15 21:55:01 +08:00
|
|
|
|
{
|
2018-10-31 06:22:38 +08:00
|
|
|
|
DateTime now = DateTime.Now;
|
2021-01-01 14:46:27 +08:00
|
|
|
|
double passedSeconds = (now - this.LastUpdateDateTime).TotalSeconds;
|
|
|
|
|
this._lastUpdateSize += bytesRead;
|
2018-11-03 07:45:56 +08:00
|
|
|
|
if (passedSeconds > 1.5)
|
2018-10-31 06:22:38 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.DownloadSpeedMegaBitps = this._lastUpdateSize / passedSeconds * 8d / 1_000_000d; // mega bit per second
|
|
|
|
|
this.DownloadSpeedPersentage = (this.DownloadSpeedPersentage / 2) + ((this.Processor.TotalMaxTimestamp - this._lastUpdateTimestamp) / passedSeconds / 1000 / 2); // ((RecordedTime/1000) / RealTime)%
|
|
|
|
|
this._lastUpdateTimestamp = this.Processor.TotalMaxTimestamp;
|
|
|
|
|
this._lastUpdateSize = 0;
|
|
|
|
|
this.LastUpdateDateTime = now;
|
2018-10-31 06:22:38 +08:00
|
|
|
|
}
|
2018-03-15 21:55:01 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-13 13:21:01 +08:00
|
|
|
|
// Called by API or GUI
|
2021-01-01 14:46:27 +08:00
|
|
|
|
public void Clip() => this.Processor?.Clip();
|
2018-03-13 13:21:01 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
public void Shutdown() => this.Dispose(true);
|
2018-10-30 20:33:44 +08:00
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
private (string fullPath, string relativePath) GetStreamFilePath()
|
2020-11-23 17:35:42 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
var path = this.FormatFilename(this.RoomConfig.RecordFilenameFormat);
|
2020-11-23 17:35:42 +08:00
|
|
|
|
|
|
|
|
|
// 有点脏的写法,不过凑合吧
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (this.RoomConfig.RecordDanmaku)
|
2020-11-23 17:35:42 +08:00
|
|
|
|
{
|
2020-12-20 20:56:40 +08:00
|
|
|
|
var xmlpath = Path.ChangeExtension(path.fullPath, "xml");
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.basicDanmakuWriter.EnableWithPath(xmlpath, this);
|
2020-11-23 17:35:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.recordEndData = new RecordEndData
|
2020-12-20 20:56:40 +08:00
|
|
|
|
{
|
|
|
|
|
RoomId = RoomId,
|
|
|
|
|
Title = Title,
|
|
|
|
|
Name = StreamerName,
|
|
|
|
|
StartRecordTime = DateTimeOffset.Now,
|
|
|
|
|
RelativePath = path.relativePath,
|
|
|
|
|
};
|
|
|
|
|
|
2020-11-23 17:35:42 +08:00
|
|
|
|
return path;
|
|
|
|
|
}
|
2018-11-02 00:14:18 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
private string GetClipFilePath() => this.FormatFilename(this.RoomConfig.ClipFilenameFormat).fullPath;
|
2020-04-24 22:04:34 +08:00
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
private (string fullPath, string relativePath) FormatFilename(string formatString)
|
2020-04-24 22:04:34 +08:00
|
|
|
|
{
|
2021-01-08 18:54:50 +08:00
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
var date = now.ToString("yyyyMMdd");
|
|
|
|
|
var time = now.ToString("HHmmss");
|
|
|
|
|
var randomStr = random.Next(100, 999).ToString();
|
2020-04-25 01:27:39 +08:00
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
var relativePath = formatString
|
2020-04-25 01:27:39 +08:00
|
|
|
|
.Replace(@"{date}", date)
|
2020-04-24 22:04:34 +08:00
|
|
|
|
.Replace(@"{time}", time)
|
|
|
|
|
.Replace(@"{random}", randomStr)
|
2021-01-01 14:46:27 +08:00
|
|
|
|
.Replace(@"{roomid}", this.RoomId.ToString())
|
|
|
|
|
.Replace(@"{title}", this.Title.RemoveInvalidFileName())
|
2021-01-08 18:54:50 +08:00
|
|
|
|
.Replace(@"{name}", this.StreamerName.RemoveInvalidFileName())
|
|
|
|
|
.Replace(@"{parea}", this.ParentAreaName.RemoveInvalidFileName())
|
|
|
|
|
.Replace(@"{area}", this.AreaName.RemoveInvalidFileName())
|
|
|
|
|
;
|
2020-04-25 01:27:39 +08:00
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
if (!relativePath.EndsWith(".flv", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
relativePath += ".flv";
|
2020-04-25 01:27:39 +08:00
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
relativePath = relativePath.RemoveInvalidFileName(ignore_slash: true);
|
2021-01-01 14:46:27 +08:00
|
|
|
|
var workDirectory = this.RoomConfig.WorkDirectory;
|
|
|
|
|
var fullPath = Path.Combine(workDirectory, relativePath);
|
2020-12-20 20:56:40 +08:00
|
|
|
|
fullPath = Path.GetFullPath(fullPath);
|
2020-04-25 01:27:39 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (!CheckPath(workDirectory, Path.GetDirectoryName(fullPath)))
|
2020-04-25 01:27:39 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Warn, "录制文件位置超出允许范围,请检查设置。将写入到默认路径。");
|
|
|
|
|
relativePath = Path.Combine(this.RoomId.ToString(), $"{this.RoomId}-{date}-{time}-{randomStr}.flv");
|
|
|
|
|
fullPath = Path.Combine(workDirectory, relativePath);
|
2020-04-25 01:27:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
if (new FileInfo(relativePath).Exists)
|
2020-04-25 01:27:39 +08:00
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
logger.Log(this.RoomId, LogLevel.Warn, "录制文件名冲突,请检查设置。将写入到默认路径。");
|
|
|
|
|
relativePath = Path.Combine(this.RoomId.ToString(), $"{this.RoomId}-{date}-{time}-{randomStr}.flv");
|
|
|
|
|
fullPath = Path.Combine(workDirectory, relativePath);
|
2020-04-25 01:27:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-20 20:56:40 +08:00
|
|
|
|
return (fullPath, relativePath);
|
2020-04-25 01:27:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool CheckPath(string parent, string child)
|
|
|
|
|
{
|
|
|
|
|
DirectoryInfo di_p = new DirectoryInfo(parent);
|
|
|
|
|
DirectoryInfo di_c = new DirectoryInfo(child);
|
|
|
|
|
|
2020-05-01 08:37:56 +08:00
|
|
|
|
if (di_c.FullName == di_p.FullName)
|
|
|
|
|
return true;
|
|
|
|
|
|
2020-04-25 01:27:39 +08:00
|
|
|
|
bool isParent = false;
|
|
|
|
|
while (di_c.Parent != null)
|
|
|
|
|
{
|
|
|
|
|
if (di_c.Parent.FullName == di_p.FullName)
|
|
|
|
|
{
|
|
|
|
|
isParent = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
di_c = di_c.Parent;
|
|
|
|
|
}
|
|
|
|
|
return isParent;
|
2020-04-24 22:04:34 +08:00
|
|
|
|
}
|
2018-11-02 00:14:18 +08:00
|
|
|
|
|
2018-03-12 18:57:20 +08:00
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
2018-03-24 09:48:06 +08:00
|
|
|
|
protected void TriggerPropertyChanged(string propertyName)
|
|
|
|
|
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
2018-11-24 23:45:08 +08:00
|
|
|
|
|
|
|
|
|
#region IDisposable Support
|
|
|
|
|
private bool disposedValue = false; // 要检测冗余调用
|
|
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
if (!this.disposedValue)
|
2018-11-24 23:45:08 +08:00
|
|
|
|
{
|
|
|
|
|
if (disposing)
|
|
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Stop();
|
|
|
|
|
this.StopRecord();
|
|
|
|
|
this.Processor?.FinallizeFile();
|
|
|
|
|
this.Processor?.Dispose();
|
|
|
|
|
this.StreamMonitor?.Dispose();
|
|
|
|
|
this._response?.Dispose();
|
|
|
|
|
this._stream?.Dispose();
|
|
|
|
|
this.cancellationTokenSource?.Dispose();
|
|
|
|
|
this.basicDanmakuWriter?.Dispose();
|
2018-11-24 23:45:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Processor = null;
|
|
|
|
|
this._response = null;
|
|
|
|
|
this._stream = null;
|
|
|
|
|
this.cancellationTokenSource = null;
|
2018-11-24 23:45:08 +08:00
|
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.disposedValue = true;
|
2018-11-24 23:45:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
// 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
|
2021-01-01 14:46:27 +08:00
|
|
|
|
this.Dispose(true);
|
2018-11-24 23:45:08 +08:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
2018-03-12 18:57:20 +08:00
|
|
|
|
}
|
|
|
|
|
}
|