diff --git a/src/JT1078.Hls.Test/M3U8Config.cs b/src/JT1078.Hls.Test/M3U8Config.cs
new file mode 100644
index 0000000..e698afb
--- /dev/null
+++ b/src/JT1078.Hls.Test/M3U8Config.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JT1078.Hls.Test
+{
+ ///
+ /// m3u8配置文件
+ ///
+ public class M3U8Config
+ {
+ ///
+ /// m3u8文件中包含的ts文件数
+ ///
+ public int TsFileCount { get; set; } = 10;
+ ///
+ /// 每个ts文件的最大时长
+ ///
+ public int TsFileMaxSecond { get; set; } = 10;
+ ///
+ /// m3u8文件中第一个ts文件序号
+ ///
+ public int FirstTsSerialNo { get; set; } = 0;
+ }
+}
diff --git a/src/JT1078.Hls.Test/TS_Package_Test.cs b/src/JT1078.Hls.Test/TS_Package_Test.cs
index 54bcfcc..11b47f8 100644
--- a/src/JT1078.Hls.Test/TS_Package_Test.cs
+++ b/src/JT1078.Hls.Test/TS_Package_Test.cs
@@ -4,6 +4,7 @@ using JT1078.Hls.MessagePack;
using JT1078.Protocol;
using JT1078.Protocol.Extensions;
using System;
+using System.Buffers;
using System.Buffers.Binary;
using System.Collections;
using System.Collections.Generic;
@@ -19,6 +20,7 @@ namespace JT1078.Hls.Test
///
public class TS_Package_Test
{
+
[Fact]
public void ToBufferTest1()
{
@@ -174,10 +176,9 @@ namespace JT1078.Hls.Test
{
try
{
- int file_count = 10;
- int file_max_second = 10;
- int first_serialno = 0;
- double file_real_second = 10;
+ ArrayPool arrayPool = ArrayPool.Create();
+ M3U8Config m3U8Config = new M3U8Config();
+ double file_real_second = m3U8Config.TsFileMaxSecond;
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.txt"));
bool isNeedFirstHeadler = true;
@@ -185,11 +186,13 @@ namespace JT1078.Hls.Test
ulong init_seconds = 0;
int duration = 0;
- int temp_seconds = 0;
+ int accu_seconds = 0;
+
+ var m3u8Filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "index.m3u8");
+ AppendM3U8Start(m3u8Filepath, m3U8Config.TsFileMaxSecond, m3U8Config.FirstTsSerialNo);
+
+ var fileData= arrayPool.Rent(18888888);
- var m3u8Filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_HLS.m3u8");
- AppendM3U8Start(m3u8Filepath, file_max_second, first_serialno);
- byte[] fileData = new byte[1888888888];
int fileIndex = 0;
foreach (var line in lines)
{
@@ -199,21 +202,25 @@ namespace JT1078.Hls.Test
JT1078Package fullpackage = JT1078Serializer.Merge(package);
if (fullpackage != null)
{
- if (temp_seconds / 1000>= file_max_second) {
- file_real_second = temp_seconds / 1000.0;//秒
- //生成一个文件
- var file_name = $"JT1078_{first_serialno}.ts";
- var ts_filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", file_name);
- CreateTsFile(ts_filepath, fileData);
+ if (accu_seconds / 1000>= m3U8Config.TsFileMaxSecond) {
+ //ecode_slice_header error 以非关键帧开始得
+ file_real_second = accu_seconds / 1000.0;//秒
+ //生成一个ts文件
+ var ts_name = $"{m3U8Config.FirstTsSerialNo}.ts";
+ var ts_filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", ts_name);
+ CreateTsFile(ts_filepath, fileData.AsSpan().Slice(0, fileIndex).ToArray());
+ isNeedFirstHeadler = true;
+ arrayPool.Return(fileData);
+ fileData = arrayPool.Rent(18888888);
+ fileIndex = 0;
+ var media_sequence_no = m3U8Config.FirstTsSerialNo - m3U8Config.TsFileCount;
+ var del_ts_name=$"{media_sequence_no}.ts";
+ var del_ts_filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264",$"{media_sequence_no}.ts");
//更新m3u8文件
- AppendTSFile(m3u8Filepath, file_real_second, file_name);
- //删除最早一个文件
- var del_file_name = $"JT1078_{first_serialno- file_count}.ts";
- var del_ts_filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", del_file_name);
- DeleteTsFile(del_ts_filepath);
- fileData = new byte[1888888888];
- temp_seconds = 0;
- first_serialno = first_serialno + 1;
+ UpdateM3U8File(m3u8Filepath, file_real_second, media_sequence_no+1, del_ts_filepath, del_ts_name,ts_name);
+
+ accu_seconds = 0;
+ m3U8Config.FirstTsSerialNo = m3U8Config.FirstTsSerialNo + 1;
}
if (init_seconds == 0)
@@ -223,7 +230,7 @@ namespace JT1078.Hls.Test
else {
duration =(int)( fullpackage.Timestamp - init_seconds);
init_seconds = fullpackage.Timestamp;
- temp_seconds = Convert.ToInt32(temp_seconds) + duration;
+ accu_seconds = Convert.ToInt32(accu_seconds) + duration;
}
if (isNeedFirstHeadler)
@@ -260,21 +267,75 @@ namespace JT1078.Hls.Test
}
}
- private void DeleteTsFile(string tsFilepath) {
- if (File.Exists(tsFilepath)) File.Delete(tsFilepath);
- }
- private void CreateTsFile(string tsFilepath, byte[] data) {
- using (var fileStream = new FileStream(tsFilepath, FileMode.OpenOrCreate, FileAccess.Write))
+ private void CreateTsFile(string ts_filepath, byte[] data) {
+ using (var fileStream = new FileStream(ts_filepath, FileMode.OpenOrCreate, FileAccess.Write))
{
fileStream.Write(data);
}
}
- private void AppendTSFile(string filepath,double tsRealSecond, string tsName) {
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void UpdateM3U8File(string m3u8_filepath,double tsRealSecond,int media_sequence_no, string del_ts_filepath,string del_ts_name, string ts_name) {
StringBuilder sb = new StringBuilder();
+ if (File.Exists(del_ts_filepath))
+ {
+ //删除最早一个ts文件
+ File.Delete(del_ts_filepath);
+ bool startAppendFileContent = true;
+ bool isFirstEXTINF = true;
+ using (StreamReader sr = new StreamReader(m3u8_filepath))
+ {
+ while (!sr.EndOfStream)
+ {
+ var text = sr.ReadLine();
+ if (text.Length == 0) continue;
+ if (text.StartsWith("#EXT-X-MEDIA-SEQUENCE"))
+ {
+ string media_sequence = $"#EXT-X-MEDIA-SEQUENCE:{media_sequence_no}";
+ sb.AppendLine(media_sequence);
+ continue;
+ }
+ if (text.StartsWith("#EXTINF") && isFirstEXTINF)
+ {
+ startAppendFileContent = false;
+ continue;
+ }
+ if (text.StartsWith(del_ts_name) && isFirstEXTINF)
+ {
+ isFirstEXTINF = false;
+ startAppendFileContent = true;
+ continue;
+ }
+ if (startAppendFileContent)
+ {
+ sb.AppendLine(text);
+ }
+ }
+ }
+ AppendM3u8(m3u8_filepath, tsRealSecond, ts_name, sb, false);
+ }
+ else {
+ AppendM3u8(m3u8_filepath, tsRealSecond, ts_name, sb);
+ }
+ }
+ ///
+ /// m3u8追加ts文件
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void AppendM3u8(string m3u8_filepath, double tsRealSecond, string tsName, StringBuilder sb,bool isAppend=true) {
sb.AppendLine($"#EXTINF:{tsRealSecond},");//extra info,分片TS的信息,如时长,带宽等
sb.AppendLine($"{tsName}");//文件名
- using (StreamWriter sw = new StreamWriter(filepath))
+ using (StreamWriter sw = new StreamWriter(m3u8_filepath, isAppend))
{
sw.WriteLine(sb);
}
@@ -289,7 +350,7 @@ namespace JT1078.Hls.Test
sb.AppendLine($"#EXT-X-TARGETDURATION:{fileMaxSecond}");//每个分片TS的最大的时长
sb.AppendLine($"#EXT-X-MEDIA-SEQUENCE:{firstTSSerialno}");//第一个TS分片的序列号
- using (StreamWriter sw = new StreamWriter(filepath))
+ using (StreamWriter sw = new StreamWriter(filepath,true))
{
sw.WriteLine(sb);
}
@@ -302,7 +363,7 @@ namespace JT1078.Hls.Test
StringBuilder sb = new StringBuilder();
sb.AppendLine("#EXT-X-ENDLIST"); //m3u8文件结束符 表示视频已经结束 有这个标志同时也说明当前流是一个非直播流
//#EXT-X-PLAYLIST-TYPE:VOD/Live //VOD表示当前视频流不是一个直播流,而是点播流(也就是视频的全部ts文件已经生成)
- using (StreamWriter sw = new StreamWriter(filepath))
+ using (StreamWriter sw = new StreamWriter(filepath,true))
{
sw.WriteLine(sb);
}