@@ -12,10 +12,14 @@ | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\JT1078.Flv\JT1078.Flv.csproj" /> | |||
<ProjectReference Include="..\JT1078.FMp4\JT1078.FMp4.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="..\..\doc\video\jt1078_1.txt" Link="jt1078_1.txt"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
<None Include="..\..\doc\video\jt1078_3.txt" Link="jt1078_3.txt"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
</ItemGroup> | |||
</Project> |
@@ -0,0 +1,132 @@ | |||
using BenchmarkDotNet.Attributes; | |||
using BenchmarkDotNet.Configs; | |||
using BenchmarkDotNet.Environments; | |||
using BenchmarkDotNet.Jobs; | |||
using BenchmarkDotNet.Toolchains.CsProj; | |||
using JT1078.Flv; | |||
using JT1078.Flv.MessagePack; | |||
using JT1078.FMp4; | |||
using JT1078.Protocol; | |||
using JT1078.Protocol.H264; | |||
using JT1078.Protocol.MessagePack; | |||
using JT808.Protocol.Extensions; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
namespace JT1078.AV.Benchmark | |||
{ | |||
[Config(typeof(JT1078AVEncoderConfig))] | |||
[MarkdownExporterAttribute.GitHub] | |||
[MemoryDiagnoser] | |||
public class JT1078AVEncoderContext | |||
{ | |||
JT1078Package Package; | |||
List<H264NALU> H264NALUs; | |||
List<H264NALU> FMp4H264NALUs; | |||
H264NALU SPSNALu; | |||
H264Decoder h264Decoder = new H264Decoder(); | |||
FlvEncoder flvEncoder = new FlvEncoder(); | |||
FMp4Encoder fmp4Encoder = new FMp4Encoder(); | |||
[Params(100, 10000, 100000)] | |||
public int N; | |||
[GlobalSetup] | |||
public void Setup() | |||
{ | |||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078_1.txt")); | |||
foreach (var line in lines) | |||
{ | |||
var data = line.Split(','); | |||
var bytes = data[6].ToHexBytes(); | |||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||
Package = JT1078Serializer.Merge(package); | |||
} | |||
H264NALUs = h264Decoder.ParseNALU(Package); | |||
SPSNALu = H264NALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS); | |||
SPSNALu.RawData = h264Decoder.DiscardEmulationPreventionBytes(SPSNALu.RawData); | |||
List<JT1078Package> packages = new List<JT1078Package>(); | |||
var lines3 = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_3.txt")); | |||
int mergeBodyLength = 0; | |||
foreach (var line in lines3) | |||
{ | |||
var data = line.Split(','); | |||
var bytes = data[6].ToHexBytes(); | |||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||
mergeBodyLength += package.DataBodyLength; | |||
var packageMerge = JT1078Serializer.Merge(package); | |||
if (packageMerge != null) | |||
{ | |||
packages.Add(packageMerge); | |||
} | |||
} | |||
List<H264NALU> nalus = new List<H264NALU>(); | |||
bool segmentFlag = false; | |||
foreach (var package in packages) | |||
{ | |||
if (segmentFlag) | |||
{ | |||
break; | |||
} | |||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||
foreach (var nalu in h264NALUs) | |||
{ | |||
if (nalu.Slice) | |||
{ | |||
//H264 NALU slice first_mb_in_slice | |||
nalus.Add(nalu); | |||
} | |||
else | |||
{ | |||
if (nalus.Count > 0) | |||
{ | |||
FMp4H264NALUs = new List<H264NALU>(nalus); | |||
segmentFlag = true; | |||
nalus.Clear(); | |||
} | |||
nalus.Add(nalu); | |||
} | |||
} | |||
} | |||
} | |||
[Benchmark(Description = "EXPGolombReader")] | |||
public void EXPGolombReaderTest() | |||
{ | |||
for (var i = 0; i < N; i++) | |||
{ | |||
ExpGolombReader h264GolombReader = new ExpGolombReader(SPSNALu.RawData); | |||
h264GolombReader.ReadSPS(); | |||
} | |||
} | |||
[Benchmark(Description = "H264Decoder")] | |||
public void H264Decoder() | |||
{ | |||
for (var i = 0; i < N; i++) | |||
{ | |||
var nalus = h264Decoder.ParseNALU(Package); | |||
} | |||
} | |||
[Benchmark(Description = "FMp4Encoder")] | |||
public void FMp4Encoder() | |||
{ | |||
for (var i = 0; i < N; i++) | |||
{ | |||
var buffer = fmp4Encoder.EncoderOtherVideoBox(FMp4H264NALUs); | |||
} | |||
} | |||
} | |||
public class JT1078AVEncoderConfig : ManualConfig | |||
{ | |||
public JT1078AVEncoderConfig() | |||
{ | |||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp50).WithPlatform(Platform.AnyCpu)); | |||
} | |||
} | |||
} |
@@ -1,76 +0,0 @@ | |||
using BenchmarkDotNet.Attributes; | |||
using BenchmarkDotNet.Configs; | |||
using BenchmarkDotNet.Environments; | |||
using BenchmarkDotNet.Jobs; | |||
using BenchmarkDotNet.Toolchains.CsProj; | |||
using JT1078.Flv; | |||
using JT1078.Flv.MessagePack; | |||
using JT1078.Protocol; | |||
using JT1078.Protocol.H264; | |||
using JT1078.Protocol.MessagePack; | |||
using JT808.Protocol.Extensions; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
namespace JT1078.AV.Benchmark | |||
{ | |||
[Config(typeof(JT1078FlvEncoderConfig))] | |||
[MarkdownExporterAttribute.GitHub] | |||
[MemoryDiagnoser] | |||
public class JT1078FlvEncoderContext | |||
{ | |||
JT1078Package Package; | |||
List<H264NALU> H264NALUs; | |||
H264NALU SPSNALu; | |||
H264Decoder h264Decoder = new H264Decoder(); | |||
FlvEncoder flvEncoder = new FlvEncoder(); | |||
[Params(100, 10000, 100000)] | |||
public int N; | |||
[GlobalSetup] | |||
public void Setup() | |||
{ | |||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078_1.txt")); | |||
foreach (var line in lines) | |||
{ | |||
var data = line.Split(','); | |||
var bytes = data[6].ToHexBytes(); | |||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||
Package = JT1078Serializer.Merge(package); | |||
} | |||
H264NALUs = h264Decoder.ParseNALU(Package); | |||
SPSNALu = H264NALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS); | |||
SPSNALu.RawData = h264Decoder.DiscardEmulationPreventionBytes(SPSNALu.RawData); | |||
} | |||
[Benchmark(Description = "EXPGolombReader")] | |||
public void EXPGolombReaderTest() | |||
{ | |||
for (var i = 0; i < N; i++) | |||
{ | |||
ExpGolombReader h264GolombReader = new ExpGolombReader(SPSNALu.RawData); | |||
h264GolombReader.ReadSPS(); | |||
} | |||
} | |||
[Benchmark(Description = "H264Decoder")] | |||
public void H264Decoder() | |||
{ | |||
for (var i = 0; i < N; i++) | |||
{ | |||
var nalus = h264Decoder.ParseNALU(Package); | |||
} | |||
} | |||
} | |||
public class JT1078FlvEncoderConfig : ManualConfig | |||
{ | |||
public JT1078FlvEncoderConfig() | |||
{ | |||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp50).WithPlatform(Platform.AnyCpu)); | |||
} | |||
} | |||
} |
@@ -8,7 +8,7 @@ namespace JT1078.AV.Benchmark | |||
{ | |||
static void Main(string[] args) | |||
{ | |||
Summary summary = BenchmarkRunner.Run<JT1078FlvEncoderContext>(); | |||
Summary summary = BenchmarkRunner.Run<JT1078AVEncoderContext>(); | |||
} | |||
} | |||
} |
@@ -482,53 +482,6 @@ namespace JT1078.FMp4.Test | |||
fileStream.Close(); | |||
} | |||
[Fact] | |||
public void Test5() | |||
{ | |||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | |||
H264Decoder h264Decoder = new H264Decoder(); | |||
var packages = ParseNALUTests(); | |||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_6.mp4"); | |||
if (File.Exists(filepath)) | |||
{ | |||
File.Delete(filepath); | |||
} | |||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||
var ftyp = fMp4Encoder.EncoderFtypBox(); | |||
fileStream.Write(ftyp); | |||
var iNalus = h264Decoder.ParseNALU(packages[0]); | |||
//判断第一帧是否关键帧 | |||
var moov = fMp4Encoder.EncoderMoovBox( | |||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | |||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | |||
fileStream.Write(moov); | |||
List<H264NALU> nalus = new List<H264NALU>(); | |||
foreach (var package in packages) | |||
{ | |||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||
if(package.Label3.DataType== Protocol.Enums.JT1078DataType.视频I帧) | |||
{ | |||
if (nalus.Count > 0) | |||
{ | |||
var otherBuffer = fMp4Encoder.EncoderOtherVideoBox(nalus); | |||
fileStream.Write(otherBuffer); | |||
nalus.Clear(); | |||
} | |||
} | |||
nalus = nalus.Concat(h264NALUs).ToList(); | |||
} | |||
if (nalus.Count > 0) | |||
{ | |||
var otherBuffer = fMp4Encoder.EncoderOtherVideoBox(nalus); | |||
fileStream.Write(otherBuffer); | |||
nalus.Clear(); | |||
} | |||
fileStream.Close(); | |||
} | |||
[Fact] | |||
public void tkhd_width_height_test() | |||
{ | |||
@@ -30,6 +30,24 @@ namespace JT1078.FMp4 | |||
/// </summary> | |||
public class FMp4Encoder | |||
{ | |||
Dictionary<string, TrackInfo> TrackInfos; | |||
const uint DefaultSampleDuration = 48000u; | |||
const uint DefaultSampleFlags = 0x1010000; | |||
const uint FirstSampleFlags = 33554432; | |||
const uint TfhdFlags = 0x2003a; | |||
const uint TrunFlags = 0x205; | |||
const uint SampleDescriptionIndex = 1; | |||
const uint TrackID = 1; | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public FMp4Encoder() | |||
{ | |||
TrackInfos = new Dictionary<string, TrackInfo>(StringComparer.OrdinalIgnoreCase); | |||
} | |||
/// <summary> | |||
/// 编码ftyp盒子 | |||
/// </summary> | |||
@@ -66,7 +84,7 @@ namespace JT1078.FMp4 | |||
/// <returns></returns> | |||
public byte[] EncoderMoovBox(in H264NALU sps, in H264NALU pps) | |||
{ | |||
byte[] buffer = FMp4ArrayPool.Rent(sps.RawData.Length+ pps.RawData.Length + 1024); | |||
byte[] buffer = FMp4ArrayPool.Rent(sps.RawData.Length + pps.RawData.Length + 1024); | |||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||
try | |||
{ | |||
@@ -84,7 +102,7 @@ namespace JT1078.FMp4 | |||
movieBox.TrackBox.TrackHeaderBox = new TrackHeaderBox(0, 3); | |||
movieBox.TrackBox.TrackHeaderBox.CreationTime = 0; | |||
movieBox.TrackBox.TrackHeaderBox.ModificationTime = 0; | |||
movieBox.TrackBox.TrackHeaderBox.TrackID = 1; | |||
movieBox.TrackBox.TrackHeaderBox.TrackID = TrackID; | |||
movieBox.TrackBox.TrackHeaderBox.Duration = 0; | |||
movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false; | |||
movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width; | |||
@@ -126,8 +144,8 @@ namespace JT1078.FMp4 | |||
movieBox.MovieExtendsBox = new MovieExtendsBox(); | |||
movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>(); | |||
TrackExtendsBox trex = new TrackExtendsBox(); | |||
trex.TrackID = 1; | |||
trex.DefaultSampleDescriptionIndex = 1; | |||
trex.TrackID = TrackID; | |||
trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex; | |||
trex.DefaultSampleDuration = 0; | |||
trex.DefaultSampleSize = 0; | |||
trex.DefaultSampleFlags = 0; | |||
@@ -142,13 +160,11 @@ namespace JT1078.FMp4 | |||
} | |||
} | |||
uint sn = 1; | |||
/// <summary> | |||
/// 编码其他视频数据盒子 | |||
/// </summary> | |||
/// <returns></returns> | |||
public byte[] EncoderOtherVideoBox(List<H264NALU> nalus) | |||
public byte[] EncoderOtherVideoBox(in List<H264NALU> nalus) | |||
{ | |||
byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.RawData.Length + s.StartCodePrefix.Length) + 4096); | |||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||
@@ -158,9 +174,14 @@ namespace JT1078.FMp4 | |||
List<byte[]> rawdatas = new List<byte[]>(); | |||
uint iSize = 0; | |||
ulong lastTimestamp = 0; | |||
for (var i=0; i<nalus.Count;i++ ) | |||
string key = string.Empty; | |||
for (var i = 0; i < nalus.Count; i++) | |||
{ | |||
var nalu = nalus[i]; | |||
if (string.IsNullOrEmpty(key)) | |||
{ | |||
key = nalu.GetKey(); | |||
} | |||
rawdatas.Add(nalu.RawData); | |||
if (nalu.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||
{ | |||
@@ -181,32 +202,44 @@ namespace JT1078.FMp4 | |||
SampleSize = (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length), | |||
}); | |||
} | |||
if(i== (nalus.Count - 1)) | |||
if (i == (nalus.Count - 1)) | |||
{ | |||
lastTimestamp = nalu.Timestamp; | |||
} | |||
} | |||
if (TrackInfos.TryGetValue(key, out TrackInfo trackInfo)) | |||
{ | |||
if (trackInfo.SN == uint.MaxValue) | |||
{ | |||
trackInfo.SN = 1; | |||
} | |||
trackInfo.SN++; | |||
} | |||
else | |||
{ | |||
trackInfo = new TrackInfo { SN = 1, DTS = 0 }; | |||
TrackInfos.Add(key, trackInfo); | |||
} | |||
var movieFragmentBox = new MovieFragmentBox(); | |||
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | |||
//todo:SequenceNumber | |||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = sn++; | |||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN; | |||
movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x2003a); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = 1; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = 0x1010000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = lastTimestamp * 1000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.DTS; | |||
trackInfo.DTS += (ulong)(truns.Count * DefaultSampleDuration); | |||
TrackInfos[key] = trackInfo; | |||
//trun | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x205); | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 33554432; | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: TrunFlags); | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FirstSampleFlags; | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns; | |||
movieFragmentBox.ToBuffer(ref writer); | |||
var mediaDataBox = new MediaDataBox(); | |||
mediaDataBox.Data = rawdatas; | |||
mediaDataBox.ToBuffer(ref writer); | |||
@@ -220,64 +253,10 @@ namespace JT1078.FMp4 | |||
} | |||
} | |||
/// <summary> | |||
/// 编码其他视频数据盒子 | |||
/// </summary> | |||
/// <returns></returns> | |||
public byte[] EncoderOtherVideoBox(List<H264NALU> nalus,List<byte[]>samples, List<uint> sampleSizes,uint firstSize,ulong lastTimestamp) | |||
struct TrackInfo | |||
{ | |||
byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.RawData.Length + s.StartCodePrefix.Length) + 4096); | |||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||
try | |||
{ | |||
var lastNalu = nalus.Last(); | |||
int iSize = nalus.Where(w => w.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||
.Sum(s => s.RawData.Length + s.StartCodePrefix.Length); | |||
List<int> sizes = new List<int>(); | |||
sizes.Add(iSize); | |||
sizes = sizes.Concat(nalus.Where(w => w.DataType != Protocol.Enums.JT1078DataType.视频I帧) | |||
.Select(s => s.RawData.Length + s.StartCodePrefix.Length).ToList()) | |||
.ToList(); | |||
var movieFragmentBox = new MovieFragmentBox(); | |||
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | |||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = sn++; | |||
movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x2003a); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = 1; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = firstSize; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = 0x1010000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = lastTimestamp * 1000; | |||
//trun | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x205); | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 33554432; | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List<TrackRunBox.TrackRunInfo>(); | |||
foreach (var size in sizes) | |||
{ | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo() | |||
{ | |||
SampleSize = (uint)size, | |||
}); | |||
} | |||
movieFragmentBox.ToBuffer(ref writer); | |||
var mediaDataBox = new MediaDataBox(); | |||
mediaDataBox.Data = nalus.Select(s => s.RawData).ToList(); | |||
mediaDataBox.ToBuffer(ref writer); | |||
var data = writer.FlushAndGetArray(); | |||
return data; | |||
} | |||
finally | |||
{ | |||
FMp4ArrayPool.Return(buffer); | |||
} | |||
public uint SN { get; set; } | |||
public ulong DTS { get; set; } | |||
} | |||
} | |||
} |
@@ -27,9 +27,7 @@ | |||
<PackagePath></PackagePath> | |||
</None> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | |||
</ItemGroup> | |||
@@ -1320,6 +1320,11 @@ | |||
ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.#ctor"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderFtypBox"> | |||
<summary> | |||
编码ftyp盒子 | |||
@@ -1332,13 +1337,7 @@ | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderOtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU})"> | |||
<summary> | |||
编码其他视频数据盒子 | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderOtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU},System.Collections.Generic.List{System.Byte[]},System.Collections.Generic.List{System.UInt32},System.UInt32,System.UInt64)"> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderOtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU}@)"> | |||
<summary> | |||
编码其他视频数据盒子 | |||
</summary> | |||
@@ -40,6 +40,9 @@ namespace JT1078.Flv | |||
readonly H264Decoder h264Decoder; | |||
readonly AudioCodecFactory audioCodecFactory; | |||
//public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false) | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public FlvEncoder() | |||
{ | |||
audioCodecFactory = new AudioCodecFactory(); | |||
@@ -181,6 +181,11 @@ | |||
4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Flv.FlvEncoder.#ctor"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"> | |||
<summary> | |||
编码flv头 | |||
@@ -90,13 +90,13 @@ namespace JT1078.Hls.Test | |||
JT1078Package fullpackage = JT1078Serializer.Merge(package); | |||
if (fullpackage != null) | |||
{ | |||
var sdt = tSEncoder.CreateSDT(fullpackage); | |||
var sdt = tSEncoder.CreateSDT(); | |||
string sdtHEX = sdt.ToHexString(); | |||
fileStream.Write(sdt); | |||
var pat = tSEncoder.CreatePAT(fullpackage); | |||
var pat = tSEncoder.CreatePAT(); | |||
string patHEX = pat.ToHexString(); | |||
fileStream.Write(pat); | |||
var pmt = tSEncoder.CreatePMT(fullpackage); | |||
var pmt = tSEncoder.CreatePMT(); | |||
fileStream.Write(pmt); | |||
var pes = tSEncoder.CreatePES(fullpackage); | |||
fileStream.Write(pes); | |||
@@ -139,13 +139,13 @@ namespace JT1078.Hls.Test | |||
{ | |||
if (isNeedFirstHeadler) | |||
{ | |||
var sdt = tSEncoder.CreateSDT(fullpackage); | |||
var sdt = tSEncoder.CreateSDT(); | |||
string sdtHEX = sdt.ToHexString(); | |||
fileStream.Write(sdt); | |||
var pat = tSEncoder.CreatePAT(fullpackage); | |||
var pat = tSEncoder.CreatePAT(); | |||
string patHEX = pat.ToHexString(); | |||
fileStream.Write(pat); | |||
var pmt = tSEncoder.CreatePMT(fullpackage); | |||
var pmt = tSEncoder.CreatePMT(); | |||
fileStream.Write(pmt); | |||
var pes = tSEncoder.CreatePES(fullpackage, 18888); | |||
fileStream.Write(pes); | |||
@@ -14,7 +14,7 @@ | |||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | |||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | |||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||
<Version>1.1.0.0</Version> | |||
<Version>1.1.0-preview3</Version> | |||
<SignAssembly>false</SignAssembly> | |||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||
@@ -96,6 +96,19 @@ | |||
m3u8文件管理 | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Hls.M3U8FileManage.#ctor(JT1078.Hls.Options.M3U8Option)"> | |||
<summary> | |||
</summary> | |||
<param name="m3U8Option"></param> | |||
</member> | |||
<member name="M:JT1078.Hls.M3U8FileManage.#ctor(JT1078.Hls.Options.M3U8Option,JT1078.Hls.TSEncoder)"> | |||
<summary> | |||
</summary> | |||
<param name="m3U8Option"></param> | |||
<param name="tSEncoder"></param> | |||
</member> | |||
<member name="M:JT1078.Hls.M3U8FileManage.CreateTsData(JT1078.Protocol.JT1078Package)"> | |||
<summary> | |||
生成ts和m3u8文件 | |||
@@ -132,6 +145,12 @@ | |||
<param name="key">终端号_通道号(用作目录)</param> | |||
<param name="data">文件内容</param> | |||
</member> | |||
<member name="M:JT1078.Hls.M3U8FileManage.AppendM3U8End"> | |||
<summary> | |||
添加结束标识 | |||
直播流用不到 | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Hls.M3U8FileManage.Clear(System.String,System.Int32)"> | |||
<summary> | |||
停止观看直播时清零数据 | |||
@@ -273,6 +292,11 @@ | |||
4.PES | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Hls.TSEncoder.#ctor"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.Hls.TS_AdaptationInfo.PCRIncluded"> | |||
<summary> | |||
取0x50表示包含PCR或0x40表示不包含PCR | |||
@@ -19,15 +19,28 @@ namespace JT1078.Hls | |||
public class M3U8FileManage | |||
{ | |||
private TSEncoder tSEncoder; | |||
public readonly M3U8Option m3U8Option; | |||
private M3U8Option m3U8Option; | |||
ConcurrentDictionary<string, TsFileInfo> curTsFileInfoDic = new ConcurrentDictionary<string, TsFileInfo>();//当前文件信息 | |||
ConcurrentDictionary<string, Queue<TsFileInfo>> tsFileInfoQueueDic = new ConcurrentDictionary<string, Queue<TsFileInfo>>(); | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="m3U8Option"></param> | |||
public M3U8FileManage(M3U8Option m3U8Option):this(m3U8Option, new TSEncoder()) | |||
{ | |||
} | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="m3U8Option"></param> | |||
/// <param name="tSEncoder"></param> | |||
public M3U8FileManage(M3U8Option m3U8Option, TSEncoder tSEncoder) | |||
{ | |||
this.tSEncoder = tSEncoder; | |||
this.m3U8Option = m3U8Option; | |||
} | |||
/// <summary> | |||
/// 生成ts和m3u8文件 | |||
/// </summary> | |||
@@ -35,8 +48,8 @@ namespace JT1078.Hls | |||
public void CreateTsData(JT1078Package jt1078Package) | |||
{ | |||
string key = jt1078Package.GetKey(); | |||
string hlsFileDirectory = m3U8Option.HlsFileDirectory; | |||
string m3u8FileName = Path.Combine(hlsFileDirectory, key, m3U8Option.M3U8FileName); | |||
//string hlsFileDirectory = m3U8Option.HlsFileDirectory; | |||
//string m3u8FileName = Path.Combine(hlsFileDirectory, key, m3U8Option.M3U8FileName); | |||
var buff = TSArrayPool.Rent(jt1078Package.Bodies.Length + 1024); | |||
TSMessagePackWriter tSMessagePackWriter = new TSMessagePackWriter(buff); | |||
try | |||
@@ -62,11 +75,11 @@ namespace JT1078.Hls | |||
curTsFileInfo.IsCreateTsFile = false; | |||
curTsFileInfo.TsFirst1078PackageTimeStamp = jt1078Package.Timestamp; | |||
curTsFileInfo.FileName = $"{curTsFileInfo.TsFileSerialNo}.ts"; | |||
var sdt = tSEncoder.CreateSDT(jt1078Package); | |||
var sdt = tSEncoder.CreateSDT(); | |||
tSMessagePackWriter.WriteArray(sdt); | |||
var pat = tSEncoder.CreatePAT(jt1078Package); | |||
var pat = tSEncoder.CreatePAT(); | |||
tSMessagePackWriter.WriteArray(pat); | |||
var pmt = tSEncoder.CreatePMT(jt1078Package); | |||
var pmt = tSEncoder.CreatePMT(); | |||
tSMessagePackWriter.WriteArray(pmt); | |||
var pes = tSEncoder.CreatePES(jt1078Package); | |||
tSMessagePackWriter.WriteArray(pes); | |||
@@ -170,17 +183,17 @@ namespace JT1078.Hls | |||
} | |||
} | |||
///// <summary> | |||
///// 添加结束标识 | |||
///// 直播流用不到 | |||
///// </summary> | |||
///// <param name="filepath"></param> | |||
////public void AppendM3U8End() | |||
////{ | |||
//// StringBuilder sb = new StringBuilder(); | |||
//// sb.AppendLine("#EXT-X-ENDLIST"); //m3u8文件结束符 表示视频已经结束 有这个标志同时也说明当前流是一个非直播流 | |||
//// //#EXT-X-PLAYLIST-TYPE:VOD/Live //VOD表示当前视频流不是一个直播流,而是点播流(也就是视频的全部ts文件已经生成) | |||
////} | |||
/// <summary> | |||
/// 添加结束标识 | |||
/// 直播流用不到 | |||
/// </summary> | |||
public void AppendM3U8End() | |||
{ | |||
StringBuilder sb = new StringBuilder(); | |||
sb.AppendLine("#EXT-X-ENDLIST"); | |||
//m3u8文件结束符 表示视频已经结束 有这个标志同时也说明当前流是一个非直播流 | |||
//#EXT-X-PLAYLIST-TYPE:VOD/Live //VOD表示当前视频流不是一个直播流,而是点播流(也就是视频的全部ts文件已经生成) | |||
} | |||
/// <summary> | |||
/// 停止观看直播时清零数据 | |||
@@ -36,12 +36,15 @@ namespace JT1078.Hls | |||
//todo:音频同步 | |||
//private Dictionary<string, byte> AudioCounter = new Dictionary<string, byte>(); | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public TSEncoder() | |||
{ | |||
VideoCounter = new Dictionary<string, byte>(StringComparer.OrdinalIgnoreCase); | |||
} | |||
public byte[] CreateSDT(JT1078Package jt1078Package, int minBufferSize = 188) | |||
public byte[] CreateSDT(int minBufferSize = 188) | |||
{ | |||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | |||
try | |||
@@ -85,7 +88,7 @@ namespace JT1078.Hls | |||
TSArrayPool.Return(buffer); | |||
} | |||
} | |||
public byte[] CreatePAT(JT1078Package jt1078Package, int minBufferSize = 188) | |||
public byte[] CreatePAT(int minBufferSize = 188) | |||
{ | |||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | |||
try | |||
@@ -111,7 +114,7 @@ namespace JT1078.Hls | |||
TSArrayPool.Return(buffer); | |||
} | |||
} | |||
public byte[] CreatePMT(JT1078Package jt1078Package, int minBufferSize = 188) | |||
public byte[] CreatePMT(int minBufferSize = 188) | |||
{ | |||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | |||
try | |||
@@ -139,7 +142,7 @@ namespace JT1078.Hls | |||
TSArrayPool.Return(buffer); | |||
} | |||
} | |||
public byte[] CreatePES(JT1078Package jt1078Package, int minBufferSize = 1024) | |||
public byte[] CreatePES(in JT1078Package jt1078Package, int minBufferSize = 1024) | |||
{ | |||
//将1078一帧的数据拆分成一小段一小段的PES包 | |||
byte[] buffer = TSArrayPool.Rent(jt1078Package.Bodies.Length + minBufferSize); | |||
@@ -1,84 +0,0 @@ | |||
using BenchmarkDotNet.Attributes; | |||
using BenchmarkDotNet.Configs; | |||
using BenchmarkDotNet.Environments; | |||
using BenchmarkDotNet.Jobs; | |||
using BenchmarkDotNet.Toolchains.CsProj; | |||
using JT1078.Flv.MessagePack; | |||
using JT1078.Protocol; | |||
using JT1078.Protocol.H264; | |||
using JT1078.Protocol.MessagePack; | |||
using JT1078.Protocol.Extensions; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
namespace JT1078.Flv.Benchmark | |||
{ | |||
[Config(typeof(JT1078FlvEncoderConfig))] | |||
[MarkdownExporterAttribute.GitHub] | |||
[MemoryDiagnoser] | |||
public class JT1078FlvEncoderContext | |||
{ | |||
JT1078Package Package; | |||
List<H264NALU> H264NALUs; | |||
H264NALU SPSNALu; | |||
H264Decoder h264Decoder = new H264Decoder(); | |||
FlvEncoder flvEncoder = new FlvEncoder(); | |||
[Params(100, 10000, 100000)] | |||
public int N; | |||
[GlobalSetup] | |||
public void Setup() | |||
{ | |||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078_1.txt")); | |||
foreach (var line in lines) | |||
{ | |||
var data = line.Split(','); | |||
var bytes = data[6].ToHexBytes(); | |||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||
Package = JT1078Serializer.Merge(package); | |||
} | |||
H264NALUs = h264Decoder.ParseNALU(Package); | |||
SPSNALu = H264NALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS); | |||
SPSNALu.RawData = h264Decoder.DiscardEmulationPreventionBytes(SPSNALu.RawData); | |||
} | |||
[Benchmark(Description = "EXPGolombReader")] | |||
public void EXPGolombReaderTest() | |||
{ | |||
for (var i = 0; i < N; i++) | |||
{ | |||
ExpGolombReader h264GolombReader = new ExpGolombReader(SPSNALu.RawData); | |||
h264GolombReader.ReadSPS(); | |||
} | |||
} | |||
[Benchmark(Description = "H264Decoder")] | |||
public void H264Decoder() | |||
{ | |||
for (var i = 0; i < N; i++) | |||
{ | |||
var nalus = h264Decoder.ParseNALU(Package); | |||
} | |||
} | |||
//[Benchmark(Description = "FlvEncoder")] | |||
//public void FlvEncoder() | |||
//{ | |||
// for(var i=0;i< N;i++) | |||
// { | |||
// var contents = flvEncoder.CreateFlvFrame(H264NALUs); | |||
// } | |||
//} | |||
} | |||
public class JT1078FlvEncoderConfig : ManualConfig | |||
{ | |||
public JT1078FlvEncoderConfig() | |||
{ | |||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp50).WithPlatform(Platform.AnyCpu)); | |||
} | |||
} | |||
} |