@@ -10,4 +10,6 @@ ffmpeg -i ipc.264 -vcodec copy -f mp4 -movflags frag_keyframe+empty_moov ipc_fra | |||
ffmpeg -i jt1078_3.h264 -c:v copy -f mp4 -movflags empty_moov+default_base_moof+frag_keyframe fragmented_base_moof_demo.mp4 | |||
ffprobe -of json -show_frames jt1078_3.h264 > jt1078_3.json | |||
chrome://media-internals/ |
@@ -456,6 +456,7 @@ namespace JT1078.FMp4.Test | |||
NalUnitType.PPS, | |||
NalUnitType.AUD}; | |||
int i = 0; | |||
List<H264NALU> h264NALUs = new List<H264NALU>(); | |||
foreach (var package in packages) | |||
{ | |||
var otherStypBuffer = fMp4Encoder.EncoderStypBox(); | |||
@@ -467,14 +468,6 @@ namespace JT1078.FMp4.Test | |||
// continue; | |||
//} | |||
//int length = filterOtherNalus.Sum(s => s.RawData.Length); | |||
foreach(var nalu in otherNalus) | |||
{ | |||
//H264 NALU slice first_mb_in_slice | |||
if ((nalu.RawData[1] & 0x80) == 0x80) | |||
{ | |||
} | |||
} | |||
var flag = package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u; | |||
var otherMoofBuffer = fMp4Encoder.EncoderMoofBox(otherNalus, package.Bodies.Length, package.Timestamp, package.LastFrameInterval, package.LastIFrameInterval, flag); | |||
var otherMdatBuffer = fMp4Encoder.EncoderMdatBox(otherNalus, package.Bodies.Length); | |||
@@ -488,6 +481,73 @@ namespace JT1078.FMp4.Test | |||
fileStream.Close(); | |||
} | |||
[Fact] | |||
public void Test5() | |||
{ | |||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | |||
H264Decoder h264Decoder = new H264Decoder(); | |||
var packages = ParseNALUTests(); | |||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_5.mp4"); | |||
if (File.Exists(filepath)) | |||
{ | |||
File.Delete(filepath); | |||
} | |||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||
var ftyp = fMp4Encoder.EncoderFtypBox(); | |||
fileStream.Write(ftyp); | |||
List<NalUnitType> filter = new List<NalUnitType>() { | |||
NalUnitType.SPS, | |||
NalUnitType.PPS, | |||
NalUnitType.AUD | |||
}; | |||
var iNalus=h264Decoder.ParseNALU(packages[0]); | |||
//判断第一帧是否关键帧 | |||
var moov = fMp4Encoder.EncoderMoovBox( | |||
iNalus.FirstOrDefault(f=>f.NALUHeader.NalUnitType== NalUnitType.SPS), | |||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | |||
fileStream.Write(moov); | |||
List<H264NALU> nalus = new List<H264NALU>(); | |||
foreach (var package in packages) | |||
{ | |||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||
foreach (var nalu in h264NALUs) | |||
{ | |||
if (nalu.Slice) | |||
{ | |||
//H264 NALU slice first_mb_in_slice | |||
nalus.Add(nalu); | |||
} | |||
else | |||
{ | |||
if (nalus.Count > 0) | |||
{ | |||
var iStypBuffer = fMp4Encoder.EncoderStypBox(); | |||
fileStream.Write(iStypBuffer); | |||
var firstNalu = nalus[0]; | |||
var flag = firstNalu.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u; | |||
int iSize = nalus.Where(w => w.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||
.Sum(s => s.RawData.Length + s.StartCodePrefix.Length); | |||
List<int> sizes = new List<int>(); | |||
sizes.Add(iSize); | |||
sizes = sizes.Concat(nalus.Where(w => w.DataType != Protocol.Enums.JT1078DataType.视频I帧) | |||
.Select(s => s.RawData.Length + s.StartCodePrefix.Length).ToList()) | |||
.ToList(); | |||
var iMoofBuffer = fMp4Encoder.EncoderMoofBox(sizes, firstNalu.Timestamp, firstNalu.LastFrameInterval, firstNalu.LastIFrameInterval, flag); | |||
var iMdatBuffer = fMp4Encoder.EncoderMdatBox(nalus.Select(s => s.RawData).ToList()); | |||
var iSidxBuffer = fMp4Encoder.EncoderSidxBox(iMoofBuffer.Length + iMdatBuffer.Length, firstNalu.Timestamp, firstNalu.LastIFrameInterval, firstNalu.LastFrameInterval); | |||
fileStream.Write(iSidxBuffer); | |||
fileStream.Write(iMoofBuffer); | |||
fileStream.Write(iMdatBuffer); | |||
nalus.Clear(); | |||
} | |||
nalus.Add(nalu); | |||
} | |||
} | |||
} | |||
fileStream.Close(); | |||
} | |||
[Fact] | |||
public void tkhd_width_height_test() | |||
{ | |||
@@ -38,6 +38,12 @@ namespace JT1078.FMp4 | |||
TrackFragmentBox.ToBuffer(ref writer); | |||
} | |||
End(ref writer); | |||
var moofOffsetPosition = writer.GetMoofOffsetPosition(); | |||
if (moofOffsetPosition > 0) | |||
{ | |||
writer.WriteUInt64Return((ulong)writer.GetCurrentPosition(), moofOffsetPosition); | |||
} | |||
writer.ClearMoofOffsetPosition(); | |||
var trunOffsetPosition = writer.GetTrunOffsetPosition(); | |||
if (trunOffsetPosition > 0) | |||
{ | |||
@@ -38,7 +38,7 @@ namespace JT1078.FMp4 | |||
MovieFragmentRandomAccessOffsetBox.ToBuffer(ref writer); | |||
} | |||
End(ref writer); | |||
var mfraSizePosition = writer.GetMfraSizePositionn(); | |||
var mfraSizePosition = writer.GetMfraSizePosition(); | |||
if (mfraSizePosition > 0) | |||
{ | |||
writer.WriteInt32Return(writer.GetCurrentPosition() - SizePosition, mfraSizePosition); | |||
@@ -60,7 +60,16 @@ namespace JT1078.FMp4 | |||
writer.WriteUInt32(TrackID); | |||
if ((FMp4Constants.TFHD_FLAG_BASE_DATA_OFFSET & Flags) > 0) | |||
{ | |||
writer.WriteUInt64(BaseDataOffset); | |||
if (BaseDataOffset > 0) | |||
{ | |||
writer.WriteUInt64(BaseDataOffset); | |||
} | |||
else | |||
{ | |||
//程序自动计算 | |||
writer.CreateMoofOffsetPosition(); | |||
writer.Skip(8, out _); | |||
} | |||
} | |||
if ((FMp4Constants.TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX & Flags) > 0) | |||
{ | |||
@@ -108,7 +108,9 @@ namespace JT1078.FMp4 | |||
} | |||
} | |||
uint IframeIntervalCache = 259960; | |||
ulong IframeIntervalCache = 259960; | |||
ulong cts = 0; | |||
/// <summary> | |||
/// 编码sidx盒子 | |||
@@ -122,7 +124,9 @@ namespace JT1078.FMp4 | |||
{ | |||
SegmentIndexBox segmentIndexBox = new SegmentIndexBox(1); | |||
segmentIndexBox.ReferenceID = 1; | |||
segmentIndexBox.EarliestPresentationTime = timestamp; | |||
cts = cts == 0 ? 2160000 : (cts + cts); | |||
segmentIndexBox.EarliestPresentationTime = cts; | |||
IframeIntervalCache += frameInterval; | |||
segmentIndexBox.SegmentIndexs = new List<SegmentIndexBox.SegmentIndex>() | |||
{ | |||
new SegmentIndexBox.SegmentIndex | |||
@@ -228,6 +232,88 @@ namespace JT1078.FMp4 | |||
} | |||
} | |||
/// <summary> | |||
/// 编码moov盒子 | |||
/// </summary> | |||
/// <returns></returns> | |||
public byte[] EncoderMoovBox(in H264NALU sps, in H264NALU pps) | |||
{ | |||
byte[] buffer = FMp4ArrayPool.Rent(sps.RawData.Length+ pps.RawData.Length + 4096); | |||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||
try | |||
{ | |||
ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData); | |||
var spsInfo = h264GolombReader.ReadSPS(); | |||
//moov | |||
MovieBox movieBox = new MovieBox(); | |||
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 2); | |||
movieBox.MovieHeaderBox.CreationTime = 0; | |||
movieBox.MovieHeaderBox.ModificationTime = 0; | |||
movieBox.MovieHeaderBox.Duration = 0; | |||
movieBox.MovieHeaderBox.Timescale = 1000; | |||
movieBox.MovieHeaderBox.NextTrackID = 99; | |||
movieBox.TrackBox = new TrackBox(); | |||
movieBox.TrackBox.TrackHeaderBox = new TrackHeaderBox(0, 3); | |||
movieBox.TrackBox.TrackHeaderBox.CreationTime = 0; | |||
movieBox.TrackBox.TrackHeaderBox.ModificationTime = 0; | |||
movieBox.TrackBox.TrackHeaderBox.TrackID = 1; | |||
movieBox.TrackBox.TrackHeaderBox.Duration = 0; | |||
movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false; | |||
movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width; | |||
movieBox.TrackBox.TrackHeaderBox.Height = (uint)spsInfo.height; | |||
movieBox.TrackBox.MediaBox = new MediaBox(); | |||
movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox(); | |||
movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 0; | |||
movieBox.TrackBox.MediaBox.MediaHeaderBox.ModificationTime = 0; | |||
movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1200000; | |||
movieBox.TrackBox.MediaBox.MediaHeaderBox.Duration = 0; | |||
movieBox.TrackBox.MediaBox.HandlerBox = new HandlerBox(); | |||
movieBox.TrackBox.MediaBox.HandlerBox.HandlerType = HandlerType.vide; | |||
movieBox.TrackBox.MediaBox.HandlerBox.Name = "VideoHandler"; | |||
movieBox.TrackBox.MediaBox.MediaInformationBox = new MediaInformationBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.VideoMediaHeaderBox = new VideoMediaHeaderBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox = new DataInformationBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox = new DataReferenceBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | |||
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>(); | |||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | |||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | |||
//h264 | |||
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 }; | |||
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(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox(); | |||
movieBox.MovieExtendsBox = new MovieExtendsBox(); | |||
movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>(); | |||
TrackExtendsBox trex = new TrackExtendsBox(); | |||
trex.TrackID = 1; | |||
trex.DefaultSampleDescriptionIndex = 1; | |||
trex.DefaultSampleDuration = 0; | |||
trex.DefaultSampleSize = 0; | |||
trex.DefaultSampleFlags = 0; | |||
movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex); | |||
movieBox.ToBuffer(ref writer); | |||
var data = writer.FlushAndGetArray(); | |||
return data; | |||
} | |||
finally | |||
{ | |||
FMp4ArrayPool.Return(buffer); | |||
} | |||
} | |||
/// <summary> | |||
/// 编码Moof盒子 | |||
/// </summary> | |||
@@ -283,6 +369,67 @@ namespace JT1078.FMp4 | |||
} | |||
} | |||
/// <summary> | |||
/// 编码Moof盒子 | |||
/// </summary> | |||
/// <returns></returns> | |||
public byte[] EncoderMoofBox(List<int> naluSzies, ulong timestamp, uint frameInterval, uint IframeInterval, uint keyframeFlag) | |||
{ | |||
byte[] buffer = FMp4ArrayPool.Rent(4096); | |||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||
try | |||
{ | |||
var movieFragmentBox = new MovieFragmentBox(); | |||
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | |||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = sn++; | |||
movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); | |||
//0x39 写文件 | |||
//0x02 分段 | |||
//0x2003a | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x2003a); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = 1; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = frameInterval; | |||
//movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = (uint)naluSzies[0]; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = 0x1010000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = cts; | |||
//trun | |||
//0x39 写文件 | |||
//0x02 分段 | |||
//0x205 | |||
//uint flag = 0x000200 | 0x000800 | 0x000400 | 0x000100; | |||
uint flag = 0x0001; | |||
if (!first) | |||
{ | |||
flag |= 0x0004; | |||
first = true; | |||
} | |||
flag |= 0x000200; | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: flag); | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 33554432; | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List<TrackRunBox.TrackRunInfo>(); | |||
foreach(var size in naluSzies) | |||
{ | |||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo() | |||
{ | |||
//SampleDuration= frameInterval, | |||
SampleSize = (uint)size, | |||
//SampleCompositionTimeOffset = frameInterval, | |||
//SampleFlags = movieFragmentBox.TrackFragmentBox.TrackRunBox.Flags | |||
}); | |||
} | |||
movieFragmentBox.ToBuffer(ref writer); | |||
var data = writer.FlushAndGetArray(); | |||
return data; | |||
} | |||
finally | |||
{ | |||
FMp4ArrayPool.Return(buffer); | |||
} | |||
} | |||
/// <summary> | |||
/// 编码Mdat盒子 | |||
/// </summary> | |||
@@ -305,6 +452,28 @@ namespace JT1078.FMp4 | |||
} | |||
} | |||
/// <summary> | |||
/// 编码Mdat盒子 | |||
/// </summary> | |||
/// <returns></returns> | |||
public byte[] EncoderMdatBox(List<byte[]> nalus) | |||
{ | |||
byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s=>s.Length) + 4096); | |||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||
try | |||
{ | |||
var mediaDataBox = new MediaDataBox(); | |||
mediaDataBox.Data = nalus; | |||
mediaDataBox.ToBuffer(ref writer); | |||
var data = writer.FlushAndGetArray(); | |||
return data; | |||
} | |||
finally | |||
{ | |||
FMp4ArrayPool.Return(buffer); | |||
} | |||
} | |||
/// <summary> | |||
/// 编码首个视频盒子 | |||
/// </summary> | |||
@@ -1348,18 +1348,36 @@ | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderMoovBox(JT1078.Protocol.H264.H264NALU@,JT1078.Protocol.H264.H264NALU@)"> | |||
<summary> | |||
编码moov盒子 | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderMoofBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU},System.Int32,System.UInt64,System.UInt32,System.UInt32,System.UInt32)"> | |||
<summary> | |||
编码Moof盒子 | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderMoofBox(System.Collections.Generic.List{System.Int32},System.UInt64,System.UInt32,System.UInt32,System.UInt32)"> | |||
<summary> | |||
编码Moof盒子 | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderMdatBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU},System.Int32)"> | |||
<summary> | |||
编码Mdat盒子 | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderMdatBox(System.Collections.Generic.List{System.Byte[]})"> | |||
<summary> | |||
编码Mdat盒子 | |||
</summary> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderFirstVideoBox(JT1078.Protocol.JT1078Package)"> | |||
<summary> | |||
编码首个视频盒子 | |||
@@ -13,12 +13,14 @@ namespace JT1078.FMp4.MessagePack | |||
private int TrunDataOffsetPosition; | |||
private int MfraSizePosition; | |||
private int MoofOffsetPosition; | |||
public FMp4MessagePackWriter(Span<byte> buffer) | |||
{ | |||
this.writer = new FMp4BufferWriter(buffer); | |||
TrunDataOffsetPosition = 0; | |||
MfraSizePosition = 0; | |||
MoofOffsetPosition = 0; | |||
} | |||
public byte[] FlushAndGetArray() | |||
{ | |||
@@ -109,6 +111,10 @@ namespace JT1078.FMp4.MessagePack | |||
{ | |||
BinaryPrimitives.WriteUInt32BigEndian(writer.Written.Slice(position, 4), value); | |||
} | |||
public void WriteUInt64Return(ulong value, int position) | |||
{ | |||
BinaryPrimitives.WriteUInt64BigEndian(writer.Written.Slice(position, 8), value); | |||
} | |||
public void WriteByteReturn(byte value, int position) | |||
{ | |||
writer.Written[position] = value; | |||
@@ -139,7 +145,7 @@ namespace JT1078.FMp4.MessagePack | |||
MfraSizePosition = writer.WrittenCount; | |||
} | |||
public int GetMfraSizePositionn() | |||
public int GetMfraSizePosition() | |||
{ | |||
return MfraSizePosition; | |||
} | |||
@@ -149,6 +155,22 @@ namespace JT1078.FMp4.MessagePack | |||
MfraSizePosition = 0; | |||
} | |||
public void CreateMoofOffsetPosition() | |||
{ | |||
MoofOffsetPosition = writer.WrittenCount; | |||
} | |||
public int GetMoofOffsetPosition() | |||
{ | |||
return MoofOffsetPosition; | |||
} | |||
public void ClearMoofOffsetPosition() | |||
{ | |||
MoofOffsetPosition = 0; | |||
} | |||
/// <summary> | |||
/// ref | |||
/// </summary> | |||
@@ -336,13 +336,13 @@ namespace JT1078.Protocol.Test | |||
[Fact] | |||
public void MergeTest() | |||
{ | |||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078.txt")); | |||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "h264","JT1078_1.txt")); | |||
JT1078Package merge=null; | |||
int mergeBodyLength=0; | |||
foreach (var line in lines) | |||
{ | |||
var data = line.Split(','); | |||
var bytes = data[5].ToHexBytes(); | |||
var bytes = data[6].ToHexBytes(); | |||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||
mergeBodyLength += package.DataBodyLength; | |||
merge = JT1078Serializer.Merge(package); | |||
@@ -18,7 +18,7 @@ namespace JT1078.Protocol.H264 | |||
public List<H264NALU> ParseNALU(JT1078Package package, string key = null) | |||
{ | |||
List<H264NALU> h264NALUs = new List<H264NALU>(); | |||
int i=0,state=0; | |||
int i=0,state=0,laststate=0; | |||
int? lastIndex=null; | |||
int length = package.Bodies.Length; | |||
byte value; | |||
@@ -45,9 +45,10 @@ namespace JT1078.Protocol.H264 | |||
if (lastIndex.HasValue) | |||
{ | |||
var tmp = buffer.Slice(lastIndex.Value, i - state - 1 - lastIndex.Value); | |||
h264NALUs.Add(Create(package, tmp, state)); | |||
h264NALUs.Add(Create(package, tmp, state+1)); | |||
} | |||
lastIndex = i; | |||
laststate = state+1; | |||
state = 0; | |||
} | |||
else | |||
@@ -61,11 +62,69 @@ namespace JT1078.Protocol.H264 | |||
} | |||
if (lastIndex.HasValue) | |||
{ | |||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), 4)); | |||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), laststate)); | |||
} | |||
return h264NALUs; | |||
} | |||
/// <summary> | |||
/// | |||
/// <see cref="https://github.com/samirkumardas/jmuxer/blob/master/src/parsers/h264.js"/> | |||
/// </summary> | |||
/// <param name="package"></param> | |||
/// <param name="h264NALUs"></param> | |||
/// <param name="key"></param> | |||
/// <returns></returns> | |||
public void ParseNALU(JT1078Package package, List<H264NALU> h264NALUs,string key = null) | |||
{ | |||
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); | |||
h264NALUs.Add(Create(package, tmp, lastIndex.Value)); | |||
} | |||
lastIndex = i; | |||
laststate = state + 1; | |||
state = 0; | |||
} | |||
else | |||
{ | |||
state = 0; | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
if (lastIndex.HasValue) | |||
{ | |||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), laststate)); | |||
} | |||
} | |||
private H264NALU Create(JT1078Package package,ReadOnlySpan<byte> nalu, int startCodePrefix) | |||
{ | |||
H264NALU nALU = new H264NALU(); | |||
@@ -75,6 +134,7 @@ namespace JT1078.Protocol.H264 | |||
nALU.LastFrameInterval = package.LastFrameInterval; | |||
nALU.LastIFrameInterval = package.LastIFrameInterval; | |||
nALU.Timestamp = package.Timestamp; | |||
nALU.Slice = (nalu[1] & 0x80)== 0x80; | |||
nALU.RawData = nalu.ToArray(); | |||
if (startCodePrefix == 3) | |||
{ | |||
@@ -40,6 +40,11 @@ namespace JT1078.Protocol.H264 | |||
/// 当数据类型为01000时,则没有该字段 | |||
/// </summary> | |||
public ulong Timestamp { get; set; } | |||
/// <summary> | |||
/// 是否切片 0x80 | |||
/// </summary> | |||
public bool Slice { get; set; } | |||
/// <summary> | |||
/// 数据体 | |||
/// </summary> | |||
@@ -122,6 +122,16 @@ | |||
<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> | |||
<see cref="!:https://github.com/samirkumardas/jmuxer/blob/master/src/parsers/h264.js"/> | |||
</summary> | |||
<param name="package"></param> | |||
<param name="h264NALUs"></param> | |||
<param name="key"></param> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.Protocol.H264.H264Decoder.DiscardEmulationPreventionBytes(System.ReadOnlySpan{System.Byte})"> | |||
<summary> | |||
Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload" | |||
@@ -166,6 +176,11 @@ | |||
当数据类型为01000时,则没有该字段 | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.Protocol.H264.H264NALU.Slice"> | |||
<summary> | |||
是否切片 0x80 | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.Protocol.H264.H264NALU.RawData"> | |||
<summary> | |||
数据体 | |||