2018-03-24 04:58:13 +08:00
|
|
|
using System;
|
2018-03-12 18:57:20 +08:00
|
|
|
using System.Collections.Generic;
|
2018-03-19 16:51:35 +08:00
|
|
|
using System.IO;
|
2021-01-01 14:46:27 +08:00
|
|
|
using NLog;
|
2018-03-12 18:57:20 +08:00
|
|
|
|
|
|
|
namespace BililiveRecorder.FlvProcessor
|
|
|
|
{
|
2018-10-24 13:33:43 +08:00
|
|
|
public class FlvClipProcessor : IFlvClipProcessor
|
2018-03-12 18:57:20 +08:00
|
|
|
{
|
2018-03-24 04:58:13 +08:00
|
|
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
|
|
|
|
2019-01-16 23:05:54 +08:00
|
|
|
private readonly Func<IFlvTag> funcFlvTag;
|
|
|
|
|
2018-10-25 19:20:23 +08:00
|
|
|
public IFlvMetadata Header { get; private set; }
|
|
|
|
public List<IFlvTag> HTags { get; private set; }
|
|
|
|
public List<IFlvTag> Tags { get; private set; }
|
|
|
|
private int target = -1;
|
2018-10-31 06:22:38 +08:00
|
|
|
private string path;
|
2018-03-20 00:12:32 +08:00
|
|
|
|
2019-01-16 23:05:54 +08:00
|
|
|
public FlvClipProcessor(Func<IFlvTag> funcFlvTag)
|
2018-03-12 18:57:20 +08:00
|
|
|
{
|
2019-01-16 23:05:54 +08:00
|
|
|
this.funcFlvTag = funcFlvTag;
|
2018-10-25 19:20:23 +08:00
|
|
|
}
|
|
|
|
|
2018-10-31 06:22:38 +08:00
|
|
|
public IFlvClipProcessor Initialize(string path, IFlvMetadata metadata, List<IFlvTag> head, List<IFlvTag> data, uint seconds)
|
2018-10-25 19:20:23 +08:00
|
|
|
{
|
2018-10-31 06:22:38 +08:00
|
|
|
this.path = path;
|
2021-01-01 14:46:27 +08:00
|
|
|
this.Header = metadata; // TODO: Copy a copy, do not share
|
|
|
|
this.HTags = head;
|
|
|
|
this.Tags = data;
|
|
|
|
this.target = this.Tags[this.Tags.Count - 1].TimeStamp + (int)(seconds * FlvStreamProcessor.SEC_TO_MS);
|
2018-03-27 06:10:30 +08:00
|
|
|
logger.Debug("Clip 创建 Tags.Count={0} Tags[0].TimeStamp={1} Tags[Tags.Count-1].TimeStamp={2} Tags里秒数={3}",
|
2021-01-01 14:46:27 +08:00
|
|
|
this.Tags.Count, this.Tags[0].TimeStamp, this.Tags[this.Tags.Count - 1].TimeStamp, (this.Tags[this.Tags.Count - 1].TimeStamp - this.Tags[0].TimeStamp) / 1000d);
|
2018-10-25 19:20:23 +08:00
|
|
|
|
|
|
|
return this;
|
2018-03-12 18:57:20 +08:00
|
|
|
}
|
|
|
|
|
2018-10-24 13:33:43 +08:00
|
|
|
public void AddTag(IFlvTag tag)
|
2018-03-13 13:21:01 +08:00
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
this.Tags.Add(tag);
|
|
|
|
if (tag.TimeStamp >= this.target)
|
2018-03-19 16:51:35 +08:00
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
this.FinallizeFile();
|
2018-03-19 16:51:35 +08:00
|
|
|
}
|
2018-03-13 13:21:01 +08:00
|
|
|
}
|
|
|
|
|
2018-03-19 16:51:35 +08:00
|
|
|
public void FinallizeFile()
|
2018-03-26 02:56:56 +08:00
|
|
|
{
|
|
|
|
try
|
2018-03-19 16:51:35 +08:00
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
if (!Directory.Exists(Path.GetDirectoryName(this.path)))
|
2018-11-02 00:14:18 +08:00
|
|
|
{
|
2021-01-01 14:46:27 +08:00
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(this.path));
|
2018-11-02 00:14:18 +08:00
|
|
|
}
|
2021-01-01 14:46:27 +08:00
|
|
|
using (var fs = new FileStream(this.path, FileMode.CreateNew, FileAccess.ReadWrite))
|
2018-03-26 02:57:17 +08:00
|
|
|
{
|
|
|
|
fs.Write(FlvStreamProcessor.FLV_HEADER_BYTES, 0, FlvStreamProcessor.FLV_HEADER_BYTES.Length);
|
|
|
|
fs.Write(new byte[] { 0, 0, 0, 0, }, 0, 4);
|
2018-03-19 16:51:35 +08:00
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
double clipDuration = (this.Tags[this.Tags.Count - 1].TimeStamp - this.Tags[0].TimeStamp) / 1000d;
|
|
|
|
this.Header["duration"] = clipDuration;
|
|
|
|
this.Header["lasttimestamp"] = (double)(this.Tags[this.Tags.Count - 1].TimeStamp - this.Tags[0].TimeStamp);
|
2018-03-19 16:51:35 +08:00
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
var t = this.funcFlvTag();
|
2019-01-16 23:05:54 +08:00
|
|
|
t.TagType = TagType.DATA;
|
2019-01-17 22:29:49 +08:00
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
if (this.Header.ContainsKey("BililiveRecorder"))
|
2019-01-17 22:29:49 +08:00
|
|
|
{
|
|
|
|
// TODO: 更好的写法
|
2021-01-01 14:46:27 +08:00
|
|
|
(this.Header["BililiveRecorder"] as Dictionary<string, object>)["starttime"] = DateTime.UtcNow - TimeSpan.FromSeconds(clipDuration);
|
2019-01-17 22:29:49 +08:00
|
|
|
}
|
2021-01-01 14:46:27 +08:00
|
|
|
t.Data = this.Header.ToBytes();
|
2018-03-26 02:57:17 +08:00
|
|
|
t.WriteTo(fs);
|
2018-03-19 16:51:35 +08:00
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
int offset = this.Tags[0].TimeStamp;
|
2018-03-29 12:25:45 +08:00
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
this.HTags.ForEach(tag => tag.WriteTo(fs));
|
|
|
|
this.Tags.ForEach(tag => tag.WriteTo(fs, offset));
|
2018-03-26 02:57:17 +08:00
|
|
|
|
2021-01-01 14:46:27 +08:00
|
|
|
logger.Info("剪辑已保存:{0}", Path.GetFileName(this.path));
|
2018-03-27 06:10:30 +08:00
|
|
|
|
2018-03-26 02:57:17 +08:00
|
|
|
fs.Close();
|
|
|
|
}
|
2021-01-01 14:46:27 +08:00
|
|
|
this.Tags.Clear();
|
2018-03-26 02:56:56 +08:00
|
|
|
}
|
2020-12-19 17:27:45 +08:00
|
|
|
catch (IOException ex)
|
|
|
|
{
|
|
|
|
logger.Warn(ex, "保存剪辑文件时出错");
|
|
|
|
}
|
2018-03-26 02:56:56 +08:00
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
logger.Error(ex, "保存剪辑文件时出错");
|
|
|
|
}
|
2018-11-02 00:14:18 +08:00
|
|
|
ClipFinalized?.Invoke(this, new ClipFinalizedArgs() { ClipProcessor = this });
|
2018-03-19 16:51:35 +08:00
|
|
|
}
|
2018-03-13 13:21:01 +08:00
|
|
|
|
|
|
|
public event ClipFinalizedEvent ClipFinalized;
|
2018-03-12 18:57:20 +08:00
|
|
|
}
|
|
|
|
}
|