@@ -12,7 +12,7 @@ jobs: | |||||
- name: Setup .NET Core | - name: Setup .NET Core | ||||
uses: actions/setup-dotnet@master | uses: actions/setup-dotnet@master | ||||
with: | with: | ||||
dotnet-version: 5.0.100 | |||||
dotnet-version: 6.0.100 | |||||
- name: dotnet info | - name: dotnet info | ||||
run: dotnet --info | run: dotnet --info | ||||
- name: dotnet restore | - name: dotnet restore | ||||
@@ -2,11 +2,11 @@ | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<OutputType>Exe</OutputType> | <OutputType>Exe</OutputType> | ||||
<TargetFramework>net5.0</TargetFramework> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" /> | |||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.12.1" /> | |||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" /> | |||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.1" /> | |||||
<PackageReference Include="System.Buffers" Version="4.5.1" /> | <PackageReference Include="System.Buffers" Version="4.5.1" /> | ||||
<PackageReference Include="System.Memory" Version="4.5.4" /> | <PackageReference Include="System.Memory" Version="4.5.4" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -72,24 +72,6 @@ namespace JT1078.AV.Benchmark | |||||
break; | break; | ||||
} | } | ||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | 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); | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -117,7 +99,8 @@ namespace JT1078.AV.Benchmark | |||||
{ | { | ||||
for (var i = 0; i < N; i++) | for (var i = 0; i < N; i++) | ||||
{ | { | ||||
var buffer = fmp4Encoder.OtherVideoBox(FMp4H264NALUs); | |||||
//todo:OtherVideoBox | |||||
//var buffer = fmp4Encoder.OtherVideoBox(FMp4H264NALUs); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -126,7 +109,7 @@ namespace JT1078.AV.Benchmark | |||||
{ | { | ||||
public JT1078AVEncoderConfig() | public JT1078AVEncoderConfig() | ||||
{ | { | ||||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp50).WithPlatform(Platform.AnyCpu)); | |||||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp60).WithPlatform(Platform.AnyCpu)); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,19 +1,19 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>net5.0</TargetFramework> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
<IsPackable>false</IsPackable> | <IsPackable>false</IsPackable> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | <PackageReference Include="xunit" Version="2.4.1" /> | ||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
<PackageReference Include="coverlet.collector" Version="1.3.0"> | |||||
<PackageReference Include="coverlet.collector" Version="3.1.0"> | |||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
@@ -39,9 +39,6 @@ | |||||
</None> | </None> | ||||
<None Update="FMP4\fragmented_demo_trun.txt"> | <None Update="FMP4\fragmented_demo_trun.txt"> | ||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
</None> | |||||
<None Update="H264\index.html"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | </None> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -438,107 +438,11 @@ namespace JT1078.FMp4.Test | |||||
[Fact] | [Fact] | ||||
public void Test4() | public void Test4() | ||||
{ | |||||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | |||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
var packages = ParseNALUTests(); | |||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_5.mp4"); | |||||
if (File.Exists(filepath)) | |||||
{ | |||||
File.Delete(filepath); | |||||
} | |||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||||
var ftyp = fMp4Encoder.FtypBox(); | |||||
fileStream.Write(ftyp); | |||||
var iNalus = h264Decoder.ParseNALU(packages[0]); | |||||
//判断第一帧是否关键帧 | |||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
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); | |||||
foreach (var nalu in h264NALUs) | |||||
{ | |||||
if (nalu.Slice) | |||||
{ | |||||
//H264 NALU slice first_mb_in_slice | |||||
nalus.Add(nalu); | |||||
} | |||||
else | |||||
{ | |||||
if (nalus.Count > 0) | |||||
{ | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
nalus.Add(nalu); | |||||
} | |||||
} | |||||
} | |||||
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.FtypBox(); | |||||
fileStream.Write(ftyp); | |||||
var iNalus = h264Decoder.ParseNALU(packages[0]); | |||||
//判断第一帧是否关键帧 | |||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
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.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
} | |||||
nalus = nalus.Concat(h264NALUs).ToList(); | |||||
} | |||||
if (nalus.Count > 0) | |||||
{ | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
fileStream.Close(); | |||||
} | |||||
[Fact] | |||||
public void Test6() | |||||
{ | { | ||||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | FMp4Encoder fMp4Encoder = new FMp4Encoder(); | ||||
H264Decoder h264Decoder = new H264Decoder(); | H264Decoder h264Decoder = new H264Decoder(); | ||||
var packages = ParseNALUTests1(); | var packages = ParseNALUTests1(); | ||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7.mp4"); | |||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7_3.mp4"); | |||||
if (File.Exists(filepath)) | if (File.Exists(filepath)) | ||||
{ | { | ||||
File.Delete(filepath); | File.Delete(filepath); | ||||
@@ -551,38 +455,30 @@ namespace JT1078.FMp4.Test | |||||
var iPackage = packages.FirstOrDefault(f => f.Label3.DataType == JT1078DataType.视频I帧); | var iPackage = packages.FirstOrDefault(f => f.Label3.DataType == JT1078DataType.视频I帧); | ||||
var iNalus = h264Decoder.ParseNALU(iPackage); | var iNalus = h264Decoder.ParseNALU(iPackage); | ||||
//判断第一帧是否关键帧 | //判断第一帧是否关键帧 | ||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
var moov = fMp4Encoder.MoovBox( | |||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | ||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | ||||
fileStream.Write(moov); | fileStream.Write(moov); | ||||
List<H264NALU> nalus = new List<H264NALU>(); | |||||
List<JT1078Package> tmp = new List<JT1078Package>(); | |||||
foreach (var package in packages) | foreach (var package in packages) | ||||
{ | { | ||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||||
if (package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧) | if (package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧) | ||||
{ | { | ||||
if (nalus.Count > 0) | |||||
if (tmp.Count>0) | |||||
{ | { | ||||
fileStream.Write(fMp4Encoder.StypBox()); | fileStream.Write(fMp4Encoder.StypBox()); | ||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(tmp); | |||||
fileStream.Write(otherBuffer); | fileStream.Write(otherBuffer); | ||||
nalus.Clear(); | |||||
tmp.Clear(); | |||||
} | } | ||||
} | } | ||||
nalus = nalus.Concat(h264NALUs).ToList(); | |||||
} | |||||
if (nalus.Count > 0) | |||||
{ | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
tmp.Add(package); | |||||
} | } | ||||
fileStream.Close(); | fileStream.Close(); | ||||
} | } | ||||
[Fact] | [Fact] | ||||
public void Test6_2() | |||||
public void Test4_2() | |||||
{ | { | ||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7.h264"); | var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7.h264"); | ||||
if (File.Exists(filepath)) | if (File.Exists(filepath)) | ||||
@@ -49,7 +49,7 @@ namespace JT1078.FMp4 | |||||
public uint DefaultSampleSize { get; set; } | public uint DefaultSampleSize { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// TFHD_FLAG_DEFAULT_FLAGS | /// TFHD_FLAG_DEFAULT_FLAGS | ||||
/// MOV_AUDIO == handler_type ? 0x02000000 : (0x00010000| 0x01000000); | |||||
/// MOV_AUDIO == handler_type ? TFHD_FLAG_AUDIO_TPYE : TFHD_FLAG_VIDEO_TPYE; | |||||
/// </summary> | /// </summary> | ||||
public uint DefaultSampleFlags { get; set; } | public uint DefaultSampleFlags { get; set; } | ||||
#endregion | #endregion | ||||
@@ -18,6 +18,25 @@ namespace JT1078.FMp4 | |||||
/// </summary> | /// </summary> | ||||
public static readonly DateTime UTCBaseTime = new DateTime(1904, 1, 1); | public static readonly DateTime UTCBaseTime = new DateTime(1904, 1, 1); | ||||
/// <summary> | /// <summary> | ||||
/// fmp4 FLAG_SEGMENT | |||||
/// </summary> | |||||
public const int FLAG_SEGMENT = 0x00000002; | |||||
/// <summary> | |||||
/// key frame | |||||
/// </summary> | |||||
public const int AV_FLAG_KEYFREAME = 0x0001; | |||||
/// <summary> | |||||
/// I frame | |||||
/// </summary> | |||||
public const int TREX_FLAG_SAMPLE_DEPENDS_ON_I_PICTURE = 0x02000000; | |||||
/// <summary> | |||||
/// p b frame | |||||
/// </summary> | |||||
public const int TREX_FLAG_SAMPLE_DEPENDS_ON_NOT_I_PICTURE = 0x01000000; | |||||
/// <summary> | |||||
/// TKHD_FLAG_ENABLED | /// TKHD_FLAG_ENABLED | ||||
/// </summary> | /// </summary> | ||||
public const int TKHD_FLAG_ENABLED = 0x000001; | public const int TKHD_FLAG_ENABLED = 0x000001; | ||||
@@ -38,6 +57,14 @@ namespace JT1078.FMp4 | |||||
/// </summary> | /// </summary> | ||||
public const int TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX = 0x00000002; | public const int TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX = 0x00000002; | ||||
/// <summary> | /// <summary> | ||||
/// TFHD_FLAG_AUDIO_TPYE | |||||
/// </summary> | |||||
public const int TFHD_FLAG_AUDIO_TPYE = 0x02000000; | |||||
/// <summary> | |||||
/// TFHD_FLAG_VIDEO_TPYE | |||||
/// </summary> | |||||
public const int TFHD_FLAG_VIDEO_TPYE = (0x00010000| 0x01000000); | |||||
/// <summary> | |||||
/// TFHD_FLAG_SAMPLE_DUR | /// TFHD_FLAG_SAMPLE_DUR | ||||
/// </summary> | /// </summary> | ||||
public const int TFHD_FLAG_DEFAULT_DURATION = 0x00000008; | public const int TFHD_FLAG_DEFAULT_DURATION = 0x00000008; | ||||
@@ -19,28 +19,36 @@ namespace JT1078.FMp4 | |||||
/// ftyp | /// ftyp | ||||
/// moov | /// moov | ||||
/// styp 1 | /// styp 1 | ||||
/// sidx 1 | |||||
/// moof 1 | /// moof 1 | ||||
/// mdat 1 | /// mdat 1 | ||||
/// ... | /// ... | ||||
/// styp n | /// styp n | ||||
/// sidx n | |||||
/// moof n | /// moof n | ||||
/// mdat n | /// mdat n | ||||
/// mfra | |||||
/// ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | /// ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | ||||
/// </summary> | /// </summary> | ||||
public class FMp4Encoder | public class FMp4Encoder | ||||
{ | { | ||||
Dictionary<string, TrackInfo> TrackInfos; | Dictionary<string, TrackInfo> TrackInfos; | ||||
const uint DefaultSampleDuration = 40u; | |||||
const uint DefaultSampleFlags = 0x1010000; | |||||
const uint FirstSampleFlags = 33554432; | |||||
const uint TfhdFlags = 0x2003a; | |||||
//const uint TrunFlags = 0x205; | |||||
const uint TrunFlags = 0x205; | |||||
const uint TfhdFlags = FMp4Constants.TFHD_FLAG_DEFAULT_BASE_IS_MOOF | | |||||
FMp4Constants.TFHD_FLAG_DEFAULT_SIZE| | |||||
FMp4Constants.TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX| | |||||
FMp4Constants.TFHD_FLAG_DEFAULT_FLAGS; | |||||
const uint TrunFlags = FMp4Constants.TRUN_FLAG_DATA_OFFSET_PRESENT| | |||||
FMp4Constants.TRUN_FLAG_FIRST_SAMPLE_FLAGS_PRESENT| | |||||
FMp4Constants.TRUN_FLAG_SAMPLE_DURATION_PRESENT| | |||||
FMp4Constants.TRUN_FLAG_SAMPLE_SIZE_PRESENT; | |||||
const uint SampleDescriptionIndex = 1; | const uint SampleDescriptionIndex = 1; | ||||
const uint TrackID = 1; | const uint TrackID = 1; | ||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// </summary> | /// </summary> | ||||
@@ -61,13 +69,15 @@ namespace JT1078.FMp4 | |||||
{ | { | ||||
//ftyp | //ftyp | ||||
FileTypeBox fileTypeBox = new FileTypeBox(); | FileTypeBox fileTypeBox = new FileTypeBox(); | ||||
fileTypeBox.MajorBrand = "isom"; | |||||
fileTypeBox.MinorVersion = "\0\0\u0002\0"; | |||||
fileTypeBox.MajorBrand = "msdh"; | |||||
fileTypeBox.MinorVersion = "\0\0\0\0"; | |||||
fileTypeBox.CompatibleBrands.Add("isom"); | fileTypeBox.CompatibleBrands.Add("isom"); | ||||
fileTypeBox.CompatibleBrands.Add("iso2"); | |||||
fileTypeBox.CompatibleBrands.Add("avc1"); | |||||
fileTypeBox.CompatibleBrands.Add("mp41"); | |||||
fileTypeBox.CompatibleBrands.Add("mp42"); | |||||
fileTypeBox.CompatibleBrands.Add("msdh"); | |||||
fileTypeBox.CompatibleBrands.Add("msix"); | |||||
// default‐base is‐moof flag | |||||
fileTypeBox.CompatibleBrands.Add("iso5"); | fileTypeBox.CompatibleBrands.Add("iso5"); | ||||
// styp | |||||
fileTypeBox.CompatibleBrands.Add("iso6"); | fileTypeBox.CompatibleBrands.Add("iso6"); | ||||
fileTypeBox.ToBuffer(ref writer); | fileTypeBox.ToBuffer(ref writer); | ||||
var data = writer.FlushAndGetArray(); | var data = writer.FlushAndGetArray(); | ||||
@@ -83,7 +93,7 @@ namespace JT1078.FMp4 | |||||
/// 编码moov盒子 | /// 编码moov盒子 | ||||
/// </summary> | /// </summary> | ||||
/// <returns></returns> | /// <returns></returns> | ||||
public byte[] VideoMoovBox(in H264NALU sps, in H264NALU pps) | |||||
public byte[] MoovBox(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); | FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | ||||
@@ -112,7 +122,6 @@ namespace JT1078.FMp4 | |||||
movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox(); | movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox(); | ||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 0; | movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 0; | ||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.ModificationTime = 0; | movieBox.TrackBox.MediaBox.MediaHeaderBox.ModificationTime = 0; | ||||
//movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1200000; | |||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1000; | movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1000; | ||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.Duration = 0; | movieBox.TrackBox.MediaBox.MediaHeaderBox.Duration = 0; | ||||
movieBox.TrackBox.MediaBox.HandlerBox = new HandlerBox(); | movieBox.TrackBox.MediaBox.HandlerBox = new HandlerBox(); | ||||
@@ -173,7 +182,7 @@ namespace JT1078.FMp4 | |||||
try | try | ||||
{ | { | ||||
SegmentTypeBox stypTypeBox = new SegmentTypeBox(); | SegmentTypeBox stypTypeBox = new SegmentTypeBox(); | ||||
stypTypeBox.MajorBrand = "isom"; | |||||
stypTypeBox.MajorBrand = "msdh"; | |||||
stypTypeBox.MinorVersion = "\0\0\0\0"; | stypTypeBox.MinorVersion = "\0\0\0\0"; | ||||
stypTypeBox.CompatibleBrands.Add("isom"); | stypTypeBox.CompatibleBrands.Add("isom"); | ||||
stypTypeBox.CompatibleBrands.Add("mp42"); | stypTypeBox.CompatibleBrands.Add("mp42"); | ||||
@@ -193,19 +202,22 @@ namespace JT1078.FMp4 | |||||
/// <summary> | /// <summary> | ||||
/// 编码其他视频数据盒子 | /// 编码其他视频数据盒子 | ||||
/// 注意:固定I帧解析 | |||||
/// I P P P P I P P P P I P P P P | |||||
/// todo:50ms或者一个关键帧进行切片 | |||||
/// todo:优化编码 | |||||
/// </summary> | /// </summary> | ||||
/// <returns></returns> | /// <returns></returns> | ||||
public byte[] OtherVideoBox(in List<H264NALU> nalus) | |||||
public byte[] OtherVideoBox(in List<JT1078Package> nalus) | |||||
{ | { | ||||
byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.RawData.Length + s.StartCodePrefix.Length) + 4096); | |||||
byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.Bodies.Length) + 4096); | |||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | ||||
try | try | ||||
{ | { | ||||
var truns = new List<TrackRunBox.TrackRunInfo>(); | var truns = new List<TrackRunBox.TrackRunInfo>(); | ||||
List<byte[]> rawdatas = new List<byte[]>(); | |||||
uint iSize = 0; | |||||
ulong lastTimestamp = 0; | |||||
string key = string.Empty; | string key = string.Empty; | ||||
ulong timestamp = 0; | |||||
uint subsegmentDuration=0; | |||||
for (var i = 0; i < nalus.Count; i++) | for (var i = 0; i < nalus.Count; i++) | ||||
{ | { | ||||
var nalu = nalus[i]; | var nalu = nalus[i]; | ||||
@@ -213,46 +225,44 @@ namespace JT1078.FMp4 | |||||
{ | { | ||||
key = nalu.GetKey(); | key = nalu.GetKey(); | ||||
} | } | ||||
rawdatas.Add(nalu.RawData); | |||||
if (nalu.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||||
uint duration = 0; | |||||
if (timestamp>0) | |||||
{ | { | ||||
iSize += (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length); | |||||
} | |||||
else | |||||
{ | |||||
if (iSize > 0) | |||||
{ | |||||
truns.Add(new TrackRunBox.TrackRunInfo() | |||||
{ | |||||
SampleDuration=40, | |||||
SampleSize = iSize, | |||||
}); | |||||
iSize = 0; | |||||
} | |||||
truns.Add(new TrackRunBox.TrackRunInfo() | |||||
{ | |||||
SampleDuration = 40, | |||||
SampleSize = (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length), | |||||
}); | |||||
duration=(uint)(nalu.Timestamp-timestamp); | |||||
} | } | ||||
if (i == (nalus.Count - 1)) | |||||
truns.Add(new TrackRunBox.TrackRunInfo() | |||||
{ | { | ||||
lastTimestamp = nalu.Timestamp; | |||||
} | |||||
SampleDuration = duration, | |||||
SampleSize = (uint)(nalu.Bodies.Length), | |||||
}); | |||||
subsegmentDuration+=duration; | |||||
timestamp=nalu.Timestamp; | |||||
} | } | ||||
if (TrackInfos.TryGetValue(key, out TrackInfo trackInfo)) | |||||
if (!TrackInfos.TryGetValue(key, out TrackInfo trackInfo)) | |||||
{ | { | ||||
if (trackInfo.SN == uint.MaxValue) | |||||
{ | |||||
trackInfo.SN = 1; | |||||
} | |||||
trackInfo.SN++; | |||||
trackInfo = new TrackInfo { SN = 1, DTS = 0, SubsegmentDuration=0 }; | |||||
TrackInfos.Add(key, trackInfo); | |||||
} | } | ||||
else | |||||
if (trackInfo.SN == uint.MaxValue) | |||||
{ | { | ||||
trackInfo = new TrackInfo { SN = 1, DTS = 0 }; | |||||
TrackInfos.Add(key, trackInfo); | |||||
trackInfo.SN = 1; | |||||
} | } | ||||
trackInfo.SN++; | |||||
SegmentIndexBox segmentIndexBox = new SegmentIndexBox(1); | |||||
segmentIndexBox.ReferenceID = 1; | |||||
segmentIndexBox.EarliestPresentationTime = trackInfo.SubsegmentDuration; | |||||
segmentIndexBox.SegmentIndexs = new List<SegmentIndexBox.SegmentIndex>() | |||||
{ | |||||
new SegmentIndexBox.SegmentIndex | |||||
{ | |||||
SubsegmentDuration=subsegmentDuration | |||||
} | |||||
}; | |||||
segmentIndexBox.ToBuffer(ref writer); | |||||
var current1 = writer.GetCurrentPosition(); | |||||
//moof | |||||
var movieFragmentBox = new MovieFragmentBox(); | var movieFragmentBox = new MovieFragmentBox(); | ||||
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | ||||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN; | movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN; | ||||
@@ -260,23 +270,39 @@ namespace JT1078.FMp4 | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags); | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags); | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID; | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID; | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex; | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex; | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize; | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize; | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = FMp4Constants.TFHD_FLAG_VIDEO_TPYE; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.DTS; | |||||
trackInfo.DTS += (ulong)(truns.Count * DefaultSampleDuration); | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.SubsegmentDuration; | |||||
trackInfo.SubsegmentDuration+=subsegmentDuration; | |||||
TrackInfos[key] = trackInfo; | TrackInfos[key] = trackInfo; | ||||
//trun | //trun | ||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: TrunFlags); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FirstSampleFlags; | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(1, flags: TrunFlags); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FMp4Constants.TREX_FLAG_SAMPLE_DEPENDS_ON_I_PICTURE; | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns; | movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns; | ||||
movieFragmentBox.ToBuffer(ref writer); | movieFragmentBox.ToBuffer(ref writer); | ||||
//mdat | |||||
var mediaDataBox = new MediaDataBox(); | var mediaDataBox = new MediaDataBox(); | ||||
mediaDataBox.Data = rawdatas; | |||||
mediaDataBox.Data=new List<byte[]>(); | |||||
foreach(var nalu in nalus) | |||||
{ | |||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(nalu); | |||||
if (h264NALUs!=null) | |||||
{ | |||||
foreach(var n in h264NALUs) | |||||
{ | |||||
mediaDataBox.Data.Add(n.RawData); | |||||
} | |||||
} | |||||
} | |||||
mediaDataBox.ToBuffer(ref writer); | mediaDataBox.ToBuffer(ref writer); | ||||
var current2 = writer.GetCurrentPosition(); | |||||
foreach (var postion in segmentIndexBox.ReferencedSizePositions) | |||||
{ | |||||
writer.WriteUInt32Return((uint)(current2 - current1), postion); | |||||
} | |||||
var data = writer.FlushAndGetArray(); | var data = writer.FlushAndGetArray(); | ||||
return data; | return data; | ||||
} | } | ||||
@@ -290,6 +316,7 @@ namespace JT1078.FMp4 | |||||
{ | { | ||||
public uint SN { get; set; } | public uint SN { get; set; } | ||||
public ulong DTS { get; set; } | public ulong DTS { get; set; } | ||||
public ulong SubsegmentDuration { get; set; } | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,8 +1,8 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net5.0;</TargetFrameworks> | |||||
<LangVersion>8.0</LangVersion> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
<LangVersion>10.0</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | <Copyright>Copyright 2019.</Copyright> | ||||
<Authors>SmallChi(Koike)</Authors> | <Authors>SmallChi(Koike)</Authors> | ||||
<PackageId>JT1078.FMp4</PackageId> | <PackageId>JT1078.FMp4</PackageId> | ||||
@@ -983,7 +983,7 @@ | |||||
<member name="P:JT1078.FMp4.TrackFragmentHeaderBox.DefaultSampleFlags"> | <member name="P:JT1078.FMp4.TrackFragmentHeaderBox.DefaultSampleFlags"> | ||||
<summary> | <summary> | ||||
TFHD_FLAG_DEFAULT_FLAGS | TFHD_FLAG_DEFAULT_FLAGS | ||||
MOV_AUDIO == handler_type ? 0x02000000 : (0x00010000| 0x01000000); | |||||
MOV_AUDIO == handler_type ? TFHD_FLAG_AUDIO_TPYE : TFHD_FLAG_VIDEO_TPYE; | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="T:JT1078.FMp4.TrackFragmentRandomAccessBox"> | <member name="T:JT1078.FMp4.TrackFragmentRandomAccessBox"> | ||||
@@ -1222,6 +1222,26 @@ | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="F:JT1078.FMp4.FMp4Constants.FLAG_SEGMENT"> | |||||
<summary> | |||||
fmp4 FLAG_SEGMENT | |||||
</summary> | |||||
</member> | |||||
<member name="F:JT1078.FMp4.FMp4Constants.AV_FLAG_KEYFREAME"> | |||||
<summary> | |||||
key frame | |||||
</summary> | |||||
</member> | |||||
<member name="F:JT1078.FMp4.FMp4Constants.TREX_FLAG_SAMPLE_DEPENDS_ON_I_PICTURE"> | |||||
<summary> | |||||
I frame | |||||
</summary> | |||||
</member> | |||||
<member name="F:JT1078.FMp4.FMp4Constants.TREX_FLAG_SAMPLE_DEPENDS_ON_NOT_I_PICTURE"> | |||||
<summary> | |||||
p b frame | |||||
</summary> | |||||
</member> | |||||
<member name="F:JT1078.FMp4.FMp4Constants.TKHD_FLAG_ENABLED"> | <member name="F:JT1078.FMp4.FMp4Constants.TKHD_FLAG_ENABLED"> | ||||
<summary> | <summary> | ||||
TKHD_FLAG_ENABLED | TKHD_FLAG_ENABLED | ||||
@@ -1247,6 +1267,16 @@ | |||||
TFHD_FLAG_SAMPLE_DESC | TFHD_FLAG_SAMPLE_DESC | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="F:JT1078.FMp4.FMp4Constants.TFHD_FLAG_AUDIO_TPYE"> | |||||
<summary> | |||||
TFHD_FLAG_AUDIO_TPYE | |||||
</summary> | |||||
</member> | |||||
<member name="F:JT1078.FMp4.FMp4Constants.TFHD_FLAG_VIDEO_TPYE"> | |||||
<summary> | |||||
TFHD_FLAG_VIDEO_TPYE | |||||
</summary> | |||||
</member> | |||||
<member name="F:JT1078.FMp4.FMp4Constants.TFHD_FLAG_DEFAULT_DURATION"> | <member name="F:JT1078.FMp4.FMp4Constants.TFHD_FLAG_DEFAULT_DURATION"> | ||||
<summary> | <summary> | ||||
TFHD_FLAG_SAMPLE_DUR | TFHD_FLAG_SAMPLE_DUR | ||||
@@ -1310,13 +1340,14 @@ | |||||
ftyp | ftyp | ||||
moov | moov | ||||
styp 1 | styp 1 | ||||
sidx 1 | |||||
moof 1 | moof 1 | ||||
mdat 1 | mdat 1 | ||||
... | ... | ||||
styp n | styp n | ||||
sidx n | |||||
moof n | moof n | ||||
mdat n | mdat n | ||||
mfra | |||||
ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
@@ -1331,7 +1362,7 @@ | |||||
</summary> | </summary> | ||||
<returns></returns> | <returns></returns> | ||||
</member> | </member> | ||||
<member name="M:JT1078.FMp4.FMp4Encoder.VideoMoovBox(JT1078.Protocol.H264.H264NALU@,JT1078.Protocol.H264.H264NALU@)"> | |||||
<member name="M:JT1078.FMp4.FMp4Encoder.MoovBox(JT1078.Protocol.H264.H264NALU@,JT1078.Protocol.H264.H264NALU@)"> | |||||
<summary> | <summary> | ||||
编码moov盒子 | 编码moov盒子 | ||||
</summary> | </summary> | ||||
@@ -1343,9 +1374,13 @@ | |||||
</summary> | </summary> | ||||
<returns></returns> | <returns></returns> | ||||
</member> | </member> | ||||
<member name="M:JT1078.FMp4.FMp4Encoder.OtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU}@)"> | |||||
<member name="M:JT1078.FMp4.FMp4Encoder.OtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.JT1078Package}@)"> | |||||
<summary> | <summary> | ||||
编码其他视频数据盒子 | 编码其他视频数据盒子 | ||||
注意:固定I帧解析 | |||||
I P P P P I P P P P I P P P P | |||||
todo:50ms或者一个关键帧进行切片 | |||||
todo:优化编码 | |||||
</summary> | </summary> | ||||
<returns></returns> | <returns></returns> | ||||
</member> | </member> | ||||
@@ -1,17 +1,17 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>net5.0</TargetFramework> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | <PackageReference Include="xunit" Version="2.4.1" /> | ||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
<PackageReference Include="coverlet.collector" Version="1.3.0"> | |||||
<PackageReference Include="coverlet.collector" Version="3.1.0"> | |||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
@@ -1,8 +1,8 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net5.0;</TargetFrameworks> | |||||
<LangVersion>8.0</LangVersion> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
<LangVersion>10.0</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | <Copyright>Copyright 2019.</Copyright> | ||||
<Authors>SmallChi(Koike)</Authors> | <Authors>SmallChi(Koike)</Authors> | ||||
<PackageId>JT1078.Flv</PackageId> | <PackageId>JT1078.Flv</PackageId> | ||||
@@ -35,6 +35,6 @@ | |||||
</None> | </None> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -1,7 +1,7 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>net5.0</TargetFramework> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
<IsPackable>false</IsPackable> | <IsPackable>false</IsPackable> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
@@ -15,14 +15,14 @@ | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | <PackageReference Include="xunit" Version="2.4.1" /> | ||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
<PackageReference Include="coverlet.collector" Version="1.3.0"> | |||||
<PackageReference Include="coverlet.collector" Version="3.1.0"> | |||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
@@ -1,8 +1,8 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net5.0;</TargetFrameworks> | |||||
<LangVersion>8.0</LangVersion> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
<LangVersion>10.0</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | <Copyright>Copyright 2019.</Copyright> | ||||
<Authors>SmallChi(Koike)</Authors> | <Authors>SmallChi(Koike)</Authors> | ||||
<PackageId>JT1078.Hls</PackageId> | <PackageId>JT1078.Hls</PackageId> | ||||
@@ -31,7 +31,7 @@ | |||||
</None> | </None> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" /> | |||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | <ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | ||||
@@ -2,15 +2,15 @@ | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<OutputType>Exe</OutputType> | <OutputType>Exe</OutputType> | ||||
<TargetFramework>net5.0</TargetFramework> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | ||||
<Optimize>true</Optimize> | <Optimize>true</Optimize> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" /> | |||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.12.1" /> | |||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" /> | |||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.1" /> | |||||
<PackageReference Include="System.Buffers" Version="4.5.1" /> | <PackageReference Include="System.Buffers" Version="4.5.1" /> | ||||
<PackageReference Include="System.Memory" Version="4.5.4" /> | <PackageReference Include="System.Memory" Version="4.5.4" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -135,7 +135,6 @@ namespace JT1078.Protocol.Test.H264 | |||||
nALUs = nALUs.Concat(nalus).ToList(); | nALUs = nALUs.Concat(nalus).ToList(); | ||||
} | } | ||||
} | } | ||||
var a = nALUs.Count(c => !c.Slice); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,14 +1,14 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>net5.0</TargetFramework> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
<IsPackable>false</IsPackable> | <IsPackable>false</IsPackable> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="JT808" Version="2.3.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> | |||||
<PackageReference Include="JT808" Version="2.4.5" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | <PackageReference Include="xunit" Version="2.4.1" /> | ||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
@@ -134,7 +134,6 @@ namespace JT1078.Protocol.H264 | |||||
nALU.LastFrameInterval = package.LastFrameInterval; | nALU.LastFrameInterval = package.LastFrameInterval; | ||||
nALU.LastIFrameInterval = package.LastIFrameInterval; | nALU.LastIFrameInterval = package.LastIFrameInterval; | ||||
nALU.Timestamp = package.Timestamp; | nALU.Timestamp = package.Timestamp; | ||||
nALU.Slice = (nalu[1] & 0x80)== 0x80; | |||||
nALU.RawData = nalu.ToArray(); | nALU.RawData = nalu.ToArray(); | ||||
if (startCodePrefix == 3) | if (startCodePrefix == 3) | ||||
{ | { | ||||
@@ -40,12 +40,6 @@ namespace JT1078.Protocol.H264 | |||||
/// 当数据类型为01000时,则没有该字段 | /// 当数据类型为01000时,则没有该字段 | ||||
/// </summary> | /// </summary> | ||||
public ulong Timestamp { get; set; } | public ulong Timestamp { get; set; } | ||||
/// <summary> | |||||
/// 是否切片 0x80 | |||||
/// H264 NALU slice first_mb_in_slice | |||||
/// </summary> | |||||
public bool Slice { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 数据体 | /// 数据体 | ||||
/// </summary> | /// </summary> | ||||
@@ -11,16 +11,19 @@ namespace JT1078.Protocol.H264 | |||||
ForbiddenZeroBit = (value & 0x80) >> 7; | ForbiddenZeroBit = (value & 0x80) >> 7; | ||||
NalRefIdc = (value & 0x60) >> 5; | NalRefIdc = (value & 0x60) >> 5; | ||||
NalUnitType = (NalUnitType)(value & 0x1f); | NalUnitType = (NalUnitType)(value & 0x1f); | ||||
KeyFrame=NalUnitType== NalUnitType.IDR; | |||||
} | } | ||||
public NALUHeader(ReadOnlySpan<byte> value) | public NALUHeader(ReadOnlySpan<byte> value) | ||||
{ | { | ||||
ForbiddenZeroBit = (value[0] & 0x80) >> 7; | ForbiddenZeroBit = (value[0] & 0x80) >> 7; | ||||
NalRefIdc = (value[0] & 0x60) >> 5; | NalRefIdc = (value[0] & 0x60) >> 5; | ||||
NalUnitType = (NalUnitType)(value[0] & 0x1f); | NalUnitType = (NalUnitType)(value[0] & 0x1f); | ||||
KeyFrame=NalUnitType== NalUnitType.IDR; | |||||
} | } | ||||
public int ForbiddenZeroBit { get; set; } | public int ForbiddenZeroBit { get; set; } | ||||
public int NalRefIdc { get; set; } | public int NalRefIdc { get; set; } | ||||
public NalUnitType NalUnitType { get; set; } | public NalUnitType NalUnitType { get; set; } | ||||
public bool KeyFrame { get; set; } | |||||
} | } | ||||
public enum NalUnitType : int | public enum NalUnitType : int | ||||
@@ -1,8 +1,8 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net5.0;</TargetFrameworks> | |||||
<LangVersion>8.0</LangVersion> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
<LangVersion>10.0</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | <Copyright>Copyright 2019.</Copyright> | ||||
<Authors>SmallChi(Koike)</Authors> | <Authors>SmallChi(Koike)</Authors> | ||||
<PackageId>JT1078</PackageId> | <PackageId>JT1078</PackageId> | ||||
@@ -21,12 +21,6 @@ | |||||
<DocumentationFile>JT1078.Protocol.xml</DocumentationFile> | <DocumentationFile>JT1078.Protocol.xml</DocumentationFile> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | |||||
<PackageReference Include="System.Memory" Version="4.5.4" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Remove="Audio\FaacEncoder.cs" /> | <Compile Remove="Audio\FaacEncoder.cs" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -37,8 +31,4 @@ | |||||
<PackagePath></PackagePath> | <PackagePath></PackagePath> | ||||
</None> | </None> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<PackageReference Include="System.Text.Json" Version="4.7.2" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
@@ -176,12 +176,6 @@ | |||||
当数据类型为01000时,则没有该字段 | 当数据类型为01000时,则没有该字段 | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.Protocol.H264.H264NALU.Slice"> | |||||
<summary> | |||||
是否切片 0x80 | |||||
H264 NALU slice first_mb_in_slice | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.Protocol.H264.H264NALU.RawData"> | <member name="P:JT1078.Protocol.H264.H264NALU.RawData"> | ||||
<summary> | <summary> | ||||
数据体 | 数据体 | ||||
@@ -1,9 +1,14 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | <Project Sdk="Microsoft.NET.Sdk.Web"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>net5.0</TargetFramework> | |||||
<TargetFramework>net6.0</TargetFramework> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | |||||
<Content Remove="wwwroot\index.html" /> | |||||
<Content Remove="wwwroot\signalr.min.js" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\JT1078.FMp4\JT1078.FMp4.csproj" /> | <ProjectReference Include="..\JT1078.FMp4\JT1078.FMp4.csproj" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -26,5 +31,11 @@ | |||||
<None Include="..\..\doc\video\jt1078_6.txt" Link="H264\jt1078_6.txt"> | <None Include="..\..\doc\video\jt1078_6.txt" Link="H264\jt1078_6.txt"> | ||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
</None> | </None> | ||||
<None Include="wwwroot\index.html"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="wwwroot\signalr.min.js"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -0,0 +1,12 @@ | |||||
{ | |||||
"profiles": { | |||||
"JT1078.SignalR.Test": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
}, | |||||
"applicationUrl": "http://localhost:5000/" | |||||
} | |||||
} | |||||
} |
@@ -52,20 +52,11 @@ namespace JT1078.SignalR.Test.Services | |||||
public void a() | public void a() | ||||
{ | { | ||||
List<JT1078Package> packages = new List<JT1078Package>(); | List<JT1078Package> packages = new List<JT1078Package>(); | ||||
//var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_3.txt")); | |||||
//var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_5.txt")); | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_6.txt")); | var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_6.txt")); | ||||
int mergeBodyLength = 0; | int mergeBodyLength = 0; | ||||
foreach (var line in lines) | foreach (var line in lines) | ||||
{ | { | ||||
//var data = line.Split(','); | |||||
//jt1078_5 | |||||
//var bytes = data[1].ToHexBytes(); | |||||
//jt1078_3 | |||||
//var bytes = data[6].ToHexBytes(); | |||||
//jt1078_6 | |||||
var bytes = line.ToHexBytes(); | var bytes = line.ToHexBytes(); | ||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | JT1078Package package = JT1078Serializer.Deserialize(bytes); | ||||
mergeBodyLength += package.DataBodyLength; | mergeBodyLength += package.DataBodyLength; | ||||
var packageMerge = JT1078Serializer.Merge(package); | var packageMerge = JT1078Serializer.Merge(package); | ||||
@@ -75,61 +66,31 @@ namespace JT1078.SignalR.Test.Services | |||||
} | } | ||||
} | } | ||||
List<byte[]> first = new List<byte[]>(); | List<byte[]> first = new List<byte[]>(); | ||||
//var styp = fMp4Encoder.EncoderStypBox(); | |||||
//first.Add(styp); | |||||
//q.Enqueue(styp); | |||||
var ftyp = fMp4Encoder.FtypBox(); | var ftyp = fMp4Encoder.FtypBox(); | ||||
//q.Enqueue(ftyp); | |||||
first.Add(ftyp); | first.Add(ftyp); | ||||
var package1 = packages[0]; | var package1 = packages[0]; | ||||
var nalus1 = h264Decoder.ParseNALU(package1); | var nalus1 = h264Decoder.ParseNALU(package1); | ||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
var moov = fMp4Encoder.MoovBox( | |||||
nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | ||||
nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | ||||
//q.Enqueue(moov); | |||||
first.Add(moov); | first.Add(moov); | ||||
q.Add(first.SelectMany(s=>s).ToArray()); | q.Add(first.SelectMany(s=>s).ToArray()); | ||||
List<NalUnitType> filter = new List<NalUnitType>() { NalUnitType.SEI,NalUnitType.SPS,NalUnitType.PPS,NalUnitType.AUD}; | |||||
List<H264NALU> nalus = new List<H264NALU>(); | |||||
List<JT1078Package> tmp = new List<JT1078Package>(); | |||||
//缓存组包到下一个I帧 | |||||
foreach (var package in packages) | 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.OtherVideoBox(nalus); | |||||
// q.Add(fMp4Encoder.StypBox().Concat(otherBuffer).ToArray()); | |||||
// nalus.Clear(); | |||||
// } | |||||
// else | |||||
// { | |||||
// nalus = nalus.Concat(h264NALUs).ToList(); | |||||
// } | |||||
//} | |||||
//else | |||||
//{ | |||||
// nalus = nalus.Concat(h264NALUs).ToList(); | |||||
//} | |||||
foreach (var nalu in h264NALUs) | |||||
if (package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||||
{ | { | ||||
if (nalu.Slice) | |||||
{ | |||||
//H264 NALU slice first_mb_in_slice | |||||
nalus.Add(nalu); | |||||
} | |||||
else | |||||
if (tmp.Count>0) | |||||
{ | { | ||||
if (nalus.Count > 0) | |||||
{ | |||||
q.Add(fMp4Encoder.StypBox()); | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
q.Add(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
nalus.Add(nalu); | |||||
List<byte[]> buffer = new List<byte[]>(); | |||||
buffer.Add(fMp4Encoder.StypBox()); | |||||
buffer.Add(fMp4Encoder.OtherVideoBox(tmp)); | |||||
q.Add(buffer.SelectMany(s => s).ToArray()); | |||||
tmp.Clear(); | |||||
} | } | ||||
} | } | ||||
tmp.Add(package); | |||||
} | } | ||||
} | } | ||||
@@ -50,6 +50,8 @@ namespace JT1078.SignalR.Test | |||||
{ | { | ||||
app.UseDeveloperExceptionPage(); | app.UseDeveloperExceptionPage(); | ||||
} | } | ||||
app.UseDefaultFiles(); | |||||
app.UseStaticFiles(); | |||||
app.UseRouting(); | app.UseRouting(); | ||||
app.UseCors("CorsPolicy"); | app.UseCors("CorsPolicy"); | ||||
app.UseAuthorization(); | app.UseAuthorization(); | ||||