@@ -0,0 +1,7 @@ | |||||
#EXTM3U | |||||
#EXT-X-VERSION:3 | |||||
#EXT-X-TARGETDURATION:7 | |||||
#EXT-X-MEDIA-SEQUENCE:0 | |||||
#EXTINF:7.200667, | |||||
demo0.ts | |||||
#EXT-X-ENDLIST |
@@ -0,0 +1,20 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp3.1</TargetFramework> | |||||
<IsPackable>false</IsPackable> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> | |||||
<PackageReference Include="xunit" Version="2.4.0" /> | |||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> | |||||
<PackageReference Include="coverlet.collector" Version="1.2.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.Hls\JT1078.Hls.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -0,0 +1,55 @@ | |||||
using System; | |||||
using Xunit; | |||||
namespace JT1078.Hls.Test | |||||
{ | |||||
public class UnitTest1 | |||||
{ | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
} | |||||
//---------PMT | |||||
//47 50 00 10 00 02 B0 1D 00 01 C1 00 00 E1 00 F0 00 1B E1 00 F0 00 0F E1 01 F0 06 0A 04 75 6E 64 00 08 7D E8 77 | |||||
//47 | |||||
//50 00 | |||||
//10 | |||||
//00 | |||||
//02 | |||||
//B0 1D | |||||
//00 01 | |||||
//C1 | |||||
//00 | |||||
//00 | |||||
//E1 00 | |||||
//F0 00 | |||||
//1B | |||||
//E1 00 | |||||
// F0 00 | |||||
//0F | |||||
//E1 01 | |||||
// F0 06 | |||||
// 0A | |||||
// 04 75 | |||||
// 6E 64 | |||||
// 00 | |||||
//08 7D E8 77 | |||||
//----------PAT | |||||
//47 40 00 10 00 00 B0 0D 00 01 C1 00 00 00 01 F0 00 2A B1 04 B2 | |||||
//47 | |||||
//40 00 | |||||
//10 | |||||
//00 | |||||
//00 | |||||
//B0 0D | |||||
//00 01 | |||||
//C1 | |||||
//00 | |||||
//00 | |||||
//00 01 | |||||
//F0 00 | |||||
//2A B1 04 B2 | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
using System; | |||||
namespace JT1078.Hls | |||||
{ | |||||
/// <summary> | |||||
/// <see cref="System.Buffers.Writer"/> | |||||
/// </summary> | |||||
ref partial struct TSBufferWriter | |||||
{ | |||||
private Span<byte> _buffer; | |||||
public TSBufferWriter(Span<byte> buffer) | |||||
{ | |||||
_buffer = buffer; | |||||
WrittenCount = 0; | |||||
} | |||||
public Span<byte> Free => _buffer.Slice(WrittenCount); | |||||
public Span<byte> Written => _buffer.Slice(0, WrittenCount); | |||||
public int WrittenCount { get; private set; } | |||||
public void Advance(int count) | |||||
{ | |||||
WrittenCount += count; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,28 @@ | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using JT1078.Protocol.H264; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class ES_Package: ITSMessagePackFormatter | |||||
{ | |||||
public static byte[] NALU0X09 = new byte[] { 0x00, 0x00, 0x00, 0x01, 0x09, 0xFF }; | |||||
public byte[] NALU0x09 { get; set; } = NALU0X09; | |||||
public List<H264NALU> NALUs { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteArray(NALU0x09); | |||||
if(NALUs!=null) | |||||
{ | |||||
foreach(var nalu in NALUs) | |||||
{ | |||||
writer.WriteArray(nalu.StartCodePrefix); | |||||
writer.WriteArray(nalu.RawData); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls.Enums | |||||
{ | |||||
public enum AdaptationFieldControl | |||||
{ | |||||
保留= 0000_0000, | |||||
无自适应域_仅含有效负载 = 0001_0000, | |||||
仅含自适应域_无有效负载 = 0010_0000, | |||||
同时带有自适应域和有效负载 = 0011_0000, | |||||
} | |||||
} |
@@ -0,0 +1,15 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls.Enums | |||||
{ | |||||
/// <summary> | |||||
/// 取0x50表示包含PCR或0x40表示不包含PCR | |||||
/// </summary> | |||||
public enum PCRInclude:byte | |||||
{ | |||||
包含= 0x50, | |||||
不包含= 0x40 | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls.Enums | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
public enum PTS_DTS_Flags:byte | |||||
{ | |||||
all = 0xc0, | |||||
pts = 0x80, | |||||
dts = 0x40 | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls.Enums | |||||
{ | |||||
public enum StreamType:byte | |||||
{ | |||||
h264 = 0x1B, | |||||
aac = 0x0f, | |||||
mp3 = 0x03 | |||||
} | |||||
} |
@@ -0,0 +1,10 @@ | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
namespace JT1078.Hls.Formatters | |||||
{ | |||||
public interface ITSMessagePackFormatter | |||||
{ | |||||
void ToBuffer(ref TSMessagePackWriter writer); | |||||
} | |||||
} |
@@ -0,0 +1,163 @@ | |||||
using System; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Hls.MessagePack | |||||
{ | |||||
public ref partial struct TSMessagePackWriter | |||||
{ | |||||
private TSBufferWriter writer; | |||||
public TSMessagePackWriter(Span<byte> buffer) | |||||
{ | |||||
this.writer = new TSBufferWriter(buffer); | |||||
} | |||||
public byte[] FlushAndGetArray() | |||||
{ | |||||
return writer.Written.ToArray(); | |||||
} | |||||
public void WriteByte(byte value) | |||||
{ | |||||
var span = writer.Free; | |||||
span[0] = value; | |||||
writer.Advance(1); | |||||
} | |||||
public void WriteUInt16(ushort value) | |||||
{ | |||||
BinaryPrimitives.WriteUInt16BigEndian(writer.Free, value); | |||||
writer.Advance(2); | |||||
} | |||||
public void WriteInt32(int value) | |||||
{ | |||||
BinaryPrimitives.WriteInt32BigEndian(writer.Free, value); | |||||
writer.Advance(4); | |||||
} | |||||
public void WriteUInt64(ulong value) | |||||
{ | |||||
BinaryPrimitives.WriteUInt64BigEndian(writer.Free, value); | |||||
writer.Advance(8); | |||||
} | |||||
public void WriteUInt5(ulong value) | |||||
{ | |||||
writer.Free[0] = (byte)(value >> 32); | |||||
writer.Free[1] = (byte)(value >> 24); | |||||
writer.Free[2] = (byte)(value >> 16); | |||||
writer.Free[3] = (byte)(value >> 8); | |||||
writer.Free[4] = (byte)(value); | |||||
writer.Advance(5); | |||||
} | |||||
public void WriteUInt6(ulong value) | |||||
{ | |||||
writer.Free[0] = (byte)(value >> 40); | |||||
writer.Free[1] = (byte)(value >> 32); | |||||
writer.Free[2] = (byte)(value >> 24); | |||||
writer.Free[3] = (byte)(value >> 16); | |||||
writer.Free[4] = (byte)(value >> 8); | |||||
writer.Free[5] = (byte)(value); | |||||
writer.Advance(6); | |||||
} | |||||
public void WriteInt5(long value) | |||||
{ | |||||
writer.Free[0] = (byte)(value >> 32); | |||||
writer.Free[1] = (byte)(value >> 24); | |||||
writer.Free[2] = (byte)(value >> 16); | |||||
writer.Free[3] = (byte)(value >> 8); | |||||
writer.Free[4] = (byte)(value); | |||||
writer.Advance(5); | |||||
} | |||||
public void WriteInt6(long value) | |||||
{ | |||||
writer.Free[0] = (byte)(value >> 40); | |||||
writer.Free[1] = (byte)(value >> 32); | |||||
writer.Free[2] = (byte)(value >> 24); | |||||
writer.Free[3] = (byte)(value >> 16); | |||||
writer.Free[4] = (byte)(value >> 8); | |||||
writer.Free[5] = (byte)(value); | |||||
writer.Advance(6); | |||||
} | |||||
public void WriteUInt3(uint value) | |||||
{ | |||||
writer.Free[0] = (byte)(value >> 16); | |||||
writer.Free[1] = (byte)(value >> 8); | |||||
writer.Free[2] = (byte)(value); | |||||
writer.Advance(3); | |||||
} | |||||
public void WriteInt3(int value) | |||||
{ | |||||
writer.Free[0] = (byte)(value >> 16); | |||||
writer.Free[1] = (byte)(value >> 8); | |||||
writer.Free[2] = (byte)(value); | |||||
writer.Advance(3); | |||||
} | |||||
public void WriteUInt32(uint value) | |||||
{ | |||||
BinaryPrimitives.WriteUInt32BigEndian(writer.Free, value); | |||||
writer.Advance(4); | |||||
} | |||||
public void WriteArray(ReadOnlySpan<byte> src) | |||||
{ | |||||
src.CopyTo(writer.Free); | |||||
writer.Advance(src.Length); | |||||
} | |||||
public void Skip(int count, out int position) | |||||
{ | |||||
position = writer.WrittenCount; | |||||
byte[] tmp = new byte[count]; | |||||
tmp.CopyTo(writer.Free); | |||||
writer.Advance(count); | |||||
} | |||||
public void WriteCRC32() | |||||
{ | |||||
if (writer.WrittenCount < 1) | |||||
{ | |||||
throw new ArgumentOutOfRangeException($"Written<start:{writer.WrittenCount}>{1}"); | |||||
} | |||||
//从第1位开始 | |||||
var crcSpan = writer.Written.Slice(1); | |||||
uint crc = 0xFFFFFFFF; | |||||
for (int i = 0; i < crcSpan.Length; i++) | |||||
{ | |||||
crc = (crc << 8) ^ Util.crcTable[(crc >> 24) ^ crcSpan[i]]; | |||||
} | |||||
WriteUInt32(crc); | |||||
} | |||||
public void WriteCRC32(int start) | |||||
{ | |||||
if (writer.WrittenCount < start) | |||||
{ | |||||
throw new ArgumentOutOfRangeException($"Written<start:{writer.WrittenCount}>{1}"); | |||||
} | |||||
var crcSpan = writer.Written.Slice(start); | |||||
uint crc = 0xFFFFFFFF; | |||||
for (int i = 0; i < crcSpan.Length; i++) | |||||
{ | |||||
crc = (crc << 8) ^ Util.crcTable[(crc >> 24) ^ crcSpan[i]]; | |||||
} | |||||
WriteUInt32(crc); | |||||
} | |||||
public void WriteCRC32(int start, int end) | |||||
{ | |||||
if (start > end) | |||||
{ | |||||
throw new ArgumentOutOfRangeException($"start>end:{start}>{end}"); | |||||
} | |||||
var crcSpan = writer.Written.Slice(start, end); | |||||
uint crc = 0xFFFFFFFF; | |||||
for (int i = 0; i < crcSpan.Length; i++) | |||||
{ | |||||
crc = ((crc << 8) ^ Util.crcTable[(crc >> 8) ^ crcSpan[i]]); | |||||
} | |||||
WriteUInt32(crc); | |||||
} | |||||
public void WriteUInt16Return(ushort value, int position) | |||||
{ | |||||
BinaryPrimitives.WriteUInt16BigEndian(writer.Written.Slice(position, 2), value); | |||||
} | |||||
public void WriteByteReturn(byte value, int position) | |||||
{ | |||||
writer.Written[position] = value; | |||||
} | |||||
public int GetCurrentPosition() | |||||
{ | |||||
return writer.WrittenCount; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,81 @@ | |||||
using JT1078.Hls.Enums; | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class PES_Package: ITSMessagePackFormatter | |||||
{ | |||||
public static byte[] PESSTARCODE = new byte[] { 0x00, 0x00, 0x01 }; | |||||
/// <summary> | |||||
/// 开始码,固定为0x000001 | |||||
/// </summary> | |||||
public byte[] PESStartCode { get; set; } = PESSTARCODE; | |||||
/// <summary> | |||||
/// 音频取值(0xc0-0xdf),通常为0xc0 | |||||
/// 视频取值(0xe0-0xef),通常为0xe0 | |||||
/// </summary> | |||||
public byte StreamId { get; set; } | |||||
/// <summary> | |||||
/// 后面pes数据的长度,0表示长度不限制,只有视频数据长度会超过0xffff | |||||
/// </summary> | |||||
public ushort PESPacketLength { get; set; } | |||||
/// <summary> | |||||
/// 通常取值0x80,表示数据不加密、无优先级、备份的数据 | |||||
/// ISOIEC13818-1 120页 Table E-1 -- PES packet header example | |||||
/// </summary> | |||||
internal byte Flag1 { get; set; } = 0x80; | |||||
/// <summary> | |||||
/// 取值0x80表示只含有pts,取值0xc0表示含有pts和dts | |||||
/// ISOIEC13818-1 120页 Table E-1 -- PES packet header example | |||||
/// </summary> | |||||
public PTS_DTS_Flags PTS_DTS_Flag { get; set; } | |||||
/// <summary> | |||||
/// 根据PTS_DTS_Flag来判断后续长度 | |||||
/// 后面数据的长度,取值5或10 | |||||
/// </summary> | |||||
internal byte PESDataLength { get; set; } | |||||
/// <summary> | |||||
/// 5B | |||||
/// 33bit值 | |||||
/// </summary> | |||||
public long PTS { get; set; } | |||||
/// <summary> | |||||
/// 5B | |||||
/// 33bit值 | |||||
/// </summary> | |||||
public long DTS { get; set; } | |||||
/// <summary> | |||||
/// 音视频数据 | |||||
/// </summary> | |||||
public ES_Package Payload { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteArray(PESStartCode); | |||||
writer.WriteByte(StreamId); | |||||
writer.WriteUInt16(PESPacketLength); | |||||
writer.WriteByte(Flag1); | |||||
writer.WriteByte((byte)PTS_DTS_Flag); | |||||
if(PTS_DTS_Flag== PTS_DTS_Flags.all) | |||||
{ | |||||
writer.WriteByte(10); | |||||
writer.WriteInt5(PTS); | |||||
writer.WriteInt5(DTS); | |||||
} | |||||
else if(PTS_DTS_Flag == PTS_DTS_Flags.pts) | |||||
{ | |||||
writer.WriteByte(5); | |||||
writer.WriteInt5(PTS); | |||||
} | |||||
else if (PTS_DTS_Flag == PTS_DTS_Flags.dts) | |||||
{ | |||||
writer.WriteByte(5); | |||||
writer.WriteInt5(DTS); | |||||
} | |||||
Payload.ToBuffer(ref writer); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
using System.Buffers; | |||||
namespace JT1078.Hls | |||||
{ | |||||
internal static class TSArrayPool | |||||
{ | |||||
private readonly static ArrayPool<byte> ArrayPool; | |||||
static TSArrayPool() | |||||
{ | |||||
ArrayPool = ArrayPool<byte>.Create(); | |||||
} | |||||
public static byte[] Rent(int minimumLength) | |||||
{ | |||||
return ArrayPool.Rent(minimumLength); | |||||
} | |||||
public static void Return(byte[] array, bool clearArray = false) | |||||
{ | |||||
ArrayPool.Return(array, clearArray); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,58 @@ | |||||
using JT1078.Protocol.Enums; | |||||
using JT1078.Protocol.H264; | |||||
using JT1078.Protocol.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Runtime.CompilerServices; | |||||
using JT1078.Protocol; | |||||
using JT1078.Hls.MessagePack; | |||||
[assembly: InternalsVisibleTo("JT1078.Hls.Test")] | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class TSEncoder | |||||
{ | |||||
readonly H264Decoder h264Decoder = new H264Decoder(); | |||||
public byte[] Create(JT1078Package package, int minBufferSize = 65535) | |||||
{ | |||||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | |||||
try | |||||
{ | |||||
TS_Package tS_Package = new TS_Package(); | |||||
tS_Package.Header = new TS_Header(); | |||||
tS_Package.Header.PID = (ushort)package.GetKey().GetHashCode(); | |||||
tS_Package.Header.ContinuityCounter = (byte)package.SN; | |||||
tS_Package.Header.PayloadUnitStartIndicator = 1; | |||||
tS_Package.Header.Adaptation = new TS_AdaptationInfo(); | |||||
tS_Package.Payload = new PES_Package(); | |||||
TSMessagePackWriter messagePackReader = new TSMessagePackWriter(buffer); | |||||
var nalus = h264Decoder.ParseNALU(package); | |||||
if (nalus != null && nalus.Count > 0) | |||||
{ | |||||
var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 6); | |||||
var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 7); | |||||
var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 8); | |||||
nalus.Remove(sps); | |||||
nalus.Remove(pps); | |||||
nalus.Remove(sei); | |||||
foreach (var naln in nalus) | |||||
{ | |||||
} | |||||
} | |||||
return messagePackReader.FlushAndGetArray(); | |||||
} | |||||
finally | |||||
{ | |||||
TSArrayPool.Return(buffer); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,32 @@ | |||||
using JT1078.Hls.Enums; | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class TS_AdaptationInfo : ITSMessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// 取0x50表示包含PCR或0x40表示不包含PCR | |||||
/// 1B | |||||
/// </summary> | |||||
public PCRInclude PCRIncluded { get; set; } | |||||
/// <summary> | |||||
/// Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟STC(System Time Clock) | |||||
/// 5B | |||||
/// </summary> | |||||
public long PCR { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteByte((byte)PCRIncluded); | |||||
if (PCRIncluded== PCRInclude.包含) | |||||
{ | |||||
writer.WriteInt5(PCR); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,79 @@ | |||||
using JT1078.Hls.Enums; | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class TS_Header : ITSMessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// 同步字节,固定为0x47 | |||||
/// </summary> | |||||
internal byte SyncByte { get; set; } = 0x47; | |||||
/// <summary> | |||||
/// 传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0,这个字节算在adapt域长度内 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte TransportErrorIndicator { get; set; } = 0; | |||||
/// <summary> | |||||
/// 负载单元起始标示符,一个完整的数据包开始时标记为1 | |||||
/// 1bit | |||||
/// </summary> | |||||
public byte PayloadUnitStartIndicator { get; set; } = 1; | |||||
/// <summary> | |||||
/// 传输优先级,0为低优先级,1为高优先级,通常取0 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte TransportPriority { get; set; } = 0; | |||||
/// <summary> | |||||
/// pid值 | |||||
/// 13bit | |||||
/// </summary> | |||||
public ushort PID { get; set; } | |||||
/// <summary> | |||||
/// 传输优先级,0为低优先级,1为高优先级,通常取0 | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte TransportScramblingControl { get; set; } = 0; | |||||
/// <summary> | |||||
/// 是否包含自适应区,‘00’保留;‘01’为无自适应域,仅含有效负载;‘10’为仅含自适应域,无有效负载;‘11’为同时带有自适应域和有效负载。 | |||||
/// 2bit | |||||
/// </summary> | |||||
public AdaptationFieldControl AdaptationFieldControl { get; set; } | |||||
/// <summary> | |||||
/// 递增计数器,从0-f,起始值不一定取0,但必须是连续的 | |||||
/// 4bit | |||||
/// </summary> | |||||
public byte ContinuityCounter { get; set; } = 0; | |||||
/// <summary> | |||||
/// 自适应域长度,后面的字节数 | |||||
/// </summary> | |||||
public byte AdaptationLength { get; set; } | |||||
/// <summary> | |||||
/// 附加字段 | |||||
/// </summary> | |||||
public TS_AdaptationInfo Adaptation { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteByte(SyncByte); | |||||
//TransportErrorIndicator PayloadUnitStartIndicator TransportPriority PID | |||||
//0 1 0 0000 0000 0000 0 | |||||
writer.WriteUInt16((ushort)(0100_0000_0000_0000 | PID)); | |||||
writer.WriteByte((byte)((int)AdaptationFieldControl | ContinuityCounter)); | |||||
if (Adaptation != null) | |||||
{ | |||||
writer.Skip(1, out int AdaptationLengthPosition); | |||||
Adaptation.ToBuffer(ref writer); | |||||
writer.WriteByteReturn((byte)(writer.GetCurrentPosition() - AdaptationLengthPosition - 1), AdaptationLengthPosition); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteByte(0); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,106 @@ | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
/// <summary> | |||||
/// 格式节目关联表 | |||||
/// </summary> | |||||
public class TS_PAT_Package : ITSMessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// PAT表固定为0x00 | |||||
/// 8bit | |||||
/// </summary> | |||||
public byte TableId { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为二进制1 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte SectionSyntaxIndicator { get; set; } = 0x01; | |||||
/// <summary> | |||||
/// 固定为二进制0 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte Zero { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为二进制3 | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved1 { get; set; } = 0x03; | |||||
/// <summary> | |||||
/// 后面数据的长度 | |||||
/// 12bit | |||||
/// </summary> | |||||
public ushort SectionLength { get; set; } | |||||
/// <summary> | |||||
/// 传输流ID | |||||
/// 16bit | |||||
/// </summary> | |||||
internal ushort TransportStreamId { get; set; } = 0x0001; | |||||
/// <summary> | |||||
/// 固定为二进制3 | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved2 { get; set; } = 0x03; | |||||
/// <summary> | |||||
/// 版本号,固定为二进制00000,如果PAT有变化则版本号加1 | |||||
/// 5bit | |||||
/// </summary> | |||||
public byte VersionNumber { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为二进制1,表示这个PAT表可以用,如果为0则要等待下一个PAT表 | |||||
/// 1bit | |||||
/// </summary> | |||||
public byte CurrentNextIndicator { get; set; } = 1; | |||||
/// <summary> | |||||
/// 固定为0x00 | |||||
/// bit8 | |||||
/// </summary> | |||||
internal byte SectionNumber { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为0x00 | |||||
/// bit8 | |||||
/// </summary> | |||||
internal byte LastSectionNumber { get; set; } = 0x00; | |||||
public List<TS_PAT_Program> Programs { get; set; } | |||||
/// <summary> | |||||
/// 前面数据的CRC32校验码 | |||||
/// </summary> | |||||
public uint CRC32 { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteByte(TableId); | |||||
//SectionSyntaxIndicator Zero Reserved1 SectionLength | |||||
//1 0 11 0000 0000 0000 | |||||
//(ushort)(1011_0000_0000_0000 | SectionLength) | |||||
// todo: | |||||
//writer.WriteUInt16((ushort)(1011_0000_0000_0000 | SectionLength)); | |||||
writer.Skip(2, out int SectionLengthPosition); | |||||
writer.WriteUInt16(TransportStreamId); | |||||
//Reserved2 VersionNumber CurrentNextIndicator | |||||
//11 00000 1 | |||||
var a = Reserved2 & 0xC0; | |||||
var b = VersionNumber & 0x3E; | |||||
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); | |||||
} | |||||
} | |||||
writer.WriteUInt16Return((ushort)(1011_0000_0000_0000 | (ushort)(writer.GetCurrentPosition() - SectionLengthPosition - 2)), SectionLengthPosition); | |||||
writer.WriteCRC32(SectionLengthPosition); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,34 @@ | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class TS_PAT_Program : ITSMessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// 节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PMT | |||||
/// 16bit | |||||
/// </summary> | |||||
public ushort ProgramNumber { get; set; } | |||||
/// <summary> | |||||
/// 固定为二进制111(7) | |||||
/// 1110_0000_0000_0000 | |||||
/// 3bit | |||||
/// </summary> | |||||
internal byte Reserved1 { get; set; } = 0x07; | |||||
/// <summary> | |||||
/// 节目号对应内容的PID值 | |||||
/// 13bit | |||||
/// </summary> | |||||
public ushort PID { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteUInt16(ProgramNumber); | |||||
writer.WriteUInt16((ushort)(1110_0000_0000_0000 | PID)); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,47 @@ | |||||
using JT1078.Hls.Enums; | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class TS_PMT_Component: ITSMessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// 流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03 | |||||
/// 8bit | |||||
/// </summary> | |||||
public StreamType StreamType { get; set; } | |||||
/// <summary> | |||||
/// 固定为二进制111(7) | |||||
/// 0111_0000_0000_0000 | |||||
/// 3bit | |||||
/// </summary> | |||||
internal byte Reserved1 { get; set; } = 0x07; | |||||
/// <summary> | |||||
/// 与StreamType对应的PID | |||||
/// 13bit | |||||
/// </summary> | |||||
public ushort ElementaryPID { get; set; } | |||||
/// <summary> | |||||
/// 固定为二进制1111(15) | |||||
/// 1111_0000_0000_0000 | |||||
/// 4bit | |||||
/// </summary> | |||||
internal byte Reserved2 { get; set; } = 0x0F; | |||||
/// <summary> | |||||
/// 描述信息,指定为0x000表示没有 | |||||
/// 12bit | |||||
/// </summary> | |||||
internal ushort ESInfoLength { get; set; } = 0x000; | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteByte((byte)StreamType); | |||||
writer.WriteUInt16((ushort)(0111_0000_0000_0000| ElementaryPID)); | |||||
writer.WriteUInt16((ushort)(1111_0000_0000_0000| ESInfoLength)); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,129 @@ | |||||
using JT1078.Hls.Formatters; | |||||
using JT1078.Hls.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
/// <summary> | |||||
/// 格式节目映射表 | |||||
/// </summary> | |||||
public class TS_PMT_Package : ITSMessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// PMT表取值随意 | |||||
/// 8bit | |||||
/// </summary> | |||||
public byte TableId { get; set; } = 0xFF; | |||||
/// <summary> | |||||
/// 固定为二进制1 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte SectionSyntaxIndicator { get; set; } = 0x01; | |||||
/// <summary> | |||||
/// 固定为二进制0 | |||||
/// 1bit | |||||
/// </summary> | |||||
internal byte Zero { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为二进制3 | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved1 { get; set; } = 0x03; | |||||
/// <summary> | |||||
/// 后面数据的长度 | |||||
/// 12bit | |||||
/// </summary> | |||||
public ushort SectionLength { get; set; } | |||||
/// <summary> | |||||
/// 频道号码,表示当前的PMT关联到的频道,取值0x0001 | |||||
/// 16bit | |||||
/// </summary> | |||||
public ushort ProgramNumber { get; set; } = 0x0001; | |||||
/// <summary> | |||||
/// 固定为二进制3 | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved2 { get; set; } = 0x03; | |||||
/// <summary> | |||||
/// 版本号,固定为二进制00000,如果PAT有变化则版本号加1 | |||||
/// 5bit | |||||
/// </summary> | |||||
public byte VersionNumber { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为二进制1,表示这个PAT表可以用,如果为0则要等待下一个PAT表 | |||||
/// 1bit | |||||
/// </summary> | |||||
public byte CurrentNextIndicator { get; set; } = 1; | |||||
/// <summary> | |||||
/// 固定为0x00 | |||||
/// bit8 | |||||
/// </summary> | |||||
internal byte SectionNumber { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为0x00 | |||||
/// bit8 | |||||
/// </summary> | |||||
internal byte LastSectionNumber { get; set; } = 0x00; | |||||
/// <summary> | |||||
/// 固定为二进制111(7) | |||||
/// 2bit | |||||
/// </summary> | |||||
internal byte Reserved3 { get; set; } = 0x07; | |||||
/// <summary> | |||||
/// PCR(节目参考时钟)所在TS分组的PID,指定为视频PID | |||||
/// 13bit | |||||
/// </summary> | |||||
public ushort PCR_PID { get; set; } | |||||
/// <summary> | |||||
/// 固定为二进制1111(F) | |||||
/// 4bit | |||||
/// </summary> | |||||
internal byte Reserved4 { get; set; } = 0x0F; | |||||
/// <summary> | |||||
/// 节目描述信息,指定为0x000表示没有 | |||||
/// 12bit | |||||
/// </summary> | |||||
public ushort ProgramInfoLength { get; set; } | |||||
public List<TS_PMT_Component> Components { get; set; } | |||||
/// <summary> | |||||
/// 前面数据的CRC32校验码 | |||||
/// </summary> | |||||
public uint CRC32 { get; set; } | |||||
public void ToBuffer(ref TSMessagePackWriter writer) | |||||
{ | |||||
writer.WriteByte(TableId); | |||||
//SectionSyntaxIndicator Zero Reserved1 SectionLength | |||||
//1 0 11 0000 0000 0000 | |||||
//(ushort)(1011_0000_0000_0000 | SectionLength) | |||||
// todo: | |||||
//writer.WriteUInt16((ushort)(1011_0000_0000_0000 | SectionLength)); | |||||
writer.Skip(2, out int SectionLengthPosition); | |||||
writer.WriteUInt16(ProgramNumber); | |||||
//Reserved2 VersionNumber CurrentNextIndicator | |||||
//11 00000 1 | |||||
var a = Reserved2 & 0xC0; | |||||
var b = VersionNumber & 0x3E; | |||||
var c=(byte)(a | b | CurrentNextIndicator); | |||||
writer.WriteByte(c); | |||||
writer.WriteByte(SectionNumber); | |||||
writer.WriteByte(LastSectionNumber); | |||||
//Reserved3 PCR_PID | |||||
//111 0000 0000 0000 0 | |||||
writer.WriteUInt16((ushort)(0111_0000_0000_0000 | PCR_PID)); | |||||
//Reserved4 ProgramInfoLength | |||||
//1111 0000 0000 0000 | |||||
writer.WriteUInt16((ushort)(1111_0000_0000_0000 | ProgramInfoLength)); | |||||
if (Components != null) | |||||
{ | |||||
foreach(var component in Components) | |||||
{ | |||||
component.ToBuffer(ref writer); | |||||
} | |||||
} | |||||
writer.WriteUInt16Return((ushort)(1011_0000_0000_0000 | (ushort)(writer.GetCurrentPosition() - SectionLengthPosition - 2)), SectionLengthPosition); | |||||
writer.WriteCRC32(SectionLengthPosition); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
using JT1078.Protocol.H264; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
public class TS_Package | |||||
{ | |||||
public TS_Header Header { get; set; } | |||||
public PES_Package Payload { get; set; } | |||||
/// <summary> | |||||
/// 填充字节,取值0xff | |||||
/// </summary> | |||||
public byte[] Fill { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Hls | |||||
{ | |||||
class Util | |||||
{ | |||||
public static uint[] crcTable = | |||||
{ | |||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, | |||||
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, | |||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, | |||||
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, | |||||
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, | |||||
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, | |||||
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, | |||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, | |||||
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, | |||||
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, | |||||
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, | |||||
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, | |||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, | |||||
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, | |||||
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, | |||||
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, | |||||
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, | |||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, | |||||
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, | |||||
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, | |||||
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, | |||||
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, | |||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, | |||||
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, | |||||
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, | |||||
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, | |||||
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, | |||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, | |||||
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, | |||||
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, | |||||
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, | |||||
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 | |||||
}; | |||||
} | |||||
} |
@@ -34,6 +34,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Flv.Benchmark", "JT1 | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Hls", "JT1078.Hls\JT1078.Hls.csproj", "{C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Hls", "JT1078.Hls\JT1078.Hls.csproj", "{C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}" | ||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Hls.Test", "JT1078.Hls.Test\JT1078.Hls.Test.csproj", "{5564C20B-BFF4-4A2A-BDF2-C7427E93E993}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -88,6 +90,10 @@ Global | |||||
{C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}.Debug|Any CPU.Build.0 = Debug|Any CPU | {C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}.Release|Any CPU.ActiveCfg = Release|Any CPU | {C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}.Release|Any CPU.Build.0 = Release|Any CPU | {C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{5564C20B-BFF4-4A2A-BDF2-C7427E93E993}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{5564C20B-BFF4-4A2A-BDF2-C7427E93E993}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{5564C20B-BFF4-4A2A-BDF2-C7427E93E993}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{5564C20B-BFF4-4A2A-BDF2-C7427E93E993}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
@@ -98,6 +104,7 @@ Global | |||||
{9ADD82F9-E0B2-4263-8573-151F673BB33F} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {9ADD82F9-E0B2-4263-8573-151F673BB33F} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{9DB37370-AC73-434B-9CE2-6659321858C8} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {9DB37370-AC73-434B-9CE2-6659321858C8} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {D13FE092-1D11-4545-A322-9F06BCDAC0FD} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{5564C20B-BFF4-4A2A-BDF2-C7427E93E993} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ExtensibilityGlobals) = postSolution | GlobalSection(ExtensibilityGlobals) = postSolution | ||||
SolutionGuid = {FAE1656D-226F-4B4B-8C33-615D7E632B26} | SolutionGuid = {FAE1656D-226F-4B4B-8C33-615D7E632B26} | ||||