@@ -0,0 +1,20 @@ | |||||
using JT1078.Hls.Enums; | |||||
using JT1078.Hls.MessagePack; | |||||
using JT1078.Protocol.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using Xunit; | |||||
namespace JT1078.Hls.Test | |||||
{ | |||||
/// <summary> | |||||
/// ʹÓÃdoc/video/demo0.ts | |||||
/// </summary> | |||||
public class TS_SDT_Package_Test | |||||
{ | |||||
[Fact] | |||||
public void ToBufferTest() | |||||
{ | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls.Enums | |||||
{ | |||||
/// <summary> | |||||
/// 运行状态 | |||||
/// </summary> | |||||
public enum RunningStatus | |||||
{ | |||||
未定义=0, | |||||
未运行=1, | |||||
几秒后开始=2, | |||||
暂停=3, | |||||
运行=4, | |||||
} | |||||
} |
@@ -33,4 +33,7 @@ | |||||
<PackagePath></PackagePath> | <PackagePath></PackagePath> | ||||
</None> | </None> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.1" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
@@ -163,5 +163,11 @@ namespace JT1078.Hls.MessagePack | |||||
{ | { | ||||
return writer.WrittenCount; | return writer.WrittenCount; | ||||
} | } | ||||
public void WriteString(string value) | |||||
{ | |||||
byte[] codeBytes = TSConstants.Encoding.GetBytes(value); | |||||
codeBytes.CopyTo(writer.Free); | |||||
writer.Advance(codeBytes.Length); | |||||
} | |||||
} | } | ||||
} | } |
@@ -6,6 +6,12 @@ namespace JT1078.Hls | |||||
{ | { | ||||
public static class TSConstants | public static class TSConstants | ||||
{ | { | ||||
static TSConstants() | |||||
{ | |||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); | |||||
Encoding = Encoding.GetEncoding("GBK"); | |||||
} | |||||
public static Encoding Encoding { get; } | |||||
/// <summary> | /// <summary> | ||||
/// 固定包长度 | /// 固定包长度 | ||||
/// </summary> | /// </summary> | ||||
@@ -3,6 +3,7 @@ using JT1078.Hls.Interfaces; | |||||
using JT1078.Hls.MessagePack; | using JT1078.Hls.MessagePack; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics.Tracing; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | using System.Text; | ||||
@@ -15,12 +16,12 @@ namespace JT1078.Hls | |||||
{ | { | ||||
public TS_Header Header { get; set; } | public TS_Header Header { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// PAT表固定为0x00 | |||||
/// | |||||
/// 8bit | /// 8bit | ||||
/// </summary> | /// </summary> | ||||
public byte TableId { get; set; } = 0x42; | public byte TableId { get; set; } = 0x42; | ||||
/// <summary> | /// <summary> | ||||
/// 固定为二进制1 | |||||
/// | |||||
/// 1bit | /// 1bit | ||||
/// </summary> | /// </summary> | ||||
internal byte SectionSyntaxIndicator { get; set; } | internal byte SectionSyntaxIndicator { get; set; } | ||||
@@ -43,14 +44,14 @@ namespace JT1078.Hls | |||||
/// 传输流ID | /// 传输流ID | ||||
/// 16bit | /// 16bit | ||||
/// </summary> | /// </summary> | ||||
internal ushort TransportStreamId { get; set; } = 0x0001; | |||||
internal ushort TransportStreamId { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// 2bit | /// 2bit | ||||
/// </summary> | /// </summary> | ||||
internal byte Reserved2 { get; set; } | internal byte Reserved2 { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// 版本号, | |||||
/// | |||||
/// 5bit | /// 5bit | ||||
/// </summary> | /// </summary> | ||||
public byte VersionNumber { get; set; } | public byte VersionNumber { get; set; } | ||||
@@ -79,7 +80,7 @@ namespace JT1078.Hls | |||||
/// 1Byte | /// 1Byte | ||||
/// </summary> | /// </summary> | ||||
internal byte ReservedFutureUse2 { get; set; } | internal byte ReservedFutureUse2 { get; set; } | ||||
public List<TS_SDT_Service_Descriptor> Services { get; set; } | |||||
public List<TS_SDT_Service> Services { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 前面数据的CRC32校验码 | /// 前面数据的CRC32校验码 | ||||
@@ -88,33 +89,21 @@ namespace JT1078.Hls | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | public void ToBuffer(ref TSMessagePackWriter writer) | ||||
{ | { | ||||
Header.PackageType = PackageType.PAT; | |||||
Header.ToBuffer(ref writer); | |||||
writer.WriteByte(TableId); | writer.WriteByte(TableId); | ||||
//SectionSyntaxIndicator Zero Reserved1 SectionLength | |||||
//1 0 11 0000 0000 0000 | |||||
//(ushort)(0b_1011_0000_0000_0000 | SectionLength) | |||||
writer.Skip(2, out int SectionLengthPosition); | writer.Skip(2, out int SectionLengthPosition); | ||||
writer.WriteUInt16(TransportStreamId); | writer.WriteUInt16(TransportStreamId); | ||||
//Reserved2 VersionNumber CurrentNextIndicator | |||||
//11 00000 1 | |||||
var a = 0xC0 & (Reserved2 << 6); | |||||
var b = 0x3E & (VersionNumber << 3); | |||||
var c = (byte)(a | b | CurrentNextIndicator); | |||||
writer.WriteByte(c); | |||||
writer.WriteByte((byte)(Reserved2 << 6 | VersionNumber << 1 | CurrentNextIndicator)); | |||||
writer.WriteByte(SectionNumber); | writer.WriteByte(SectionNumber); | ||||
writer.WriteByte(LastSectionNumber); | writer.WriteByte(LastSectionNumber); | ||||
//if (Programs != null) | |||||
//{ | |||||
// foreach (var program in Programs) | |||||
// { | |||||
// program.ToBuffer(ref writer); | |||||
// } | |||||
//} | |||||
const int crcLength= 4; | |||||
writer.WriteUInt16Return((ushort)(0b_1011_0000_0000_0000 | (ushort)(writer.GetCurrentPosition() - SectionLengthPosition - 2)+ crcLength), SectionLengthPosition); | |||||
//打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。 | |||||
//ts header(4B) + adaptation field length(1) | |||||
writer.WriteUInt16(OriginalNetworkId); | |||||
writer.WriteByte(ReservedFutureUse2); | |||||
foreach (var service in Services) | |||||
{ | |||||
service.ToBuffer(ref writer); | |||||
} | |||||
ushort servicesLength =(ushort)( writer.GetCurrentPosition() - SectionLengthPosition); | |||||
const int crcLength = 4; | |||||
writer.WriteUInt16Return((ushort)(SectionSyntaxIndicator<<15 | ReservedFutureUse1<<14 | servicesLength+ crcLength), SectionLengthPosition); | |||||
writer.WriteCRC32(5); | writer.WriteCRC32(5); | ||||
var size = TSConstants.FiexdPackageLength - writer.GetCurrentPosition(); | var size = TSConstants.FiexdPackageLength - writer.GetCurrentPosition(); | ||||
writer.WriteArray(Enumerable.Range(0, size).Select(s => (byte)0xFF).ToArray()); | writer.WriteArray(Enumerable.Range(0, size).Select(s => (byte)0xFF).ToArray()); | ||||
@@ -0,0 +1,76 @@ | |||||
using JT1078.Hls.Enums; | |||||
using JT1078.Hls.Interfaces; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
/// <summary> | |||||
/// 业务描述服务 | |||||
/// </summary> | |||||
public class TS_SDT_Service : ITSMessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// 业务标识符 | |||||
/// 用于在 TS 流中识别不同的业务。service_id 与program_map_section 中的 program_number 取同一值 | |||||
/// 16bit | |||||
/// </summary> | |||||
internal ushort ServiceId { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// 6bit | |||||
/// </summary> | |||||
internal byte ReservedFutureUse { get; set; } | |||||
/// <summary> | |||||
/// EIT 时间表标志 | |||||
/// 置“1”时,表示业务的 EIT 时间表信息存在于当前 TS 中(一个 EIT 时间表子表两次出现的最大时间间隔信息见 ETR 211)。 | |||||
/// 置“0”时,表示业务的 EIT 时间表信息不在当前 TS 中 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte EITScheduleFlag { get; set; } | |||||
/// <summary> | |||||
/// EIT 当前后续标志 | |||||
/// 置“1”时,表示业务的 EIT 当前后续信息存在于当前 TS 中(一个 EIT 当前后续子表两次出现的最大时间间隔信息见ETR 211) | |||||
/// 置“0”时,表示业务的 EIT 当前后续信息不在当前 TS 中。 | |||||
/// 1bit | |||||
/// </summary> | |||||
public byte EITPresentFollowingFlag { get; set; } | |||||
/// <summary> | |||||
/// 运行状态 | |||||
/// 对于一个 NVOD 业务,running_status 的值都置“0” | |||||
/// 3bit | |||||
/// </summary> | |||||
internal RunningStatus RunningStatus { get; set; } | |||||
/// <summary> | |||||
/// 自由条件接收模式 | |||||
/// 置“0”时,表示业务的所有组件都未被加扰 | |||||
/// 置“1”时,表示一路或多路码流的接收由 CA 系统控制。 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte FreeCAMode { get; set; } | |||||
/// <summary> | |||||
/// 描述符循环长度 | |||||
/// 指出从本字段的下一个字节开始的描述符的总字节长度。 | |||||
/// 12bit | |||||
/// </summary> | |||||
public ushort DescriptorsLoopLength { get; set; } | |||||
public List<TS_SDT_Service_Descriptor> Descriptors { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteUInt16(ServiceId); | |||||
writer.WriteByte((byte)((ReservedFutureUse << 2) | (EITScheduleFlag << 1) | EITPresentFollowingFlag)); | |||||
writer.Skip(2, out var position); | |||||
foreach (var descriptor in Descriptors) | |||||
{ | |||||
descriptor.ToBuffer(ref writer); | |||||
} | |||||
DescriptorsLoopLength = (ushort)(writer.GetCurrentPosition() - position); | |||||
writer.WriteUInt16Return((ushort)(((ushort)RunningStatus << 13) | ((ushort)FreeCAMode << 12) | DescriptorsLoopLength), position); | |||||
} | |||||
} | |||||
} |
@@ -1,123 +0,0 @@ | |||||
using JT1078.Hls.Enums; | |||||
using JT1078.Hls.Interfaces; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
/// <summary> | |||||
/// 业务描述服务 | |||||
/// </summary> | |||||
public class TS_SDT_Service : ITSMessagePackFormatter | |||||
{ | |||||
public TS_Header Header { get; set; } | |||||
/// <summary> | |||||
/// PAT表固定为0x00 | |||||
/// 8bit | |||||
/// </summary> | |||||
public byte TableId { get; set; } = 0x42; | |||||
/// <summary> | |||||
/// 固定为二进制1 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte SectionSyntaxIndicator { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte ReservedFutureUse1 { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved1 { get; set; } | |||||
/// <summary> | |||||
/// 后面数据的长度 | |||||
/// 12bit | |||||
/// </summary> | |||||
public ushort SectionLength { get; set; } | |||||
/// <summary> | |||||
/// 传输流ID | |||||
/// 16bit | |||||
/// </summary> | |||||
internal ushort TransportStreamId { get; set; } = 0x0001; | |||||
/// <summary> | |||||
/// | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved2 { get; set; } | |||||
/// <summary> | |||||
/// 版本号, | |||||
/// 5bit | |||||
/// </summary> | |||||
public byte VersionNumber { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// 1bit | |||||
/// </summary> | |||||
public byte CurrentNextIndicator { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// bit8 | |||||
/// </summary> | |||||
internal byte SectionNumber { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// bit8 | |||||
/// </summary> | |||||
internal byte LastSectionNumber { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// bit8 | |||||
/// </summary> | |||||
internal ushort OriginalNetworkId { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// 1Byte | |||||
/// </summary> | |||||
internal byte ReservedFutureUse2 { get; set; } | |||||
public List<TS_PAT_Program> Services { get; set; } | |||||
/// <summary> | |||||
/// 前面数据的CRC32校验码 | |||||
/// </summary> | |||||
public uint CRC32 { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
Header.PackageType = PackageType.PAT; | |||||
Header.ToBuffer(ref writer); | |||||
writer.WriteByte(TableId); | |||||
//SectionSyntaxIndicator Zero Reserved1 SectionLength | |||||
//1 0 11 0000 0000 0000 | |||||
//(ushort)(0b_1011_0000_0000_0000 | SectionLength) | |||||
writer.Skip(2, out int SectionLengthPosition); | |||||
writer.WriteUInt16(TransportStreamId); | |||||
//Reserved2 VersionNumber CurrentNextIndicator | |||||
//11 00000 1 | |||||
var a = 0xC0 & (Reserved2 << 6); | |||||
var b = 0x3E & (VersionNumber << 3); | |||||
var c = (byte)(a | b | CurrentNextIndicator); | |||||
writer.WriteByte(c); | |||||
writer.WriteByte(SectionNumber); | |||||
writer.WriteByte(LastSectionNumber); | |||||
//if (Programs != null) | |||||
//{ | |||||
// foreach (var program in Programs) | |||||
// { | |||||
// program.ToBuffer(ref writer); | |||||
// } | |||||
//} | |||||
const int crcLength= 4; | |||||
writer.WriteUInt16Return((ushort)(0b_1011_0000_0000_0000 | (ushort)(writer.GetCurrentPosition() - SectionLengthPosition - 2)+ crcLength), SectionLengthPosition); | |||||
//打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。 | |||||
//ts header(4B) + adaptation field length(1) | |||||
writer.WriteCRC32(5); | |||||
var size = TSConstants.FiexdPackageLength - writer.GetCurrentPosition(); | |||||
writer.WriteArray(Enumerable.Range(0, size).Select(s => (byte)0xFF).ToArray()); | |||||
} | |||||
} | |||||
} |
@@ -13,111 +13,53 @@ namespace JT1078.Hls | |||||
/// </summary> | /// </summary> | ||||
public class TS_SDT_Service_Descriptor : ITSMessagePackFormatter | public class TS_SDT_Service_Descriptor : ITSMessagePackFormatter | ||||
{ | { | ||||
public TS_Header Header { get; set; } | |||||
/// <summary> | |||||
/// PAT表固定为0x00 | |||||
/// 8bit | |||||
/// </summary> | |||||
public byte TableId { get; set; } = 0x42; | |||||
/// <summary> | |||||
/// 固定为二进制1 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte SectionSyntaxIndicator { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte ReservedFutureUse1 { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved1 { get; set; } | |||||
/// <summary> | |||||
/// 后面数据的长度 | |||||
/// 12bit | |||||
/// </summary> | |||||
public ushort SectionLength { get; set; } | |||||
/// <summary> | |||||
/// 传输流ID | |||||
/// 16bit | |||||
/// 8bit | |||||
/// </summary> | /// </summary> | ||||
internal ushort TransportStreamId { get; set; } = 0x0001; | |||||
public byte DescriptorTag { get; set; } = 0x48; | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved2 { get; set; } | |||||
/// <summary> | |||||
/// 版本号, | |||||
/// 5bit | |||||
/// 8bit | |||||
/// </summary> | /// </summary> | ||||
public byte VersionNumber { get; set; } | |||||
internal byte DescriptorLength { get; set; } = 0x12; | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// 1bit | |||||
/// 8bit | |||||
/// </summary> | /// </summary> | ||||
public byte CurrentNextIndicator { get; set; } | |||||
internal byte ServiceType { get; set; } = 0x01; | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// bit8 | |||||
/// 8bit | |||||
/// </summary> | /// </summary> | ||||
internal byte SectionNumber { get; set; } | |||||
internal byte ServiceProviderLength { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// bit8 | |||||
/// ServiceProviderLength | |||||
/// </summary> | /// </summary> | ||||
internal byte LastSectionNumber { get; set; } | |||||
public string ServiceProvider { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// bit8 | |||||
/// 8bit | |||||
/// </summary> | /// </summary> | ||||
internal ushort OriginalNetworkId { get; set; } | |||||
internal byte ServiceNameLenth { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// 1Byte | |||||
/// ServiceNameLenth | |||||
/// </summary> | /// </summary> | ||||
internal byte ReservedFutureUse2 { get; set; } | |||||
public List<TS_PAT_Program> Services { get; set; } | |||||
/// <summary> | |||||
/// 前面数据的CRC32校验码 | |||||
/// </summary> | |||||
public uint CRC32 { get; set; } | |||||
internal string ServiceName { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | public void ToBuffer(ref TSMessagePackWriter writer) | ||||
{ | { | ||||
Header.PackageType = PackageType.PAT; | |||||
Header.ToBuffer(ref writer); | |||||
writer.WriteByte(TableId); | |||||
//SectionSyntaxIndicator Zero Reserved1 SectionLength | |||||
//1 0 11 0000 0000 0000 | |||||
//(ushort)(0b_1011_0000_0000_0000 | SectionLength) | |||||
writer.Skip(2, out int SectionLengthPosition); | |||||
writer.WriteUInt16(TransportStreamId); | |||||
//Reserved2 VersionNumber CurrentNextIndicator | |||||
//11 00000 1 | |||||
var a = 0xC0 & (Reserved2 << 6); | |||||
var b = 0x3E & (VersionNumber << 3); | |||||
var c = (byte)(a | b | CurrentNextIndicator); | |||||
writer.WriteByte(c); | |||||
writer.WriteByte(SectionNumber); | |||||
writer.WriteByte(LastSectionNumber); | |||||
//if (Programs != null) | |||||
//{ | |||||
// foreach (var program in Programs) | |||||
// { | |||||
// program.ToBuffer(ref writer); | |||||
// } | |||||
//} | |||||
const int crcLength= 4; | |||||
writer.WriteUInt16Return((ushort)(0b_1011_0000_0000_0000 | (ushort)(writer.GetCurrentPosition() - SectionLengthPosition - 2)+ crcLength), SectionLengthPosition); | |||||
//打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。 | |||||
//ts header(4B) + adaptation field length(1) | |||||
writer.WriteCRC32(5); | |||||
var size = TSConstants.FiexdPackageLength - writer.GetCurrentPosition(); | |||||
writer.WriteArray(Enumerable.Range(0, size).Select(s => (byte)0xFF).ToArray()); | |||||
writer.WriteByte(DescriptorTag); | |||||
writer.Skip(1,out var position); | |||||
writer.WriteByte(ServiceType); | |||||
writer.Skip(1, out var serviceProviderLengthPosition); | |||||
writer.WriteString(ServiceProvider); | |||||
writer.WriteByteReturn((byte)(writer.GetCurrentPosition() - serviceProviderLengthPosition), serviceProviderLengthPosition); | |||||
writer.Skip(1, out int SeviceNameLengthPosition); | |||||
writer.WriteString(ServiceName); | |||||
writer.WriteByteReturn((byte)(writer.GetCurrentPosition() - SeviceNameLengthPosition), SeviceNameLengthPosition); | |||||
writer.WriteByteReturn((byte)(writer.GetCurrentPosition() - position), position); | |||||
} | } | ||||
} | } | ||||
} | } |