@@ -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 | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Hls", "JT1078.Hls\JT1078.Hls.csproj", "{C98AD4CE-D7F5-4F7F-BAB5-D1AD50DDF14F}" | |||
EndProject | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Hls.Test", "JT1078.Hls.Test\JT1078.Hls.Test.csproj", "{5564C20B-BFF4-4A2A-BDF2-C7427E93E993}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
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}.Release|Any CPU.ActiveCfg = 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 | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
@@ -98,6 +104,7 @@ Global | |||
{9ADD82F9-E0B2-4263-8573-151F673BB33F} = {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} | |||
{5564C20B-BFF4-4A2A-BDF2-C7427E93E993} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | |||
EndGlobalSection | |||
GlobalSection(ExtensibilityGlobals) = postSolution | |||
SolutionGuid = {FAE1656D-226F-4B4B-8C33-615D7E632B26} | |||