mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-16 03:32:20 +08:00
全部使用 interface (FlvProcessor)
This commit is contained in:
parent
028e574733
commit
4e0791c35a
|
@ -10,6 +10,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.8.1" />
|
||||
<PackageReference Include="NLog" Version="4.5.0-rc07" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ namespace BililiveRecorder.Core
|
|||
public bool IsMonitoring => StreamMonitor.receiver.IsConnected;
|
||||
public bool IsRecording => flvStream != null;
|
||||
|
||||
public FlvStreamProcessor Processor; // FlvProcessor
|
||||
public ObservableCollection<FlvClipProcessor> Clips { get; private set; } = new ObservableCollection<FlvClipProcessor>();
|
||||
public IFlvStreamProcessor Processor; // FlvProcessor
|
||||
public ObservableCollection<IFlvClipProcessor> Clips { get; private set; } = new ObservableCollection<IFlvClipProcessor>();
|
||||
|
||||
internal StreamMonitor StreamMonitor { get; }
|
||||
private Settings _settings { get; }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.8.1" />
|
||||
<PackageReference Include="NLog" Version="4.5.0-rc07" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,26 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public delegate void TagProcessedEvent(object sender, TagProcessedArgs e);
|
||||
public class TagProcessedArgs
|
||||
{
|
||||
public FlvTag Tag;
|
||||
public IFlvTag Tag;
|
||||
}
|
||||
|
||||
|
||||
public delegate void ClipFinalizedEvent(object sender, ClipFinalizedArgs e);
|
||||
public class ClipFinalizedArgs
|
||||
{
|
||||
public FlvClipProcessor ClipProcessor;
|
||||
public IFlvClipProcessor ClipProcessor;
|
||||
}
|
||||
|
||||
public delegate void StreamFinalizedEvent(object sender, StreamFinalizedArgs e);
|
||||
public class StreamFinalizedArgs
|
||||
{
|
||||
public FlvStreamProcessor StreamProcessor;
|
||||
public IFlvStreamProcessor StreamProcessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,22 +2,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public class FlvClipProcessor
|
||||
public class FlvClipProcessor : IFlvClipProcessor
|
||||
{
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public readonly FlvMetadata Header;
|
||||
public readonly List<FlvTag> HTags;
|
||||
public readonly List<FlvTag> Tags;
|
||||
private int target = -1;
|
||||
public IFlvMetadata Header { get; }
|
||||
public List<IFlvTag> HTags { get; }
|
||||
public List<IFlvTag> Tags { get; }
|
||||
private readonly int target = -1;
|
||||
|
||||
public Func<string> GetFileName;
|
||||
public Func<string> GetFileName { get; set; }
|
||||
|
||||
public FlvClipProcessor(FlvMetadata header, List<FlvTag> head, List<FlvTag> past, uint future)
|
||||
public FlvClipProcessor(IFlvMetadata header, List<IFlvTag> head, List<IFlvTag> past, uint future)
|
||||
{
|
||||
Header = header;
|
||||
HTags = head;
|
||||
|
@ -27,7 +26,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
Tags.Count, Tags[0].TimeStamp, Tags[Tags.Count - 1].TimeStamp, (Tags[Tags.Count - 1].TimeStamp - Tags[0].TimeStamp) / 1000d);
|
||||
}
|
||||
|
||||
public void AddTag(FlvTag tag)
|
||||
public void AddTag(IFlvTag tag)
|
||||
{
|
||||
Tags.Add(tag);
|
||||
if (tag.TimeStamp >= target)
|
||||
|
|
|
@ -6,23 +6,29 @@ using System.Text;
|
|||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public class FlvMetadata
|
||||
public class FlvMetadata : IFlvMetadata
|
||||
{
|
||||
public IDictionary<string, object> Meta = new Dictionary<string, object>();
|
||||
public IDictionary<string, object> Meta { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
public static FlvMetadata Parse(byte[] data)
|
||||
public FlvMetadata()
|
||||
{
|
||||
var m = new FlvMetadata
|
||||
Meta["duration"] = 0.0;
|
||||
Meta["lasttimestamp"] = 0.0;
|
||||
}
|
||||
|
||||
public FlvMetadata(byte[] data)
|
||||
{
|
||||
Meta = _Decode(data)
|
||||
};
|
||||
Meta = _Decode(data);
|
||||
|
||||
if (!m.Meta.ContainsKey("duration"))
|
||||
m.Meta["duration"] = 0.0;
|
||||
if (!m.Meta.ContainsKey("lasttimestamp"))
|
||||
m.Meta["lasttimestamp"] = 0.0;
|
||||
if (!Meta.ContainsKey("duration"))
|
||||
{
|
||||
Meta["duration"] = 0.0;
|
||||
}
|
||||
|
||||
return m;
|
||||
if (!Meta.ContainsKey("lasttimestamp"))
|
||||
{
|
||||
Meta["lasttimestamp"] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ToBytes()
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
// TODO: 重构 Tag 解析流程
|
||||
// TODO: 添加测试
|
||||
// 注:下载时会按照 4 11 N bytes 下载
|
||||
public class FlvStreamProcessor : IDisposable
|
||||
public class FlvStreamProcessor : IFlvStreamProcessor
|
||||
{
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
@ -35,22 +32,22 @@ namespace BililiveRecorder.FlvProcessor
|
|||
// 0x00, // the "0th" tag has a length of 0
|
||||
};
|
||||
|
||||
public FlvMetadata Metadata = null;
|
||||
public IFlvMetadata Metadata { get; set; } = null;
|
||||
public event TagProcessedEvent TagProcessed;
|
||||
public event StreamFinalizedEvent StreamFinalized;
|
||||
public Func<string> GetFileName;
|
||||
public Func<string> GetFileName { get; set; }
|
||||
|
||||
public uint Clip_Past = 90;
|
||||
public uint Clip_Future = 30;
|
||||
public uint Clip_Past { get; set; } = 90;
|
||||
public uint Clip_Future { get; set; } = 30;
|
||||
private readonly bool _noClip = false;
|
||||
|
||||
private bool _headerParsed = false;
|
||||
private readonly List<FlvTag> HTags = new List<FlvTag>();
|
||||
private readonly List<FlvTag> Tags = new List<FlvTag>();
|
||||
private readonly List<IFlvTag> HTags = new List<IFlvTag>();
|
||||
private readonly List<IFlvTag> Tags = new List<IFlvTag>();
|
||||
private readonly MemoryStream _buffer = new MemoryStream();
|
||||
private readonly MemoryStream _data = new MemoryStream();
|
||||
private FlvTag currentTag = null;
|
||||
private object _writelock = new object();
|
||||
private IFlvTag currentTag = null;
|
||||
private readonly object _writelock = new object();
|
||||
private bool Finallized = false;
|
||||
|
||||
private readonly FileStream _fs;
|
||||
|
@ -85,8 +82,10 @@ namespace BililiveRecorder.FlvProcessor
|
|||
public void AddBytes(byte[] data)
|
||||
{
|
||||
lock (_writelock)
|
||||
{
|
||||
_AddBytes(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void _AddBytes(byte[] data)
|
||||
{
|
||||
|
@ -104,10 +103,15 @@ namespace BililiveRecorder.FlvProcessor
|
|||
}
|
||||
var r = new bool[FLV_HEADER_BYTES.Length];
|
||||
for (int i = 0; i < FLV_HEADER_BYTES.Length; i++)
|
||||
{
|
||||
r[i] = data[i] == FLV_HEADER_BYTES[i];
|
||||
}
|
||||
|
||||
bool succ = r.All(x => x);
|
||||
if (!succ)
|
||||
{
|
||||
throw new NotSupportedException("Not FLV Stream or Not Supported"); // TODO: custom Exception.
|
||||
}
|
||||
|
||||
_headerParsed = true;
|
||||
_AddBytes(data.Skip(FLV_HEADER_BYTES.Length).ToArray());
|
||||
|
@ -140,7 +144,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
}
|
||||
}
|
||||
|
||||
private void _TagCreated(FlvTag tag)
|
||||
private void _TagCreated(IFlvTag tag)
|
||||
{
|
||||
if (Metadata == null)
|
||||
{
|
||||
|
@ -149,7 +153,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
_fs?.Write(FLV_HEADER_BYTES, 0, FLV_HEADER_BYTES.Length);
|
||||
_fs?.Write(new byte[] { 0, 0, 0, 0, }, 0, 4);
|
||||
|
||||
Metadata = FlvMetadata.Parse(tag.Data);
|
||||
Metadata = new FlvMetadata(tag.Data);
|
||||
|
||||
// TODO: 添加录播姬标记、录制信息
|
||||
|
||||
|
@ -202,7 +206,10 @@ namespace BililiveRecorder.FlvProcessor
|
|||
{
|
||||
tag.TimeStamp -= BaseTimeStamp; // 修复时间戳
|
||||
if (tag.TimeStamp < 0)
|
||||
{
|
||||
tag.TimeStamp = 0;
|
||||
}
|
||||
|
||||
MaxTimeStamp = Math.Max(MaxTimeStamp, tag.TimeStamp);
|
||||
}
|
||||
else
|
||||
|
@ -221,7 +228,9 @@ namespace BililiveRecorder.FlvProcessor
|
|||
LasttimeRemovedTimestamp = MaxTimeStamp;
|
||||
int max_remove_index = Tags.FindLastIndex(x => x.IsVideoKeyframe && ((MaxTimeStamp - x.TimeStamp) > (Clip_Past * SEC_TO_MS)));
|
||||
if (max_remove_index > 0)
|
||||
{
|
||||
Tags.RemoveRange(0, max_remove_index);
|
||||
}
|
||||
// Tags.RemoveRange(0, max_remove_index + 1 - 1);
|
||||
// 给将来的备注:这里是故意 + 1 - 1 的,因为要保留选中的那个关键帧, + 1 就把关键帧删除了
|
||||
}
|
||||
|
@ -242,7 +251,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
_buffer.Write(data, 0, data.Length);
|
||||
long dataLen = _buffer.Position;
|
||||
_buffer.Position = 0;
|
||||
FlvTag tag = new FlvTag();
|
||||
IFlvTag tag = new FlvTag();
|
||||
|
||||
// Previous Tag Size
|
||||
_buffer.Read(b, 0, 4);
|
||||
|
@ -272,7 +281,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
_AddBytes(rest);
|
||||
}
|
||||
|
||||
public FlvClipProcessor Clip()
|
||||
public IFlvClipProcessor Clip()
|
||||
{
|
||||
// 如果禁用 clip 功能 或者 已经结束处理了
|
||||
if (_noClip || Finallized)
|
||||
|
@ -284,7 +293,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
lock (_writelock)
|
||||
{
|
||||
logger.Info("剪辑处理中,将会保存过去 {0} 秒和将来 {1} 秒的直播流", (Tags[Tags.Count - 1].TimeStamp - Tags[0].TimeStamp) / 1000d, Clip_Future);
|
||||
return new FlvClipProcessor(Metadata, HTags, new List<FlvTag>(Tags.ToArray()), Clip_Future);
|
||||
return new FlvClipProcessor(Metadata, HTags, new List<IFlvTag>(Tags.ToArray()), Clip_Future);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,6 +301,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
public void FinallizeFile()
|
||||
{
|
||||
if (!Finallized)
|
||||
{
|
||||
lock (_writelock)
|
||||
{
|
||||
try
|
||||
|
@ -323,6 +333,7 @@ namespace BililiveRecorder.FlvProcessor
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public class FlvTag
|
||||
public class FlvTag : IFlvTag
|
||||
{
|
||||
public TagType TagType = 0;
|
||||
public int TagSize = 0;
|
||||
public int TimeStamp = 0;
|
||||
public byte[] StreamId = new byte[3];
|
||||
public TagType TagType { get; set; } = 0;
|
||||
public int TagSize { get; set; } = 0;
|
||||
public int TimeStamp { get; set; } = 0;
|
||||
public byte[] StreamId { get; set; } = new byte[3];
|
||||
|
||||
public bool IsVideoKeyframe => _IsVideoKeyframe != -1 ? _IsVideoKeyframe == 1 : 1 == (_IsVideoKeyframe = _ParseIsVideoKeyframe());
|
||||
private int _IsVideoKeyframe = -1;
|
||||
|
||||
public byte[] Data = null;
|
||||
public byte[] Data { get; set; } = null;
|
||||
|
||||
public byte[] ToBytes(bool useDataSize, int offset = 0)
|
||||
{
|
||||
|
@ -38,9 +35,14 @@ namespace BililiveRecorder.FlvProcessor
|
|||
private int _ParseIsVideoKeyframe()
|
||||
{
|
||||
if (TagType != TagType.VIDEO)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Data.Length < 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
const byte mask = 0b00001111;
|
||||
const byte compare = 0b00011111;
|
||||
|
|
18
BililiveRecorder.FlvProcessor/IFlvClipProcessor.cs
Normal file
18
BililiveRecorder.FlvProcessor/IFlvClipProcessor.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public interface IFlvClipProcessor
|
||||
{
|
||||
IFlvMetadata Header { get; }
|
||||
List<IFlvTag> HTags { get; }
|
||||
List<IFlvTag> Tags { get; }
|
||||
Func<string> GetFileName { get; set; }
|
||||
|
||||
void AddTag(IFlvTag tag);
|
||||
void FinallizeFile();
|
||||
event ClipFinalizedEvent ClipFinalized;
|
||||
|
||||
}
|
||||
}
|
10
BililiveRecorder.FlvProcessor/IFlvMetadata.cs
Normal file
10
BililiveRecorder.FlvProcessor/IFlvMetadata.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public interface IFlvMetadata
|
||||
{
|
||||
IDictionary<string, object> Meta { get; set; }
|
||||
byte[] ToBytes();
|
||||
}
|
||||
}
|
24
BililiveRecorder.FlvProcessor/IFlvStreamProcessor.cs
Normal file
24
BililiveRecorder.FlvProcessor/IFlvStreamProcessor.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public interface IFlvStreamProcessor : IDisposable
|
||||
{
|
||||
event TagProcessedEvent TagProcessed;
|
||||
event StreamFinalizedEvent StreamFinalized;
|
||||
|
||||
IFlvMetadata Metadata { get; set; }
|
||||
Func<string> GetFileName { get; set; }
|
||||
uint Clip_Past { get; set; }
|
||||
uint Clip_Future { get; set; }
|
||||
int LasttimeRemovedTimestamp { get; }
|
||||
int MaxTimeStamp { get; }
|
||||
int BaseTimeStamp { get; }
|
||||
int TagVideoCount { get; }
|
||||
int TagAudioCount { get; }
|
||||
|
||||
void AddBytes(byte[] data);
|
||||
IFlvClipProcessor Clip();
|
||||
void FinallizeFile();
|
||||
}
|
||||
}
|
17
BililiveRecorder.FlvProcessor/IFlvTag.cs
Normal file
17
BililiveRecorder.FlvProcessor/IFlvTag.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System.IO;
|
||||
|
||||
namespace BililiveRecorder.FlvProcessor
|
||||
{
|
||||
public interface IFlvTag
|
||||
{
|
||||
TagType TagType { get; set; }
|
||||
int TagSize { get; set; }
|
||||
int TimeStamp { get; set; }
|
||||
byte[] StreamId { get; set; }
|
||||
bool IsVideoKeyframe { get; }
|
||||
byte[] Data { get; set; }
|
||||
|
||||
byte[] ToBytes(bool useDataSize, int offset = 0);
|
||||
void WriteTo(Stream stream, int offset = 0);
|
||||
}
|
||||
}
|
|
@ -75,6 +75,9 @@
|
|||
<SignAssembly>false</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Autofac, Version=4.8.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Autofac.4.8.1\lib\net45\Autofac.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\WindowsAPICodePack-Core.1.1.2\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
|
||||
</Reference>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Autofac" version="4.8.1" targetFramework="net462" />
|
||||
<package id="NLog" version="4.5.0-rc07" targetFramework="net462" />
|
||||
<package id="NLog.Config" version="4.5.0-rc07" targetFramework="net462" />
|
||||
<package id="NLog.Schema" version="4.5.0-rc07" targetFramework="net462" />
|
||||
|
|
Loading…
Reference in New Issue
Block a user