Sfoglia il codice sorgente

1.增加AVCDecoderConfigurationRecord写入器

2.增加H264NALU的解析及创建
3.增加1078组包改用byte池
tags/v1.1.0
smallchi 5 anni fa
parent
commit
ae625cacc2
9 ha cambiato i file con 221 aggiunte e 23 eliminazioni
  1. +25
    -2
      src/JT1078.Flv/FlvMuxer.cs
  2. +120
    -1
      src/JT1078.Flv/H264/H264Demuxer.cs
  3. +41
    -1
      src/JT1078.Flv/H264/H264NALU.cs
  4. +2
    -1
      src/JT1078.Flv/MessagePack/EXPGolombReader.cs
  5. +15
    -0
      src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs
  6. +0
    -12
      src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs
  7. +1
    -1
      src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj
  8. +16
    -4
      src/JT1078.Protocol/JT1078Demuxer.cs
  9. +1
    -1
      src/JT1078.sln

+ 25
- 2
src/JT1078.Flv/FlvMuxer.cs Vedi File

@@ -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);
}
}
}
}

+ 120
- 1
src/JT1078.Flv/H264/H264Demuxer.cs Vedi File

@@ -11,8 +11,127 @@ namespace JT1078.Flv.H264
{
public class H264Demuxer
{
public const string codecstring = "avc1.";
public List<H264NALU> ParseNALU(JT1078Package package)
{
List<H264NALU> units = new List<H264NALU>();
int offset = 0;
(int previousOffset, int previousContentOffset) previous = (0, 0);
int len = package.Bodies.Length;
ReadOnlySpan<byte> 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<byte> 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;
}

/// <summary>
/// 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
/// <see cref="https://blog.csdn.net/huibailingyu/article/details/42879573"/>
/// </summary>
/// <param name="h264NALU"></param>
/// <returns></returns>
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;
}
}

/// <summary>
/// Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload"


+ 41
- 1
src/JT1078.Flv/H264/H264NALU.cs Vedi File

@@ -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; }
/// <summary>
/// 终端设备SIM卡号
/// BCD[6]
/// </summary>
public string SIM { get; set; }
/// <summary>
/// 逻辑通道号
/// </summary>
public byte LogicChannelNumber { get; set; }
/// <summary>
/// 数据类型
/// 0000:视频I帧
/// 0001:视频P帧
/// 0010:视频B帧
/// 0011:音频帧
/// 0100:透传数据
///
/// 0000:原子包,不可被拆分
/// 0001:分包处理时的第一个包
/// 0010:分包处理是的最后一个包
/// 0011:分包处理时间的中间包
/// </summary>
public JT1078Label3 Label3 { get; set; }
/// <summary>
/// 该帧与上一个关键帧之间的时间间隔,单位毫秒(ms),
/// 当数据类型为非视频帧时,则没有该字段
/// </summary>
public ushort LastIFrameInterval { get; set; }
/// <summary>
/// 该帧与上一个关键帧之间的时间间隔,单位毫秒(ms),
/// 当数据类型为非视频帧时,则没有该字段
/// </summary>
public ushort LastFrameInterval { get; set; }
/// <summary>
/// 数据体
/// </summary>
public byte[] RawData { get; set; }
public string GetKey()
{
return $"{SIM}_{LogicChannelNumber.ToString()}";
}
}
}

+ 2
- 1
src/JT1078.Flv/MessagePack/EXPGolombReader.cs Vedi File

@@ -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);


+ 15
- 0
src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs Vedi File

@@ -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);
}
}
}

+ 0
- 12
src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs Vedi File

@@ -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<SPSInfo> SPS { get; set; }
public byte[] SPSBuffer { get; set; }
public byte NumOfPictureParameterSets { get; set; } = 1;
public List<PPSInfo> 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; }
}
}
}

+ 1
- 1
src/JT1078.Protocol.Benchmark/JT1078.Protocol.Benchmark.csproj Vedi File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp2.2;net472</TargetFrameworks>
<TargetFrameworks>netcoreapp2.2;net472;netcoreapp3.0;</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputType>Exe</OutputType>


+ 16
- 4
src/JT1078.Protocol/JT1078Demuxer.cs Vedi File

@@ -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<byte> 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<byte> 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;


+ 1
- 1
src/JT1078.sln Vedi File

@@ -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


Caricamento…
Annulla
Salva