/// <summary> /// 创建M3U8文件 /// </summary> /// <param name="curTsFileInfo">当前ts文件信息</param> /// <param name="tsFileInfoQueue">ts文件信息队列</param> private void CreateM3U8File(TsFileInfo curTsFileInfo, Queue <TsFileInfo> tsFileInfoQueue) { //ecode_slice_header error 以非关键帧开始生成的ts,通过ffplay播放会出现报错信息 StringBuilder sb = new StringBuilder(); sb.AppendLine("#EXTM3U"); //开始 sb.AppendLine("#EXT-X-VERSION:3"); //版本号 sb.AppendLine("#EXT-X-ALLOW-CACHE:NO"); //是否允许cache sb.AppendLine($"#EXT-X-TARGETDURATION:{m3U8Option.TsFileMaxSecond}"); //第一个TS分片的序列号 sb.AppendLine($"#EXT-X-MEDIA-SEQUENCE:{(curTsFileInfo.TsFileSerialNo - m3U8Option.TsFileCapacity > 0 ? (curTsFileInfo.TsFileSerialNo - m3U8Option.TsFileCapacity+1) : 0)}"); //默认第一个文件为0 sb.AppendLine(); for (int i = 0; i < tsFileInfoQueue.Count; i++) { var tsFileInfo = tsFileInfoQueue.ElementAt(i); sb.AppendLine($"#EXTINF:{tsFileInfo.Duration},"); sb.AppendLine($"{tsFileInfo.FileName}?sim={tsFileInfo.Sim}&channelNo={tsFileInfo.ChannelNo}"); } string m3u8FileName = Path.Combine(m3U8Option.HlsFileDirectory, $"{curTsFileInfo.Sim}_{curTsFileInfo.ChannelNo}", m3U8Option.M3U8FileName); using (FileStream fs = new FileStream(m3u8FileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) { var buffer = Encoding.UTF8.GetBytes(sb.ToString()); fs.Write(buffer, 0, buffer.Length); } }
/// <summary> /// 创建TS文件信息 /// </summary> /// <param name="key"></param> /// <returns></returns> private TsFileInfo CreateTsFileInfo(string key) { if (!curTsFileInfoDic.TryGetValue(key, out var curTsFileInfo)) { curTsFileInfo = new TsFileInfo(); curTsFileInfoDic.TryAdd(key, curTsFileInfo); } return(curTsFileInfo); }
/// <summary> /// 生成ts和m3u8文件 /// </summary> /// <param name="jt1078Package"></param> public void CreateTsData(JT1078Package jt1078Package) { string key = jt1078Package.GetKey(); string hlsFileDirectory = m3U8Option.HlsFileDirectory; string m3u8FileName = Path.Combine(hlsFileDirectory, key, m3U8Option.M3U8FileName); if (!File.Exists(m3u8FileName)) { File.Create(m3u8FileName); //创建m3u8文件 } var buff = TSArrayPool.Rent(jt1078Package.Bodies.Length + 1024); TSMessagePackWriter tSMessagePackWriter = new TSMessagePackWriter(buff); try { var curTsFileInfo = CreateTsFileInfo(key); if (!curTsFileInfo.IsCreateTsFile) { var pes = tSEncoder.CreatePES(jt1078Package); tSMessagePackWriter.WriteArray(pes); CreateTsFile(curTsFileInfo.FileName, key, tSMessagePackWriter.FlushAndGetArray()); curTsFileInfo.Duration = (jt1078Package.Timestamp - curTsFileInfo.TsFirst1078PackageTimeStamp) / 1000.0; //按设定的时间(默认为10秒)切分ts文件 if (curTsFileInfo.Duration > m3U8Option.TsFileMaxSecond) { var tsFileInfoQueue = ManageTsFileInfo(key, curTsFileInfo); CreateM3U8File(curTsFileInfo, tsFileInfoQueue); var newTsFileInfo = new TsFileInfo { IsCreateTsFile = true, Duration = 0, TsFileSerialNo = ++curTsFileInfo.TsFileSerialNo }; curTsFileInfoDic.TryUpdate(key, newTsFileInfo, curTsFileInfo); } } else { curTsFileInfo.IsCreateTsFile = false; curTsFileInfo.TsFirst1078PackageTimeStamp = jt1078Package.Timestamp; curTsFileInfo.FileName = $"{curTsFileInfo.TsFileSerialNo}.ts"; var sdt = tSEncoder.CreateSDT(jt1078Package); tSMessagePackWriter.WriteArray(sdt); var pat = tSEncoder.CreatePAT(jt1078Package); tSMessagePackWriter.WriteArray(pat); var pmt = tSEncoder.CreatePMT(jt1078Package); tSMessagePackWriter.WriteArray(pmt); var pes = tSEncoder.CreatePES(jt1078Package); tSMessagePackWriter.WriteArray(pes); CreateTsFile(curTsFileInfo.FileName, key, tSMessagePackWriter.FlushAndGetArray()); } } finally { TSArrayPool.Return(buff); } }
/// <summary> /// 创建TS文件信息 /// </summary> /// <param name="key"></param> /// <returns></returns> private TsFileInfo CreateTsFileInfo(string key) { if (!curTsFileInfoDic.TryGetValue(key, out var curTsFileInfo)) { curTsFileInfo = new TsFileInfo() { Sim = key.Split('_')[0], ChannelNo = key.Split('_')[1] }; curTsFileInfoDic.TryAdd(key, curTsFileInfo); } else { curTsFileInfo.Sim = key.Split('_')[0]; curTsFileInfo.ChannelNo = key.Split('_')[1]; } return(curTsFileInfo); }
/// <summary> /// 维护TS文件信息队列 /// </summary> /// <param name="key"></param> /// <param name="curTsFileInfo"></param> /// <returns></returns> private Queue <TsFileInfo> ManageTsFileInfo(string key, TsFileInfo curTsFileInfo) { if (tsFileInfoQueueDic.TryGetValue(key, out var tsFileInfoQueue)) { if (tsFileInfoQueue.Count >= m3U8Option.TsFileCapacity) { var deleteTsFileInfo = tsFileInfoQueue.Dequeue(); var deleteTsFileName = Path.Combine(m3U8Option.HlsFileDirectory, deleteTsFileInfo.FileName); if (File.Exists(deleteTsFileName)) { File.Delete(deleteTsFileName); } } tsFileInfoQueue.Enqueue(curTsFileInfo); } else { tsFileInfoQueue = new Queue <TsFileInfo>(new List <TsFileInfo> { curTsFileInfo }); tsFileInfoQueueDic.TryAdd(key, tsFileInfoQueue); } return(tsFileInfoQueue); }