From 246a4bdd29e579e5e06ebf3ae030e6c28acd98b1 Mon Sep 17 00:00:00 2001 From: waterliu99 Date: Fri, 9 Jul 2021 19:01:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0mp4=20=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj | 3 + src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs | 26 ++- src/JT1078.FMp4/FMp4Encoder.cs | 172 ++++++++++--------- src/JT1078.FMp4/JT1078.FMp4.xml | 10 +- 4 files changed, 125 insertions(+), 86 deletions(-) diff --git a/src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj b/src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj index cb53f71..6f0e246 100644 --- a/src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj +++ b/src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj @@ -40,6 +40,9 @@ Always + + Always + Always diff --git a/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs b/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs index a95aca2..76f4f66 100644 --- a/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs +++ b/src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs @@ -433,6 +433,28 @@ namespace JT1078.FMp4.Test fileStream.Close(); } + [Fact] + public void Test3_1() + { + FMp4Encoder fMp4Encoder = new FMp4Encoder(); + var packages = ParseNALUTests(); + var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.mp4"); + if (File.Exists(filepath)) + { + File.Delete(filepath); + } + using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); + //fragment moof n + bool flag = true; + foreach (var package in packages) + { + var buffer= fMp4Encoder.EncoderVideo(package, flag); + flag = false; + fileStream.Write(buffer); + } + fileStream.Close(); + } + [Fact] public void Test4() { @@ -471,6 +493,7 @@ namespace JT1078.FMp4.Test { if (nalus.Count > 0) { + var otherBuffer = fMp4Encoder.EncoderOtherVideoBox(nalus); fileStream.Write(otherBuffer); nalus.Clear(); @@ -537,7 +560,7 @@ namespace JT1078.FMp4.Test public JT1078Package ParseNALUTest() { JT1078Package Package = null; - var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_1.txt")); + var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_3.txt")); int mergeBodyLength = 0; foreach (var line in lines) { @@ -549,7 +572,6 @@ namespace JT1078.FMp4.Test } return Package; } - public List ParseNALUTests() { List packages = new List(); diff --git a/src/JT1078.FMp4/FMp4Encoder.cs b/src/JT1078.FMp4/FMp4Encoder.cs index 08840dc..318393f 100644 --- a/src/JT1078.FMp4/FMp4Encoder.cs +++ b/src/JT1078.FMp4/FMp4Encoder.cs @@ -39,6 +39,9 @@ namespace JT1078.FMp4 const uint TrunFlags = 0x205; const uint SampleDescriptionIndex = 1; const uint TrackID = 1; + //to-do:如何处理多个用户观看,多个设备播放 + ulong samplesize = 0; + uint SequenceNumber = 0; /// /// @@ -63,13 +66,19 @@ namespace JT1078.FMp4 fileTypeBox.MajorBrand = "isom"; fileTypeBox.MinorVersion = "\0\0\u0002\0"; fileTypeBox.CompatibleBrands.Add("isom"); + fileTypeBox.CompatibleBrands.Add("iso6"); fileTypeBox.CompatibleBrands.Add("iso2"); fileTypeBox.CompatibleBrands.Add("avc1"); fileTypeBox.CompatibleBrands.Add("mp41"); - fileTypeBox.CompatibleBrands.Add("iso5"); - fileTypeBox.CompatibleBrands.Add("iso6"); + //fileTypeBox.CompatibleBrands.Add("isom"); + //fileTypeBox.CompatibleBrands.Add("iso2"); + //fileTypeBox.CompatibleBrands.Add("avc1"); + //fileTypeBox.CompatibleBrands.Add("mp41"); + //fileTypeBox.CompatibleBrands.Add("iso5"); + //fileTypeBox.CompatibleBrands.Add("iso6"); fileTypeBox.ToBuffer(ref writer); var data = writer.FlushAndGetArray(); + samplesize += (ulong)data.Length; return data; } finally @@ -92,17 +101,19 @@ namespace JT1078.FMp4 var spsInfo = h264GolombReader.ReadSPS(); //moov MovieBox movieBox = new MovieBox(); - movieBox.MovieHeaderBox = new MovieHeaderBox(0, 2); + movieBox.MovieHeaderBox = new MovieHeaderBox(0, 0); movieBox.MovieHeaderBox.CreationTime = 0; movieBox.MovieHeaderBox.ModificationTime = 0; movieBox.MovieHeaderBox.Duration = 0; movieBox.MovieHeaderBox.Timescale = 1000; - movieBox.MovieHeaderBox.NextTrackID = 99; + movieBox.MovieHeaderBox.Rate = 0x00010000;//typically 1.0 媒体速率,这个值代表原始倍速 + movieBox.MovieHeaderBox.Volume = 0x0100;// typically,1.0 full volume 媒体音量,这个值代表满音量 + movieBox.MovieHeaderBox.NextTrackID = 2; 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 = TrackID; + movieBox.TrackBox.TrackHeaderBox.TrackID = TrackID;//1 movieBox.TrackBox.TrackHeaderBox.Duration = 0; movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false; movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width; @@ -137,21 +148,22 @@ namespace JT1078.FMp4 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.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 = TrackID; - trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex; + trex.TrackID = TrackID;//1 + trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex;//1 trex.DefaultSampleDuration = 0; trex.DefaultSampleSize = 0; trex.DefaultSampleFlags = 0; movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex); movieBox.ToBuffer(ref writer); var data = writer.FlushAndGetArray(); + samplesize += (ulong)data.Length; return data; } finally @@ -164,93 +176,87 @@ namespace JT1078.FMp4 /// 编码其他视频数据盒子 /// /// - public byte[] EncoderOtherVideoBox(in List nalus) + public byte[] EncoderOtherVideoBox(List nalus) { - byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.RawData.Length + s.StartCodePrefix.Length) + 4096); + byte[] buffer= buffer = FMp4ArrayPool.Rent(nalus.Sum(m=>m.RawData.Length + m.StartCodePrefix.Length) + 4096); FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); try { - var truns = new List(); - List rawdatas = new List(); - uint iSize = 0; - ulong lastTimestamp = 0; - string key = string.Empty; - for (var i = 0; i < nalus.Count; i++) - { - var nalu = nalus[i]; - if (string.IsNullOrEmpty(key)) - { - key = nalu.GetKey(); - } - rawdatas.Add(nalu.RawData); - if (nalu.DataType == Protocol.Enums.JT1078DataType.视频I帧) - { - iSize += (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length); - } - else - { - if (iSize > 0) - { - truns.Add(new TrackRunBox.TrackRunInfo() - { - SampleSize = iSize, - }); - iSize = 0; - } - truns.Add(new TrackRunBox.TrackRunInfo() - { - SampleSize = (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length), - }); - } - if (i == (nalus.Count - 1)) - { - lastTimestamp = nalu.Timestamp; - } - } - if (TrackInfos.TryGetValue(key, out TrackInfo trackInfo)) - { - if (trackInfo.SN == uint.MaxValue) - { - trackInfo.SN = 1; - } - trackInfo.SN++; - } - else - { - trackInfo = new TrackInfo { SN = 1, DTS = 0 }; - TrackInfos.Add(key, trackInfo); - } - var movieFragmentBox = new MovieFragmentBox(); - movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); - movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN; - movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); - movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags); - movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID; - movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex; - movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration; - movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize; - movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags; - movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); - movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.DTS; - trackInfo.DTS += (ulong)(truns.Count * DefaultSampleDuration); - TrackInfos[key] = trackInfo; + FragmentBox fragmentBox = new FragmentBox(); + fragmentBox.MovieFragmentBox = new MovieFragmentBox(); + fragmentBox.MovieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); + fragmentBox.MovieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = SequenceNumber; + fragmentBox.MovieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x39);//TfhdFlags + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID;//1 + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.BaseDataOffset = samplesize;//基于前面盒子的长度 + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex; + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration; + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = (uint)(nalus.Sum(m => m.RawData.Length + m.StartCodePrefix.Length)); + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags; + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = nalus[0].Timestamp * 1000; //trun - movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: TrunFlags); - movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FirstSampleFlags; - movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns; - movieFragmentBox.ToBuffer(ref writer); - - var mediaDataBox = new MediaDataBox(); - mediaDataBox.Data = rawdatas; - mediaDataBox.ToBuffer(ref writer); - + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x5 );//TrunFlags + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 0;// FirstSampleFlags; + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List(); + fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo()); + fragmentBox.MediaDataBox = new MediaDataBox(); + fragmentBox.MediaDataBox.Data = nalus.Select(s => s.RawData).ToList(); + fragmentBox.ToBuffer(ref writer); var data = writer.FlushAndGetArray(); + samplesize += (ulong)data.Length; + SequenceNumber++; return data; } finally { FMp4ArrayPool.Return(buffer); + } + } + /// + /// 编码mp4 视频 + /// + /// + /// + /// + public byte[] EncoderVideo(JT1078Package package, bool needVideoHeader = false) { + H264Decoder h264Decoder = new H264Decoder(); + byte[] buffer = FMp4ArrayPool.Rent(package.Bodies.Length * 2 + 4096); + FMp4MessagePackWriter flvMessagePackWriter = new FMp4MessagePackWriter(buffer); + var nalus = h264Decoder.ParseNALU(package); + if (nalus != null && nalus.Count > 0) + { + try + { + var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.SEI); + var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.SPS); + var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.PPS); + nalus.Remove(sei); + nalus.Remove(sps); + nalus.Remove(pps); + if (needVideoHeader) + { + //mp4 header + //ftype + var ftypHeader = EncoderFtypBox(); + flvMessagePackWriter.WriteArray(ftypHeader); + // moov + var moov = EncoderMoovBox(sps, pps); + flvMessagePackWriter.WriteArray(moov); + //解析sps + } + + var otherVideoTag = EncoderOtherVideoBox(nalus); + flvMessagePackWriter.WriteArray(otherVideoTag); + } + finally + { + FMp4ArrayPool.Return(buffer); + } } + var data = flvMessagePackWriter.FlushAndGetArray(); + return data; } struct TrackInfo diff --git a/src/JT1078.FMp4/JT1078.FMp4.xml b/src/JT1078.FMp4/JT1078.FMp4.xml index b8e311b..e653350 100644 --- a/src/JT1078.FMp4/JT1078.FMp4.xml +++ b/src/JT1078.FMp4/JT1078.FMp4.xml @@ -1337,12 +1337,20 @@ - + 编码其他视频数据盒子 + + + 编码mp4 视频 + + + + + unsigned int(8)