diff --git a/.gitignore b/.gitignore index 55b5868..c1cd75f 100644 --- a/.gitignore +++ b/.gitignore @@ -329,3 +329,4 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ /src/JT1078.Flv.Test/H264/JT1078_3.txt +/src/JT1078.Flv.Test/H264/JT1078_4.txt diff --git a/doc/ffmpeginfo.txt b/doc/ffmpeginfo.txt index 87d3851..5b1e5cd 100644 --- a/doc/ffmpeginfo.txt +++ b/doc/ffmpeginfo.txt @@ -1,3 +1,5 @@ ffmpeg -i demo.mp4 -c copy -f flv -vcodec h264 -acodec aac demo_flv.flv -./ffmpeg -i JT1078_3.h264 -r 25 -c copy -f flv "D:\JT1078_3.flv" \ No newline at end of file +./ffmpeg -i JT1078_3.h264 -r 25 -c copy -f flv "D:\JT1078_3.flv" + +ffmpeg -f dshow -i video="USB2.0 PC CAMERA" -t 60 -c copy -f h264 -vcodec h264 jt1078.264 \ No newline at end of file diff --git a/src/JT1078.Flv.Test/FlvEncoderTest.cs b/src/JT1078.Flv.Test/FlvEncoderTest.cs index 56fb907..d9ae7e1 100644 --- a/src/JT1078.Flv.Test/FlvEncoderTest.cs +++ b/src/JT1078.Flv.Test/FlvEncoderTest.cs @@ -112,9 +112,9 @@ namespace JT1078.Flv.Test { File.Delete(filepath); } - + JT1078Package Package = null; - + foreach (var line in lines) { var data = line.Split(','); @@ -124,7 +124,7 @@ namespace JT1078.Flv.Test if (Package != null) { var tmp = decoder.ParseNALU(Package); - if(tmp!=null && tmp.Count > 0) + if (tmp != null && tmp.Count > 0) { h264NALULs = h264NALULs.Concat(tmp).ToList(); } @@ -144,7 +144,63 @@ namespace JT1078.Flv.Test } catch (Exception ex) { + Assert.Throws(()=> { }); + } + finally + { + fileStream?.Close(); + fileStream?.Dispose(); + } + } + + [Fact] + public void FlvEncoder_Test_4() + { + FileStream fileStream = null; + Flv.H264.H264Decoder decoder = new Flv.H264.H264Decoder(); + List h264NALULs = new List(); + FlvEncoder encoder = new FlvEncoder(); + try + { + var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_4.flv"); + var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_4.txt")); + if (File.Exists(filepath)) + { + File.Delete(filepath); + } + + JT1078Package Package = null; + + foreach (var line in lines) + { + var data = line.Split(','); + var bytes = data[6].ToHexBytes(); + JT1078Package package = JT1078Serializer.Deserialize(bytes); + Package = JT1078Serializer.Merge(package); + if (Package != null) + { + var tmp = decoder.ParseNALU(Package); + if (tmp != null && tmp.Count > 0) + { + h264NALULs = h264NALULs.Concat(tmp).ToList(); + } + } + } + fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); + var totalPage = (h264NALULs.Count + 10 - 1) / 10; + for (var i = 0; i < totalPage; i++) + { + var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList()); + if (flv2.Length != 0) + { + fileStream.Write(flv2); + } + } + } + catch (Exception ex) + { + Assert.Throws(() => { }); } finally { diff --git a/src/JT1078.Flv.Test/H264/JT1078_4.rar b/src/JT1078.Flv.Test/H264/JT1078_4.rar new file mode 100644 index 0000000..6c078df Binary files /dev/null and b/src/JT1078.Flv.Test/H264/JT1078_4.rar differ diff --git a/src/JT1078.Flv.Test/H264/index.html b/src/JT1078.Flv.Test/H264/index.html index 1c75197..62bfaf2 100644 --- a/src/JT1078.Flv.Test/H264/index.html +++ b/src/JT1078.Flv.Test/H264/index.html @@ -15,7 +15,7 @@ var player = document.getElementById('player'); var flvPlayer = flvjs.createPlayer({ type: 'flv', - url: "jt1078_1.flv" + url: "jt1078_3.flv" }); flvPlayer.attachMediaElement(player); flvPlayer.load(); @@ -24,7 +24,7 @@ var player2 = document.getElementById('player2'); var flvPlayer2 = flvjs.createPlayer({ type: 'flv', - url: "jt1078_3.flv" + url: "jt1078_4.flv" }); flvPlayer2.attachMediaElement(player2); flvPlayer2.load(); diff --git a/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj b/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj index c3b162f..d7c94af 100644 --- a/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj +++ b/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj @@ -6,6 +6,7 @@ + @@ -35,6 +36,9 @@ Always + + Always + diff --git a/src/JT1078.Flv/FlvEncoder.cs b/src/JT1078.Flv/FlvEncoder.cs index eef07ba..d2e5370 100644 --- a/src/JT1078.Flv/FlvEncoder.cs +++ b/src/JT1078.Flv/FlvEncoder.cs @@ -13,7 +13,7 @@ namespace JT1078.Flv { public class FlvEncoder { - private static readonly byte[] VideoFlvHeaderBuffer; + private static readonly byte[] VideoFlvHeaderBuffer; private const uint PreviousTagSizeFixedLength = 4; private static readonly ConcurrentDictionary PreviousTagSizeDict; private static readonly ConcurrentDictionary FrameInitDict; @@ -78,7 +78,7 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } - private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter,int width,int height) + private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter, int width, int height,double frameRate = 25d) { //flv body PreviousTagSize awalys 0 flvMessagePackWriter.WriteUInt32(0); @@ -106,7 +106,7 @@ namespace JT1078.Flv }); flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_FrameRate { - Value = 25d + Value = frameRate }); flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Width { @@ -118,7 +118,7 @@ namespace JT1078.Flv }); flvMessagePackWriter.WriteFlvTag(flvTags); } - private void CreateVideoTag0Frame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize,AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord) + private void CreateVideoTag0Frame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord) { //flv body PreviousTagSize ScriptTag flvMessagePackWriter.WriteUInt32(previousTagSize); @@ -138,7 +138,10 @@ namespace JT1078.Flv flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; flvMessagePackWriter.WriteFlvTag(flvTags); } - private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, H264NALU nALU) + + public static uint LastFrameInterval = 0; + + private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, H264NALU nALU, H264NALU sps, H264NALU pps,H264NALU sei) { //flv body PreviousTagSize flvMessagePackWriter.WriteUInt32(previousTagSize); @@ -146,26 +149,48 @@ namespace JT1078.Flv //flv body tag header FlvTags flvTags = new FlvTags(); flvTags.Type = TagType.Video; - flvTags.Timestamp = nALU.LastIFrameInterval; flvTags.TimestampExt = 0; flvTags.StreamId = 0; //flv body tag body flvTags.VideoTagsData = new VideoTags(); + flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); + flvTags.VideoTagsData.VideoData.CompositionTime = 0; + flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw; + flvTags.VideoTagsData.VideoData.MultiData = new List(); + + LastFrameInterval += nALU.LastFrameInterval; + flvTags.Timestamp = LastFrameInterval; + if (nALU.NALUHeader.NalUnitType == 5) { flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; + if (sps!=null && pps != null) + { + //flvTags.VideoTagsData.VideoData.MultiData.Add(sps.RawData); + //flvTags.VideoTagsData.VideoData.MultiData.Add(pps.RawData); + flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData); + } + else + { + //if (sei != null) + //{ + // flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData); + //} + flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData); + } } else { flvTags.VideoTagsData.FrameType = FrameType.InterFrame; + //if (sei != null) + //{ + // flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData); + //} + flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData); } - flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); - flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw; - flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastIFrameInterval; - flvTags.VideoTagsData.VideoData.Data = nALU.RawData; flvMessagePackWriter.WriteFlvTag(flvTags); } - public byte[] CreateFlvFrame(List nALUs, int minimumLength = 10240) + public byte[] CreateFlvFrame(List nALUs, int minimumLength = 65535) { byte[] buffer = FlvArrayPool.Rent(minimumLength); try @@ -173,39 +198,29 @@ namespace JT1078.Flv FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); int currentMarkPosition = 0; int nextMarkPosition = 0; - H264NALU? sps=null, pps=null; - foreach (var naln in nALUs) + H264NALU sps = nALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 7); + H264NALU pps = nALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 8); + H264NALU sei = nALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 6); + if (sps!=null && pps != null) { - string key = naln.GetKey(); - if(!FrameInitDict.ContainsKey(key)) + string key = sps.GetKey(); + if (!FrameInitDict.ContainsKey(key)) { - if (naln.NALUHeader.NalUnitType == 7) - { - naln.RawData = H264Decoder.DiscardEmulationPreventionBytes(naln.RawData); - sps = naln; - } - else if (naln.NALUHeader.NalUnitType == 8) - { - pps = naln; - } - if (sps != null && pps != null) - { - flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps)); - FrameInitDict.TryAdd(key, true); - } - else - { - continue; - } + flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps)); + FrameInitDict.TryAdd(key, true); } + } + foreach (var naln in nALUs.Where(w=> w.NALUHeader.NalUnitType != 7 && w.NALUHeader.NalUnitType != 8 && w.NALUHeader.NalUnitType != 6)) + { + string key = naln.GetKey(); if (PreviousTagSizeDict.TryGetValue(key, out uint previousTagSize)) { currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); - CreateVideoTagOtherFrame(ref flvMessagePackWriter, previousTagSize, naln); + CreateVideoTagOtherFrame(ref flvMessagePackWriter, previousTagSize, naln,sps, pps, sei); nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); PreviousTagSizeDict.TryUpdate(key, tmpPreviousTagSize, previousTagSize); - } + } } return flvMessagePackWriter.FlushAndGetArray(); } diff --git a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs index 79382da..e289158 100644 --- a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs +++ b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs @@ -87,8 +87,22 @@ namespace JT1078.Flv.MessagePack WriteUInt24(videoPacke.CompositionTime); //One or more NALUs //WriteArray(new byte[] {0,0,0,1}); - WriteInt32(videoPacke.Data.Length); - WriteArray(videoPacke.Data); + if (videoPacke.MultiData != null) + { + foreach(var item in videoPacke.MultiData) + { + if (item != null && item.Length > 0) + { + WriteInt32(item.Length); + WriteArray(item); + } + } + } + if (videoPacke.Data != null && videoPacke.Data.Length>0) + { + WriteInt32(videoPacke.Data.Length); + WriteArray(videoPacke.Data); + } } else { diff --git a/src/JT1078.Flv/Metadata/AvcVideoPacke.cs b/src/JT1078.Flv/Metadata/AvcVideoPacke.cs index b93a14c..d88406f 100644 --- a/src/JT1078.Flv/Metadata/AvcVideoPacke.cs +++ b/src/JT1078.Flv/Metadata/AvcVideoPacke.cs @@ -14,5 +14,7 @@ namespace JT1078.Flv.Metadata public AVCDecoderConfigurationRecord AVCDecoderConfiguration { get; set; } public byte[] Data { get; set; } + + public List MultiData { get; set; } } } diff --git a/src/JT808.Protocol.Extensions.WebApiTest/Program.cs b/src/JT808.Protocol.Extensions.WebApiTest/Program.cs index 7d54844..8d4ce52 100644 --- a/src/JT808.Protocol.Extensions.WebApiTest/Program.cs +++ b/src/JT808.Protocol.Extensions.WebApiTest/Program.cs @@ -25,7 +25,7 @@ namespace JT808.Protocol.Extensions.WebApiTest { c.HttpHost = new Uri("http://localhost:12828/jt808api/"); c.FormatOptions.DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fff"; - c.LoggerFactory = p.GetRequiredService(); + //c.LoggerFactory = p.GetRequiredService(); }); IServiceProvider serviceProvider = serviceDescriptors.BuildServiceProvider();