Przeglądaj źródła

1.调整fmp4编码

2.调整在解析nalu的同时解析出sps和pps避免之后再一次查询nalu块
master
SmallChi(Koike) 3 lat temu
rodzic
commit
b0b5ef69ea
20 zmienionych plików z 413 dodań i 179 usunięć
  1. +2
    -2
      README.md
  2. +1
    -1
      src/Info.props
  3. +1
    -1
      src/JT1078.AV.Benchmark/JT1078.AV.Benchmark.csproj
  4. +3
    -3
      src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj
  5. +27
    -35
      src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs
  6. +39
    -61
      src/JT1078.FMp4/FMp4Encoder.cs
  7. +3
    -24
      src/JT1078.FMp4/JT1078.FMp4.xml
  8. +3
    -3
      src/JT1078.Flv.Test/JT1078.Flv.Test.csproj
  9. +104
    -0
      src/JT1078.Flv/FlvEncoder.cs
  10. +16
    -0
      src/JT1078.Flv/JT1078.Flv.xml
  11. +4
    -4
      src/JT1078.Hls.Test/JT1078.Hls.Test.csproj
  12. +1
    -1
      src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj
  13. +3
    -3
      src/JT1078.Protocol.Test/JT1078.Protocol.Test.csproj
  14. +95
    -1
      src/JT1078.Protocol/H264/H264Decoder.cs
  15. +8
    -1
      src/JT1078.Protocol/H264/H264NALU.cs
  16. +13
    -0
      src/JT1078.Protocol/IJT1078AVKey.cs
  17. +20
    -1
      src/JT1078.Protocol/JT1078.Protocol.xml
  18. +39
    -0
      src/JT1078.Protocol/JT1078AVFrame.cs
  19. +8
    -2
      src/JT1078.Protocol/JT1078Package.cs
  20. +23
    -36
      src/JT1078.SignalR.Test/Services/ToWebSocketService.cs

+ 2
- 2
README.md Wyświetl plik

@@ -89,8 +89,8 @@ var hex = JT1078Serializer.Serialize(jT1078Package).ToHexString();

2.拆解:
30 31 63 64 --帧头表示
81 --Label1 =>10000001 V P X CC
E2 --Label2 =>11100010 M PT
81 --Label1 =>10000001 V P X CC
E2 --Label2 =>11100010 M PT
10 88 --SN 包序号
01 12 34 56 78 10 --SIM卡号
01 --逻辑通道号


+ 1
- 1
src/Info.props Wyświetl plik

@@ -8,7 +8,7 @@
<PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl>
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl>
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license>
<Version>1.2.0-preview2</Version>
<Version>1.2.0-preview6</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<AnalysisLevel>latest</AnalysisLevel>


+ 1
- 1
src/JT1078.AV.Benchmark/JT1078.AV.Benchmark.csproj Wyświetl plik

@@ -8,7 +8,7 @@
<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.Memory" Version="4.5.4" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JT1078.Flv\JT1078.Flv.csproj" />


+ 3
- 3
src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj Wyświetl plik

@@ -7,13 +7,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>


+ 27
- 35
src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs Wyświetl plik

@@ -12,6 +12,7 @@ using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -498,12 +499,15 @@ namespace JT1078.FMp4.Test
}
[Fact]
public void Test4_3()
public void Test4_4()
{
uint a = uint.MaxValue;
var b = a + 1;

FMp4Encoder fMp4Encoder = new FMp4Encoder();
H264Decoder h264Decoder = new H264Decoder();
var packages = ParseNALUTests1();
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7_4_3.mp4");
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7_4_4.mp4");
if (File.Exists(filepath))
{
File.Delete(filepath);
@@ -520,9 +524,8 @@ namespace JT1078.FMp4.Test
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS),
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS));
fileStream.Write(moov);
List<JT1078Package> tmp = new List<JT1078Package>();
List<H264NALU> stream = new List<H264NALU>();

Queue<Mp4Frame> mp4Frames = new Queue<Mp4Frame>();
List<NalUnitType> filter = new List<NalUnitType>();
filter.Add(NalUnitType.SEI);
filter.Add(NalUnitType.PPS);
@@ -531,53 +534,42 @@ namespace JT1078.FMp4.Test
foreach (var package in packages)
{
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package);
if (h264NALUs!=null && h264NALUs.Count>0)
if (h264NALUs != null && h264NALUs.Count > 0)
{
stream.AddRange(h264NALUs.Where(w=> !filter.Contains(w.NALUHeader.NalUnitType)));
Mp4Frame mp4Frame = new Mp4Frame
{
Key = package.GetKey(),
KeyFrame = package.Label3.DataType == JT1078DataType.视频I帧
};
mp4Frame.NALUs = h264NALUs;
mp4Frames.Enqueue(mp4Frame);
}
}
List<H264NALU> tmp1 = new List<H264NALU>();
H264NALU prevNalu = null;
foreach (var item in stream)
while (mp4Frames.TryDequeue(out Mp4Frame frame))
{
if (item.NALUHeader.KeyFrame)
{
if (tmp1.Count>0)
{
fileStream.Write(fMp4Encoder.OtherVideoBox(tmp1));
tmp1.Clear();
}
tmp1.Add(item);
fileStream.Write(fMp4Encoder.OtherVideoBox(tmp1));
tmp1.Clear();
prevNalu=item;
continue;
}
if (prevNalu!=null) //第一帧I帧
{
if (tmp1.Count>1)
{
fileStream.Write(fMp4Encoder.StypBox());
fileStream.Write(fMp4Encoder.OtherVideoBox(tmp1));
tmp1.Clear();
}
tmp1.Add(item);
}
fileStream.Write(fMp4Encoder.OtherVideoBox(frame.NALUs, frame.Key, frame.KeyFrame));
}
fileStream.Close();
}
}

class Mp4Frame
{
public string Key { get; set; }
public bool KeyFrame { get; set; }
public List<H264NALU> NALUs { get; set; }
}

[Fact]
public void WebSocketMp4()
{
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_8.mp4");
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_10.mp4");
if (File.Exists(filepath))
{
File.Delete(filepath);
}
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
System.Net.WebSockets.ClientWebSocket clientWebSocket = new System.Net.WebSockets.ClientWebSocket();
clientWebSocket.ConnectAsync(new Uri("ws://127.0.0.1:81/live/JT1078_7.live.mp4"), CancellationToken.None).GetAwaiter().GetResult();
clientWebSocket.ConnectAsync(new Uri("ws://127.0.0.1:8080/live/JT1078_7.live.mp4"), CancellationToken.None).GetAwaiter().GetResult();
Task.Run(async() =>
{
while (true)


+ 39
- 61
src/JT1078.FMp4/FMp4Encoder.cs Wyświetl plik

@@ -16,26 +16,6 @@ namespace JT1078.FMp4
/// FMp4编码
/// fmp4
/// stream data
/// ftyp
/// moov
/// mvex
/// trex Video
/// trex Audio
/// styp 1
/// sidx 1 Video
/// sidx 1 Audio
/// moof 1
/// traf 1 Video
/// traf 1 Audio
/// mdat 1
/// ...
/// styp n
/// sidx 1 Video
/// sidx n Audio
/// moof n
/// traf n Video
/// traf n Audio
/// mdat n
/// ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing
/// </summary>
public class FMp4Encoder
@@ -49,7 +29,7 @@ namespace JT1078.FMp4
FMp4Constants.TFHD_FLAG_DEFAULT_FLAGS;

const uint TrunFlags = FMp4Constants.TRUN_FLAG_DATA_OFFSET_PRESENT |
FMp4Constants.TRUN_FLAG_SAMPLE_SIZE_PRESENT|
FMp4Constants.TRUN_FLAG_SAMPLE_SIZE_PRESENT |
FMp4Constants.TRUN_FLAG_FIRST_SAMPLE_FLAGS_PRESENT;

const uint SampleDescriptionIndex = 1;
@@ -325,10 +305,9 @@ namespace JT1078.FMp4
/// 编码首帧
/// ftyp moov
/// </summary>
/// <param name="sps"></param>
/// <param name="pps"></param>
/// <param name="avframe"></param>
/// <returns></returns>
public byte[] FirstVideoBox(in H264NALU sps, in H264NALU pps)
public byte[] FirstVideoBox(in JT1078AVFrame avframe)
{
byte[] buffer = FMp4ArrayPool.Rent(CacheSize);
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
@@ -348,8 +327,6 @@ namespace JT1078.FMp4
fileTypeBox.CompatibleBrands.Add("iso6");
fileTypeBox.ToBuffer(ref writer);

ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData);
var spsInfo = h264GolombReader.ReadSPS();
//moov
MovieBox movieBox = new MovieBox();
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 2);
@@ -366,8 +343,8 @@ namespace JT1078.FMp4
movieBox.TrackBox.TrackHeaderBox.TrackID = VideoTrackID;
movieBox.TrackBox.TrackHeaderBox.Duration = 0;
movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false;
movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width;
movieBox.TrackBox.TrackHeaderBox.Height = (uint)spsInfo.height;
movieBox.TrackBox.TrackHeaderBox.Width = (uint)avframe.Width;
movieBox.TrackBox.TrackHeaderBox.Height = (uint)avframe.Height;
movieBox.TrackBox.MediaBox = new MediaBox();
movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox();
movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 0;
@@ -386,16 +363,19 @@ namespace JT1078.FMp4
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox();
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox();
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>();
//h264
//h264
//profileIdc profileCompat levelIdc
//0x64 0x00 0x1e
//avc1.64001e
AVC1SampleEntry avc1 = new AVC1SampleEntry();
avc1.AVCConfigurationBox = new AVCConfigurationBox();
avc1.Width = (ushort)movieBox.TrackBox.TrackHeaderBox.Width;
avc1.Height = (ushort)movieBox.TrackBox.TrackHeaderBox.Height;
avc1.AVCConfigurationBox.AVCLevelIndication = spsInfo.levelIdc;
avc1.AVCConfigurationBox.AVCProfileIndication = spsInfo.profileIdc;
avc1.AVCConfigurationBox.ProfileCompatibility = (byte)spsInfo.profileCompat;
avc1.AVCConfigurationBox.PPSs = new List<byte[]>() { pps.RawData };
avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { sps.RawData };
avc1.AVCConfigurationBox.AVCLevelIndication = avframe.LevelIdc;
avc1.AVCConfigurationBox.AVCProfileIndication = avframe.ProfileIdc;
avc1.AVCConfigurationBox.ProfileCompatibility = avframe.ProfileCompat;
avc1.AVCConfigurationBox.PPSs = new List<byte[]>() { avframe.PPS.RawData };
avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { avframe.SPS.RawData };
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries.Add(avc1);
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.TimeToSampleBox = new TimeToSampleBox();
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SyncSampleBox = new SyncSampleBox();
@@ -430,7 +410,7 @@ namespace JT1078.FMp4
/// styp sidx moof mdat
/// </summary>
/// <returns></returns>
public byte[] OtherVideoBox(in List<H264NALU> nalus)
public byte[] OtherVideoBox(in List<H264NALU> nalus, in string key,in bool keyframe)
{
byte[] buffer = FMp4ArrayPool.Rent(CacheSize);
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
@@ -450,32 +430,25 @@ namespace JT1078.FMp4

//mdat
var mediaDataBox = new MediaDataBox();
mediaDataBox.Data=new List<byte[]>();

string key = string.Empty;
bool keyFrame = nalus[0].NALUHeader.KeyFrame;
mediaDataBox.Data = new List<byte[]>();
uint sampleSize = 0;
var truns = new List<TrackRunBox.TrackRunInfo>();
uint defaultSampleDuration = 0;
foreach (var n in nalus)
{
if (key==string.Empty)
{
key=n.GetKey();
}
defaultSampleDuration+=DefaultSampleDuration;
truns.Add(new TrackRunBox.TrackRunInfo()
{
SampleDuration = DefaultSampleDuration,
SampleSize = (uint)(n.RawData.Length+n.StartCodePrefix.Length),
});
sampleSize += (uint)(n.RawData.Length + n.StartCodePrefix.Length);
mediaDataBox.Data.Add(n.RawData);
}
truns.Add(new TrackRunBox.TrackRunInfo()
{
SampleDuration = DefaultSampleDuration,
SampleSize = sampleSize,
});
if (!TrackInfos.TryGetValue(key, out TrackInfo trackInfo))
{
trackInfo = new TrackInfo { SN = 1, SubsegmentDuration=0 };
trackInfo = new TrackInfo { SN = 1, SubsegmentDuration = 0 };
TrackInfos.Add(key, trackInfo);
}
if (trackInfo.SN == uint.MaxValue)
if (trackInfo.SN == 0)
{
trackInfo.SN = 1;
}
@@ -487,7 +460,7 @@ namespace JT1078.FMp4
{
new SegmentIndexBox.SegmentIndex
{
SubsegmentDuration=defaultSampleDuration
SubsegmentDuration=DefaultSampleDuration
}
};
segmentIndexBox.ToBuffer(ref writer);
@@ -501,31 +474,36 @@ namespace JT1078.FMp4
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags);
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = VideoTrackID;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = truns[0].SampleDuration;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = sampleSize;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = FMp4Constants.TFHD_FLAG_VIDEO_TPYE;
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox();
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.SubsegmentDuration;
//update trackInfo
trackInfo.SubsegmentDuration+=defaultSampleDuration;
trackInfo.SubsegmentDuration += DefaultSampleDuration;
trackInfo.SN++;
TrackInfos[key] = trackInfo;
//trun

movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(1, flags: TrunFlags);
if (keyframe)
{
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(1, flags:
FMp4Constants.TRUN_FLAG_DATA_OFFSET_PRESENT |
FMp4Constants.TRUN_FLAG_FIRST_SAMPLE_FLAGS_PRESENT);
}
else
{
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(1, FMp4Constants.TRUN_FLAG_DATA_OFFSET_PRESENT);
}
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FMp4Constants.TREX_FLAG_SAMPLE_DEPENDS_ON_I_PICTURE;
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns;
movieFragmentBox.ToBuffer(ref writer);

//mdat
mediaDataBox.ToBuffer(ref writer);

var current2 = writer.GetCurrentPosition();
foreach (var postion in segmentIndexBox.ReferencedSizePositions)
{
writer.WriteUInt32Return((uint)(current2 - current1), postion);
}

var data = writer.FlushAndGetArray();
return data;
}


+ 3
- 24
src/JT1078.FMp4/JT1078.FMp4.xml Wyświetl plik

@@ -1348,26 +1348,6 @@
FMp4编码
fmp4
stream data
ftyp
moov
mvex
trex Video
trex Audio
styp 1
sidx 1 Video
sidx 1 Audio
moof 1
traf 1 Video
traf 1 Audio
mdat 1
...
styp n
sidx 1 Video
sidx n Audio
moof n
traf n Video
traf n Audio
mdat n
ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing
</summary>
</member>
@@ -1404,16 +1384,15 @@
</summary>
<returns></returns>
</member>
<member name="M:JT1078.FMp4.FMp4Encoder.FirstVideoBox(JT1078.Protocol.H264.H264NALU@,JT1078.Protocol.H264.H264NALU@)">
<member name="M:JT1078.FMp4.FMp4Encoder.FirstVideoBox(JT1078.Protocol.JT1078AVFrame@)">
<summary>
编码首帧
ftyp moov
</summary>
<param name="sps"></param>
<param name="pps"></param>
<param name="avframe"></param>
<returns></returns>
</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.H264.H264NALU}@,System.String@,System.Boolean@)">
<summary>
编码其他视频数据盒子
styp sidx moof mdat


+ 3
- 3
src/JT1078.Flv.Test/JT1078.Flv.Test.csproj Wyświetl plik

@@ -5,13 +5,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>


+ 104
- 0
src/JT1078.Flv/FlvEncoder.cs Wyświetl plik

@@ -68,6 +68,7 @@ namespace JT1078.Flv
/// <param name="hasAudio">是否有音频</param>
/// <param name="frameRate">帧率 默认25d 即每秒25帧</param>
/// <returns></returns>
[Obsolete("use EncoderScriptTag(JT1078AVFrame avframe, bool hasAudio = false, double frameRate = 25d)")]
public byte[] EncoderScriptTag(SPSInfo spsInfo, bool hasAudio = false, double frameRate = 25d)
{
byte[] buffer = FlvArrayPool.Rent(1024);
@@ -114,6 +115,60 @@ namespace JT1078.Flv
}
}

/// <summary>
/// 编码脚本Tag
/// </summary>
/// <param name="avframe">解析后的av信息</param>
/// <param name="hasAudio">是否有音频</param>
/// <param name="frameRate">帧率 默认25d 即每秒25帧</param>
/// <returns></returns>

public byte[] EncoderScriptTag(JT1078AVFrame avframe, bool hasAudio = false, double frameRate = 25d)
{
byte[] buffer = FlvArrayPool.Rent(1024);
try
{
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
//flv body script tag
//flv body tag header
FlvTags flvTags = new FlvTags
{
Type = TagType.ScriptData,
//flv body tag body
DataTagsData = new Amf3
{
Amf3Metadatas = new List<IAmf3Metadata>
{
new Amf3Metadata_Duration{Value = 0d},
new Amf3Metadata_VideoDataRate{Value = 0d},
new Amf3Metadata_VideoCodecId{Value = 7d},
new Amf3Metadata_FrameRate{Value = frameRate},
new Amf3Metadata_Width(){
Value=avframe.Width
},
new Amf3Metadata_Height(){
Value=avframe.Height
},
}
}
};
if (hasAudio)
{
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioCodecId());
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleRate());
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleSize());
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioStereo());
}
flvMessagePackWriter.WriteFlvTag(flvTags);
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
return flvMessagePackWriter.FlushAndGetArray();
}
finally
{
FlvArrayPool.Return(buffer);
}
}

/// <summary>
/// 编码首帧视频,即videoTag[0]
/// </summary>
@@ -122,6 +177,7 @@ namespace JT1078.Flv
/// <param name="pps"></param>
/// <param name="sei"></param>
/// <returns></returns>
[Obsolete("use EncoderFirstVideoTag(JT1078AVFrame avframe)")]
public byte[] EncoderFirstVideoTag(SPSInfo spsInfo, H264NALU sps, H264NALU pps, H264NALU sei)
{
byte[] buffer = FlvArrayPool.Rent(4096);
@@ -165,6 +221,54 @@ namespace JT1078.Flv
}
}

/// <summary>
/// 编码首帧视频,即videoTag[0]
/// </summary>
/// <param name="avframe"></param>
/// <returns></returns>
public byte[] EncoderFirstVideoTag(JT1078AVFrame avframe)
{
byte[] buffer = FlvArrayPool.Rent(4096);
try
{
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
//flv body video tag
//flv body tag header
FlvTags flvTags = new FlvTags
{
Type = TagType.Video,
Timestamp = (uint)avframe.SPS.Timestamp,
TimestampExt = 0,
StreamId = 0,
//flv body tag body
VideoTagsData = new VideoTags()
};
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
flvTags.VideoTagsData.VideoData = new AvcVideoPacke
{
AvcPacketType = AvcPacketType.SequenceHeader,
CompositionTime = 0
};
AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord
{
AVCProfileIndication = avframe.ProfileIdc,
ProfileCompatibility = avframe.ProfileCompat,
AVCLevelIndication = avframe.LevelIdc,
NumOfPictureParameterSets = 1,
PPSBuffer = avframe.PPS.RawData,
SPSBuffer = avframe.SPS.RawData
};
flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord;
flvMessagePackWriter.WriteFlvTag(flvTags);
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
return flvMessagePackWriter.FlushAndGetArray();
}
finally
{
FlvArrayPool.Return(buffer);
}
}

/// <summary>
/// 编码首帧音频,即audioTag[0]
/// </summary>


+ 16
- 0
src/JT1078.Flv/JT1078.Flv.xml Wyświetl plik

@@ -203,6 +203,15 @@
<param name="frameRate">帧率 默认25d 即每秒25帧</param>
<returns></returns>
</member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(JT1078.Protocol.JT1078AVFrame,System.Boolean,System.Double)">
<summary>
编码脚本Tag
</summary>
<param name="avframe">解析后的av信息</param>
<param name="hasAudio">是否有音频</param>
<param name="frameRate">帧率 默认25d 即每秒25帧</param>
<returns></returns>
</member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.MessagePack.SPSInfo,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)">
<summary>
编码首帧视频,即videoTag[0]
@@ -213,6 +222,13 @@
<param name="sei"></param>
<returns></returns>
</member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.JT1078AVFrame)">
<summary>
编码首帧视频,即videoTag[0]
</summary>
<param name="avframe"></param>
<returns></returns>
</member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)">
<summary>
编码首帧音频,即audioTag[0]


+ 4
- 4
src/JT1078.Hls.Test/JT1078.Hls.Test.csproj Wyświetl plik

@@ -15,14 +15,14 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>


+ 1
- 1
src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj Wyświetl plik

@@ -12,7 +12,7 @@
<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.Memory" Version="4.5.4" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JT1078.Flv\JT1078.Flv.csproj" />


+ 3
- 3
src/JT1078.Protocol.Test/JT1078.Protocol.Test.csproj Wyświetl plik

@@ -7,10 +7,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JT808" Version="2.4.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="JT808" Version="2.5.0-preview1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>


+ 95
- 1
src/JT1078.Protocol/H264/H264Decoder.cs Wyświetl plik

@@ -1,4 +1,5 @@
using System;
using JT1078.Protocol.MessagePack;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -67,6 +68,99 @@ namespace JT1078.Protocol.H264
return h264NALUs;
}

/// <summary>
///
/// </summary>
/// <param name="package"></param>
/// <param name="key"></param>
/// <returns></returns>
public JT1078AVFrame ParseAVFrame(JT1078Package package, string key = null)
{
JT1078AVFrame jT1078AVFrame = new JT1078AVFrame();
jT1078AVFrame.LogicChannelNumber = package.LogicChannelNumber;
jT1078AVFrame.SIM = package.SIM;
jT1078AVFrame.Nalus = new List<H264NALU>();
int i = 0, state = 0, laststate = 0;
int? lastIndex = null;
int length = package.Bodies.Length;
byte value;
ReadOnlySpan<byte> buffer = package.Bodies;
while (i < length)
{
value = buffer[i++];
switch (state)
{
case 0:
if (value == 0) state = 1;
break;
case 1:
state = value == 0 ? 2 : 0;
break;
case 2:
case 3:
if (value == 0)
{
state = 3;
}
else if (value == 1 && i < length)
{
if (lastIndex.HasValue)
{
var tmp = buffer.Slice(lastIndex.Value, i - state - 1 - lastIndex.Value);
H264NALU nalu = Create(package, tmp, state + 1);
if (nalu.NALUHeader.NalUnitType == NalUnitType.PPS)
{
jT1078AVFrame.PPS = nalu;
}
else if (nalu.NALUHeader.NalUnitType == NalUnitType.SPS)
{
jT1078AVFrame.SPS = nalu;
ExpGolombReader h264GolombReader = new ExpGolombReader(jT1078AVFrame.SPS.RawData);
var spsInfo = h264GolombReader.ReadSPS();
jT1078AVFrame.Width = spsInfo.width;
jT1078AVFrame.Height = spsInfo.height;
jT1078AVFrame.LevelIdc= spsInfo.levelIdc;
jT1078AVFrame.ProfileIdc=spsInfo.profileIdc;
jT1078AVFrame.ProfileCompat=(byte)spsInfo.profileCompat;
}
jT1078AVFrame.Nalus.Add(nalu);
}
lastIndex = i;
laststate = state + 1;
state = 0;
}
else
{
state = 0;
}
break;
default:
break;
}
}
if (lastIndex.HasValue)
{
H264NALU nalu = Create(package, buffer.Slice(lastIndex.Value), laststate);
if(nalu.NALUHeader.NalUnitType== NalUnitType.PPS)
{
jT1078AVFrame.PPS = nalu;
}
else if(nalu.NALUHeader.NalUnitType == NalUnitType.SPS)
{
jT1078AVFrame.SPS = nalu;
ExpGolombReader h264GolombReader = new ExpGolombReader(jT1078AVFrame.SPS.RawData);
var spsInfo = h264GolombReader.ReadSPS();
jT1078AVFrame.Width = spsInfo.width;
jT1078AVFrame.Height = spsInfo.height;
jT1078AVFrame.LevelIdc = spsInfo.levelIdc;
jT1078AVFrame.ProfileIdc = spsInfo.profileIdc;
jT1078AVFrame.ProfileCompat = (byte)spsInfo.profileCompat;
}
jT1078AVFrame.Nalus.Add(nalu);
}
return jT1078AVFrame;
}

/// <summary>
///
/// <see cref="https://github.com/samirkumardas/jmuxer/blob/master/src/parsers/h264.js"/>


+ 8
- 1
src/JT1078.Protocol/H264/H264NALU.cs Wyświetl plik

@@ -5,7 +5,7 @@ using System.Text;

namespace JT1078.Protocol.H264
{
public class H264NALU
public class H264NALU: IJT1078AVKey
{
public readonly static byte[] Start1 = new byte[3] { 0, 0, 1 };
public readonly static byte[] Start2 = new byte[4] { 0, 0, 0, 1 };
@@ -44,6 +44,13 @@ namespace JT1078.Protocol.H264
/// 数据体
/// </summary>
public byte[] RawData { get; set; }

public string GetAVKey()
{
return $"{SIM}_{LogicChannelNumber.ToString()}";
}

[Obsolete("use GetAVKey")]
public string GetKey()
{
return $"{SIM}_{LogicChannelNumber.ToString()}";


+ 13
- 0
src/JT1078.Protocol/IJT1078AVKey.cs Wyświetl plik

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JT1078.Protocol
{
public interface IJT1078AVKey
{
string GetAVKey();
}
}

+ 20
- 1
src/JT1078.Protocol/JT1078.Protocol.xml Wyświetl plik

@@ -122,6 +122,14 @@
<param name="key"></param>
<returns></returns>
</member>
<member name="M:JT1078.Protocol.H264.H264Decoder.ParseAVFrame(JT1078.Protocol.JT1078Package,System.String)">
<summary>
</summary>
<param name="package"></param>
<param name="key"></param>
<returns></returns>
</member>
<member name="M:JT1078.Protocol.H264.H264Decoder.ParseNALU(JT1078.Protocol.JT1078Package,System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU},System.String)">
<summary>
@@ -181,6 +189,17 @@
数据体
</summary>
</member>
<member name="P:JT1078.Protocol.JT1078AVFrame.SIM">
<summary>
终端设备SIM卡号
BCD[6]
</summary>
</member>
<member name="P:JT1078.Protocol.JT1078AVFrame.LogicChannelNumber">
<summary>
逻辑通道号
</summary>
</member>
<member name="T:JT1078.Protocol.JT1078Label1">
<summary>
V - 2 - 固定为2
@@ -322,7 +341,7 @@
</member>
<member name="P:JT1078.Protocol.JT1078Package.LastFrameInterval">
<summary>
该帧与上一个关键帧之间的时间间隔,单位毫秒(ms),
该帧与上一帧之间的时间间隔,单位毫秒(ms),
当数据类型为非视频帧时,则没有该字段
</summary>
</member>


+ 39
- 0
src/JT1078.Protocol/JT1078AVFrame.cs Wyświetl plik

@@ -0,0 +1,39 @@
using JT1078.Protocol.H264;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JT1078.Protocol
{
public class JT1078AVFrame : IJT1078AVKey
{
/// <summary>
/// 终端设备SIM卡号
/// BCD[6]
/// </summary>
public string SIM { get; set; }
/// <summary>
/// 逻辑通道号
/// </summary>
public byte LogicChannelNumber { get; set; }
public List<H264NALU> Nalus { get; set; }
public H264NALU SPS { get; set; }
public H264NALU PPS { get; set; }
public int Width { get; set; }
public int Height { get; set; }
string VideoType { get; set; } = "avc1";
public byte ProfileIdc { get; set; }
public byte ProfileCompat { get; set; }
public byte LevelIdc { get; set; }
public string ToCodecs()
{
return $"{VideoType}.{ProfileIdc:x2}{ProfileCompat:x2}{LevelIdc:x2}";
}
public string GetAVKey()
{
return $"{SIM}_{LogicChannelNumber.ToString()}";
}
}
}

+ 8
- 2
src/JT1078.Protocol/JT1078Package.cs Wyświetl plik

@@ -4,7 +4,7 @@ using System.Text;

namespace JT1078.Protocol
{
public class JT1078Package
public class JT1078Package: IJT1078AVKey
{
/// <summary>
/// 帧头标识
@@ -74,7 +74,7 @@ namespace JT1078.Protocol
/// </summary>
public ushort LastIFrameInterval { get; set; }
/// <summary>
/// 该帧与上一个关键帧之间的时间间隔,单位毫秒(ms),
/// 该帧与上一帧之间的时间间隔,单位毫秒(ms),
/// 当数据类型为非视频帧时,则没有该字段
/// </summary>
public ushort LastFrameInterval { get; set; }
@@ -87,9 +87,15 @@ namespace JT1078.Protocol
/// </summary>
public byte[] Bodies{ get; set; }

[Obsolete("use GetAVKey()")]
public string GetKey()
{
return $"{SIM}_{LogicChannelNumber.ToString()}";
}

public string GetAVKey()
{
return $"{SIM}_{LogicChannelNumber.ToString()}";
}
}
}

+ 23
- 36
src/JT1078.SignalR.Test/Services/ToWebSocketService.cs Wyświetl plik

@@ -18,6 +18,7 @@ using System.IO;
using JT1078.Protocol.Extensions;
using JT1078.Protocol.H264;
using System.Net.WebSockets;
using JT1078.Protocol.Enums;

namespace JT1078.SignalR.Test.Services
{
@@ -49,7 +50,7 @@ namespace JT1078.SignalR.Test.Services

public List<byte[]> q = new List<byte[]>();

public void a()
void Init()
{
List<JT1078Package> packages = new List<JT1078Package>();
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_6.txt"));
@@ -66,12 +67,10 @@ namespace JT1078.SignalR.Test.Services
}
}

var nalus1 = h264Decoder.ParseNALU(packages[0]);
q.Add(fMp4Encoder.FirstVideoBox(
nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS),
nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)));
var avframe = h264Decoder.ParseAVFrame(packages[0]);
q.Add(fMp4Encoder.FirstVideoBox(avframe));

List<H264NALU> stream = new List<H264NALU>();
Queue<Mp4Frame> mp4Frames = new Queue<Mp4Frame>();
List<NalUnitType> filter = new List<NalUnitType>();
filter.Add(NalUnitType.SEI);
filter.Add(NalUnitType.PPS);
@@ -80,47 +79,35 @@ namespace JT1078.SignalR.Test.Services
foreach (var package in packages)
{
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package);
if (h264NALUs!=null && h264NALUs.Count>0)
if (h264NALUs != null && h264NALUs.Count > 0)
{
stream.AddRange(h264NALUs.Where(w => !filter.Contains(w.NALUHeader.NalUnitType)));
Mp4Frame mp4Frame = new Mp4Frame
{
Key = package.GetKey(),
KeyFrame = package.Label3.DataType == JT1078DataType.视频I帧
};
mp4Frame.NALUs = h264NALUs;
mp4Frames.Enqueue(mp4Frame);
}
}

List<H264NALU> tmp = new List<H264NALU>();
H264NALU prevNalu = null;
foreach (var item in stream)
while (mp4Frames.TryDequeue(out Mp4Frame frame))
{
if (item.NALUHeader.KeyFrame)
{
if (tmp.Count>0)
{
q.Add(fMp4Encoder.OtherVideoBox(tmp));
tmp.Clear();
}
tmp.Add(item);
q.Add(fMp4Encoder.OtherVideoBox(tmp));
tmp.Clear();
prevNalu=item;
continue;
}
if (prevNalu!=null) //第一帧I帧
{
if (tmp.Count>1)
{
q.Add(fMp4Encoder.OtherVideoBox(tmp));
tmp.Clear();
}
tmp.Add(item);
}
q.Add(fMp4Encoder.OtherVideoBox(frame.NALUs, frame.Key, frame.KeyFrame));
}
}

class Mp4Frame
{
public string Key { get; set; }
public bool KeyFrame { get; set; }
public List<H264NALU> NALUs { get; set; }
}

public Dictionary<string,int> flag = new Dictionary<string, int>();

protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
a();
Init();
while (!stoppingToken.IsCancellationRequested)
{
try
@@ -149,7 +136,7 @@ namespace JT1078.SignalR.Test.Services
{
logger.LogError(ex,"");
}
await Task.Delay(80);
await Task.Delay(60);
}
}
}


Ładowanie…
Anuluj
Zapisz