From ae625cacc2c1cc75fd7cc131138325d5b0d95ed0 Mon Sep 17 00:00:00 2001 From: smallchi <564952747@qq.com> Date: Wed, 25 Sep 2019 13:42:57 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0AVCDecoderConfigurationReco?= =?UTF-8?q?rd=E5=86=99=E5=85=A5=E5=99=A8=202.=E5=A2=9E=E5=8A=A0H264NALU?= =?UTF-8?q?=E7=9A=84=E8=A7=A3=E6=9E=90=E5=8F=8A=E5=88=9B=E5=BB=BA=203.?= =?UTF-8?q?=E5=A2=9E=E5=8A=A01078=E7=BB=84=E5=8C=85=E6=94=B9=E7=94=A8byte?= =?UTF-8?q?=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/JT1078.Flv/FlvMuxer.cs | 27 +++- src/JT1078.Flv/H264/H264Demuxer.cs | 121 +++++++++++++++++- src/JT1078.Flv/H264/H264NALU.cs | 42 +++++- src/JT1078.Flv/MessagePack/EXPGolombReader.cs | 3 +- .../MessagePack/FlvMessagePackWriter_Flv.cs | 15 +++ .../Metadata/AVCDecoderConfigurationRecord.cs | 12 -- .../JT1078.Protocol.Benchmark.csproj | 2 +- src/JT1078.Protocol/JT1078Demuxer.cs | 20 ++- src/JT1078.sln | 2 +- 9 files changed, 221 insertions(+), 23 deletions(-) diff --git a/src/JT1078.Flv/FlvMuxer.cs b/src/JT1078.Flv/FlvMuxer.cs index a9b0589..0be9839 100644 --- a/src/JT1078.Flv/FlvMuxer.cs +++ b/src/JT1078.Flv/FlvMuxer.cs @@ -7,7 +7,7 @@ namespace JT1078.Flv { public class FlvMuxer { - private readonly FlvHeader VideoFlvHeader = new FlvHeader(true, false); + private static readonly FlvHeader VideoFlvHeader = new FlvHeader(true, false); public byte[] FlvFirstFrame() { byte[] buffer = FlvArrayPool.Rent(10240); @@ -17,7 +17,7 @@ namespace JT1078.Flv //flv header flvMessagePackWriter.WriteArray(VideoFlvHeader.ToArray()); //flv body - //flv body PreviousTagSize + //flv body PreviousTagSize awalys 0 flvMessagePackWriter.WriteUInt32(0); //flv body tag @@ -32,5 +32,28 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } + public byte[] FlvOtherFrame() + { + byte[] buffer = FlvArrayPool.Rent(10240); + try + { + FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); + + //flv body + //flv body PreviousTagSize + + //flv body tag + + //flv body tag header + + //flv body tag body + + return flvMessagePackWriter.FlushAndGetArray(); + } + finally + { + FlvArrayPool.Return(buffer); + } + } } } diff --git a/src/JT1078.Flv/H264/H264Demuxer.cs b/src/JT1078.Flv/H264/H264Demuxer.cs index 4cc5015..edf87c5 100644 --- a/src/JT1078.Flv/H264/H264Demuxer.cs +++ b/src/JT1078.Flv/H264/H264Demuxer.cs @@ -11,8 +11,127 @@ namespace JT1078.Flv.H264 { public class H264Demuxer { - public const string codecstring = "avc1."; + public List ParseNALU(JT1078Package package) + { + List units = new List(); + int offset = 0; + (int previousOffset, int previousContentOffset) previous = (0, 0); + int len = package.Bodies.Length; + ReadOnlySpan tmpBuffer = package.Bodies; + int index = 0; + while (offset < len) + { + if ((len - offset - 3) < 0 || (len - offset - 4) < 0) + { + if (previous.previousOffset == 0 && previous.previousContentOffset == 0) + { + int startCodePrefix=4; + if (tmpBuffer.Slice(0, 3).SequenceEqual(H264NALU.Start1)) + { + startCodePrefix = 3; + } + units.Add(Create(package, tmpBuffer.Slice(offset, 1), startCodePrefix)); + units[index++].RawData = tmpBuffer.ToArray(); + } + else + { + units[index++].RawData = tmpBuffer.Slice(previous.previousContentOffset + (previous.previousOffset - previous.previousContentOffset)).ToArray(); + } + break; + } + if (tmpBuffer.Slice(offset, 3).SequenceEqual(H264NALU.Start1)) + { + offset += 3; + if ((offset - 3) != 0) + { + units[index++].RawData = tmpBuffer.Slice(previous.previousContentOffset + 3, offset - previous.previousOffset - 3).ToArray(); + } + units.Add(Create(package, tmpBuffer.Slice(offset, 1), 3)); + previous = (offset, offset - 3); + } + else if (tmpBuffer.Slice(offset, 4).SequenceEqual(H264NALU.Start2)) + { + offset += 4; + if ((offset - 4) != 0) + { + units[index++].RawData = tmpBuffer.Slice(previous.previousContentOffset + 4, offset - previous.previousOffset - 4).ToArray(); + } + units.Add(Create(package, tmpBuffer.Slice(offset, 1), 4)); + previous = (offset, offset - 4); + } + else + { + offset++; + } + } + return units; + } + + private H264NALU Create(JT1078Package package,ReadOnlySpan naluheader, int startCodePrefix) + { + H264NALU nALU = new H264NALU(); + nALU.SIM = package.SIM; + nALU.Label3 = package.Label3; + nALU.LogicChannelNumber = package.LogicChannelNumber; + nALU.LastFrameInterval = package.LastFrameInterval; + nALU.LastIFrameInterval = package.LastIFrameInterval; + if (startCodePrefix == 3) + { + nALU.StartCodePrefix = H264NALU.Start1; + } + else if (startCodePrefix == 4) + { + nALU.StartCodePrefix = H264NALU.Start2; + } + nALU.NALUHeader = new NALUHeader(naluheader); + return nALU; + } + + /// + /// Identify NAL unit types and pass on the NALU, trackId, presentation and decode timestamps + /// for the NALUs to the next stream component. + /// Also, preprocess caption and sequence parameter NALUs. + /// 常用Nalu_type: + /// 0x67 (0 11 00111) SPS 非常重要 type = 7 + /// 0x68 (0 11 01000) PPS 非常重要 type = 8 + /// 0x65 (0 11 00101) IDR帧 关键帧(非常重要) type = 5 + /// 0x61 (0 11 00001) I帧 重要 type = 1 非IDR的I帧不大常见 + /// 0x41 (0 10 00001) P帧 重要 type = 1 + /// 0x01 (0 00 00001) B帧 不重要 type = 1 + /// 0x06 (0 00 00110) SEI 不重要 type = 6 + /// + /// + /// + /// + public void NALUTypeFilter(H264NALU h264NALU) + { + switch (h264NALU.NALUHeader.NalUnitType) + { + //IDR + case 5: + + break; + case 6: + h264NALU.RawData = DiscardEmulationPreventionBytes(h264NALU.RawData); + break; + //SPS + case 7: + h264NALU.RawData = DiscardEmulationPreventionBytes(h264NALU.RawData); + ExpGolombReader h264GolombReader = new ExpGolombReader(h264NALU.RawData); + var spsInfo = h264GolombReader.ReadSPS(); + + break; + //PPS + case 8: + + break; + //AUD + case 9: + + break; + } + } /// /// Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload" diff --git a/src/JT1078.Flv/H264/H264NALU.cs b/src/JT1078.Flv/H264/H264NALU.cs index cde2ca6..69aa2d1 100644 --- a/src/JT1078.Flv/H264/H264NALU.cs +++ b/src/JT1078.Flv/H264/H264NALU.cs @@ -11,6 +11,46 @@ namespace JT1078.Flv.H264 public readonly static byte[] Start2 = new byte[4] { 0, 0, 0, 1 }; public byte[] StartCodePrefix { get; set; } public NALUHeader NALUHeader { get; set; } - public JT1078Package JT1078Package { get; set; } + /// + /// 终端设备SIM卡号 + /// BCD[6] + /// + public string SIM { get; set; } + /// + /// 逻辑通道号 + /// + public byte LogicChannelNumber { get; set; } + /// + /// 数据类型 + /// 0000:视频I帧 + /// 0001:视频P帧 + /// 0010:视频B帧 + /// 0011:音频帧 + /// 0100:透传数据 + /// + /// 0000:原子包,不可被拆分 + /// 0001:分包处理时的第一个包 + /// 0010:分包处理是的最后一个包 + /// 0011:分包处理时间的中间包 + /// + public JT1078Label3 Label3 { get; set; } + /// + /// 该帧与上一个关键帧之间的时间间隔,单位毫秒(ms), + /// 当数据类型为非视频帧时,则没有该字段 + /// + public ushort LastIFrameInterval { get; set; } + /// + /// 该帧与上一个关键帧之间的时间间隔,单位毫秒(ms), + /// 当数据类型为非视频帧时,则没有该字段 + /// + public ushort LastFrameInterval { get; set; } + /// + /// 数据体 + /// + public byte[] RawData { get; set; } + public string GetKey() + { + return $"{SIM}_{LogicChannelNumber.ToString()}"; + } } } diff --git a/src/JT1078.Flv/MessagePack/EXPGolombReader.cs b/src/JT1078.Flv/MessagePack/EXPGolombReader.cs index 00c1a38..2a91f18 100644 --- a/src/JT1078.Flv/MessagePack/EXPGolombReader.cs +++ b/src/JT1078.Flv/MessagePack/EXPGolombReader.cs @@ -63,7 +63,8 @@ namespace JT1078.Flv.MessagePack for (int i = 0; i < scalingListCount; i++) { if (ReadBoolean()) - { // seq_scaling_list_present_flag[ i ] + { + // seq_scaling_list_present_flag[ i ] if (i < 6) { SkipScalingList(16); diff --git a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs index aac079f..27f394b 100644 --- a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs +++ b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs @@ -97,5 +97,20 @@ namespace JT1078.Flv.MessagePack //Empty } } + + public void WriteAVCDecoderConfigurationRecord(AVCDecoderConfigurationRecord configurationRecord) + { + WriteByte(configurationRecord.ConfigurationVersion); + WriteByte(configurationRecord.AVCProfileIndication); + WriteByte(configurationRecord.ProfileCompatibility); + WriteByte(configurationRecord.AVCLevelIndication); + WriteByte((byte)configurationRecord.LengthSizeMinusOne); + WriteByte((byte)configurationRecord.NumOfSequenceParameterSets); + WriteUInt16((ushort)configurationRecord.SPSBuffer.Length); + WriteArray(configurationRecord.SPSBuffer); + WriteByte(configurationRecord.NumOfPictureParameterSets); + WriteUInt16((ushort)configurationRecord.PPSBuffer.Length); + WriteArray(configurationRecord.PPSBuffer); + } } } diff --git a/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs b/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs index 625d386..a279f92 100644 --- a/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs +++ b/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs @@ -39,10 +39,8 @@ namespace JT1078.Flv.Metadata public byte AVCLevelIndication { get; set; } public int LengthSizeMinusOne { get; set; } public int NumOfSequenceParameterSets { get; set; } - public List SPS { get; set; } public byte[] SPSBuffer { get; set; } public byte NumOfPictureParameterSets { get; set; } = 1; - public List PPS { get; set; } public byte[] PPSBuffer { get; set; } #region Just for non-spec-conform encoders ref:org.mp4parser.boxes.iso14496.part15.AvcDecoderConfigurationRecord public const int LengthSizeMinusOnePaddingBits = 63; @@ -51,15 +49,5 @@ namespace JT1078.Flv.Metadata public const int BitDepthLumaMinus8PaddingBits = 31; public const int BitDepthChromaMinus8PaddingBits = 31; #endregion - public struct SPSInfo - { - public ushort SequenceParameterSetLength { get; set; } - public byte[] SequenceParameterSetNALUnit { get; set; } - } - public struct PPSInfo - { - public ushort PictureParameterSetLength { get; set; } - public byte[] PictureParameterSetNALUnit { get; set; } - } } } diff --git a/src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj b/src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj index d68e8af..2c39ed6 100644 --- a/src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj +++ b/src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2;net472 + netcoreapp2.2;net472;netcoreapp3.0; true AnyCPU Exe diff --git a/src/JT1078.Protocol/JT1078Demuxer.cs b/src/JT1078.Protocol/JT1078Demuxer.cs index 6a6a20b..91df8c4 100644 --- a/src/JT1078.Protocol/JT1078Demuxer.cs +++ b/src/JT1078.Protocol/JT1078Demuxer.cs @@ -23,16 +23,28 @@ namespace JT1078.Protocol { if (JT1078PackageGroupDict.TryGetValue(cacheKey, out var tmpPackage)) { - tmpPackage.Bodies.Concat(jT1078Package.Bodies).ToArray(); - JT1078PackageGroupDict[cacheKey] = tmpPackage; + var totalLength = tmpPackage.Bodies.Length + jT1078Package.Bodies.Length; + byte[] poolBytes = JT1078ArrayPool.Rent(totalLength); + Span tmpSpan = poolBytes; + tmpPackage.Bodies.CopyTo(tmpSpan); + jT1078Package.Bodies.CopyTo(tmpSpan.Slice(tmpPackage.Bodies.Length)); + tmpPackage.Bodies= tmpSpan.Slice(0, totalLength).ToArray(); + JT1078ArrayPool.Return(poolBytes); + JT1078PackageGroupDict[cacheKey] = jT1078Package; } return default; } else if (jT1078Package.Label3.SubpackageType == JT1078SubPackageType.分包处理时的最后一个包) { - if (JT1078PackageGroupDict.TryGetValue(cacheKey, out var tmpPackage)) + if(JT1078PackageGroupDict.TryRemove(cacheKey, out var tmpPackage)) { - tmpPackage.Bodies.Concat(jT1078Package.Bodies).ToArray(); + var totalLength = tmpPackage.Bodies.Length + jT1078Package.Bodies.Length; + byte[] poolBytes = JT1078ArrayPool.Rent(totalLength); + Span tmpSpan = poolBytes; + tmpPackage.Bodies.CopyTo(tmpSpan); + jT1078Package.Bodies.CopyTo(tmpSpan.Slice(tmpPackage.Bodies.Length)); + tmpPackage.Bodies = tmpSpan.Slice(0, totalLength).ToArray(); + JT1078ArrayPool.Return(poolBytes); return tmpPackage; } return default; diff --git a/src/JT1078.sln b/src/JT1078.sln index 3e17536..95a1c71 100644 --- a/src/JT1078.sln +++ b/src/JT1078.sln @@ -29,7 +29,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Protocol.Extensions.W EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Flv", "JT1078.Flv\JT1078.Flv.csproj", "{33E54FFC-7D91-42E5-9DC1-853738AB8980}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT1078.Flv.Test", "JT1078.Flv.Test\JT1078.Flv.Test.csproj", "{D13FE092-1D11-4545-A322-9F06BCDAC0FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Flv.Test", "JT1078.Flv.Test\JT1078.Flv.Test.csproj", "{D13FE092-1D11-4545-A322-9F06BCDAC0FD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution