using NLog; using System; using System.Collections.Generic; using System.IO; namespace BililiveRecorder.FlvProcessor { public class FlvClipProcessor : IFlvClipProcessor { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private readonly Func funcFlvTag; public IFlvMetadata Header { get; private set; } public List HTags { get; private set; } public List Tags { get; private set; } private int target = -1; private string path; public FlvClipProcessor(Func funcFlvTag) { this.funcFlvTag = funcFlvTag; } public IFlvClipProcessor Initialize(string path, IFlvMetadata metadata, List head, List data, uint seconds) { this.path = path; Header = metadata; // TODO: Copy a copy, do not share HTags = head; Tags = data; target = Tags[Tags.Count - 1].TimeStamp + (int)(seconds * FlvStreamProcessor.SEC_TO_MS); logger.Debug("Clip 创建 Tags.Count={0} Tags[0].TimeStamp={1} Tags[Tags.Count-1].TimeStamp={2} Tags里秒数={3}", Tags.Count, Tags[0].TimeStamp, Tags[Tags.Count - 1].TimeStamp, (Tags[Tags.Count - 1].TimeStamp - Tags[0].TimeStamp) / 1000d); return this; } public void AddTag(IFlvTag tag) { Tags.Add(tag); if (tag.TimeStamp >= target) { FinallizeFile(); } } public void FinallizeFile() { try { if (!Directory.Exists(Path.GetDirectoryName(path))) { Directory.CreateDirectory(Path.GetDirectoryName(path)); } using (var fs = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite)) { fs.Write(FlvStreamProcessor.FLV_HEADER_BYTES, 0, FlvStreamProcessor.FLV_HEADER_BYTES.Length); fs.Write(new byte[] { 0, 0, 0, 0, }, 0, 4); double clipDuration = (Tags[Tags.Count - 1].TimeStamp - Tags[0].TimeStamp) / 1000d; Header["duration"] = clipDuration; Header["lasttimestamp"] = (double)(Tags[Tags.Count - 1].TimeStamp - Tags[0].TimeStamp); var t = funcFlvTag(); t.TagType = TagType.DATA; if (Header.ContainsKey("BililiveRecorder")) { // TODO: 更好的写法 (Header["BililiveRecorder"] as Dictionary)["starttime"] = DateTime.UtcNow - TimeSpan.FromSeconds(clipDuration); } t.Data = Header.ToBytes(); t.WriteTo(fs); int offset = Tags[0].TimeStamp; HTags.ForEach(tag => tag.WriteTo(fs)); Tags.ForEach(tag => tag.WriteTo(fs, offset)); logger.Info("剪辑已保存:{0}", Path.GetFileName(path)); fs.Close(); } Tags.Clear(); } catch (Exception ex) { logger.Error(ex, "保存剪辑文件时出错"); } ClipFinalized?.Invoke(this, new ClipFinalizedArgs() { ClipProcessor = this }); } public event ClipFinalizedEvent ClipFinalized; } }