diff --git a/doc/ffmpeginfo.txt b/doc/ffmpeginfo.txt index d4abafe..9b52c27 100644 --- a/doc/ffmpeginfo.txt +++ b/doc/ffmpeginfo.txt @@ -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/ \ No newline at end of file diff --git a/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs b/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs index b71a4bb..58d9958 100644 --- a/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs +++ b/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs @@ -456,6 +456,7 @@ namespace JT1078.FMp4.Test NalUnitType.PPS, NalUnitType.AUD}; int i = 0; + List h264NALUs = new List(); 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 filter = new List() { + 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 nalus = new List(); + foreach (var package in packages) + { + List 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 sizes = new List(); + 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() { diff --git a/src/JT1078.FMp4/Boxs/MovieFragmentBox.cs b/src/JT1078.FMp4/Boxs/MovieFragmentBox.cs index d5837ae..92efd5a 100644 --- a/src/JT1078.FMp4/Boxs/MovieFragmentBox.cs +++ b/src/JT1078.FMp4/Boxs/MovieFragmentBox.cs @@ -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) { diff --git a/src/JT1078.FMp4/Boxs/MovieFragmentRandomAccessBox.cs b/src/JT1078.FMp4/Boxs/MovieFragmentRandomAccessBox.cs index 1ad7359..0b86a2d 100644 --- a/src/JT1078.FMp4/Boxs/MovieFragmentRandomAccessBox.cs +++ b/src/JT1078.FMp4/Boxs/MovieFragmentRandomAccessBox.cs @@ -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); diff --git a/src/JT1078.FMp4/Boxs/TrackFragmentHeaderBox.cs b/src/JT1078.FMp4/Boxs/TrackFragmentHeaderBox.cs index ebb721d..55e7e62 100644 --- a/src/JT1078.FMp4/Boxs/TrackFragmentHeaderBox.cs +++ b/src/JT1078.FMp4/Boxs/TrackFragmentHeaderBox.cs @@ -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) { diff --git a/src/JT1078.FMp4/FMp4Encoder.cs b/src/JT1078.FMp4/FMp4Encoder.cs index 23c3dbe..fe573f4 100644 --- a/src/JT1078.FMp4/FMp4Encoder.cs +++ b/src/JT1078.FMp4/FMp4Encoder.cs @@ -108,7 +108,9 @@ namespace JT1078.FMp4 } } - uint IframeIntervalCache = 259960; + ulong IframeIntervalCache = 259960; + + ulong cts = 0; /// /// 编码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() { new SegmentIndexBox.SegmentIndex @@ -228,6 +232,88 @@ namespace JT1078.FMp4 } } + /// + /// 编码moov盒子 + /// + /// + 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(); + 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(); + 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() { pps.RawData }; + avc1.AVCConfigurationBox.SPSs = new List() { 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 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); + } + } + /// /// 编码Moof盒子 /// @@ -283,6 +369,67 @@ namespace JT1078.FMp4 } } + /// + /// 编码Moof盒子 + /// + /// + public byte[] EncoderMoofBox(List 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(); + 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); + } + } + /// /// 编码Mdat盒子 /// @@ -305,6 +452,28 @@ namespace JT1078.FMp4 } } + /// + /// 编码Mdat盒子 + /// + /// + public byte[] EncoderMdatBox(List 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); + } + } + /// /// 编码首个视频盒子 /// diff --git a/src/JT1078.FMp4/JT1078.FMp4.xml b/src/JT1078.FMp4/JT1078.FMp4.xml index 3844fe2..9513cbf 100644 --- a/src/JT1078.FMp4/JT1078.FMp4.xml +++ b/src/JT1078.FMp4/JT1078.FMp4.xml @@ -1348,18 +1348,36 @@ + + + 编码moov盒子 + + + 编码Moof盒子 + + + 编码Moof盒子 + + + 编码Mdat盒子 + + + 编码Mdat盒子 + + + 编码首个视频盒子 diff --git a/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs b/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs index 573159a..a3cf093 100644 --- a/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs +++ b/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs @@ -13,12 +13,14 @@ namespace JT1078.FMp4.MessagePack private int TrunDataOffsetPosition; private int MfraSizePosition; + private int MoofOffsetPosition; public FMp4MessagePackWriter(Span 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; + } + + /// /// ref /// diff --git a/src/JT1078.Protocol.Test/JT1078SerializerTest.cs b/src/JT1078.Protocol.Test/JT1078SerializerTest.cs index 82e8e82..eda06ee 100644 --- a/src/JT1078.Protocol.Test/JT1078SerializerTest.cs +++ b/src/JT1078.Protocol.Test/JT1078SerializerTest.cs @@ -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); diff --git a/src/JT1078.Protocol/H264/H264Decoder.cs b/src/JT1078.Protocol/H264/H264Decoder.cs index 32ce968..2b192e9 100644 --- a/src/JT1078.Protocol/H264/H264Decoder.cs +++ b/src/JT1078.Protocol/H264/H264Decoder.cs @@ -18,7 +18,7 @@ namespace JT1078.Protocol.H264 public List ParseNALU(JT1078Package package, string key = null) { List h264NALUs = new List(); - 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; } + /// + /// + /// + /// + /// + /// + /// + /// + public void ParseNALU(JT1078Package package, List h264NALUs,string key = null) + { + int i = 0, state = 0, laststate = 0; + int? lastIndex = null; + int length = package.Bodies.Length; + byte value; + ReadOnlySpan 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 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) { diff --git a/src/JT1078.Protocol/H264/H264NALU.cs b/src/JT1078.Protocol/H264/H264NALU.cs index f85ee7f..4594aaf 100644 --- a/src/JT1078.Protocol/H264/H264NALU.cs +++ b/src/JT1078.Protocol/H264/H264NALU.cs @@ -40,6 +40,11 @@ namespace JT1078.Protocol.H264 /// 当数据类型为01000时,则没有该字段 /// public ulong Timestamp { get; set; } + + /// + /// 是否切片 0x80 + /// + public bool Slice { get; set; } /// /// 数据体 /// diff --git a/src/JT1078.Protocol/JT1078.Protocol.xml b/src/JT1078.Protocol/JT1078.Protocol.xml index b73d231..f7137b0 100644 --- a/src/JT1078.Protocol/JT1078.Protocol.xml +++ b/src/JT1078.Protocol/JT1078.Protocol.xml @@ -122,6 +122,16 @@ + + + + + + + + + + Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload" @@ -166,6 +176,11 @@ 当数据类型为01000时,则没有该字段 + + + 是否切片 0x80 + + 数据体