diff --git a/src/JT1078.Flv.Test/FlvEncoderTest.cs b/src/JT1078.Flv.Test/FlvEncoderTest.cs index eb04845..c15da3e 100644 --- a/src/JT1078.Flv.Test/FlvEncoderTest.cs +++ b/src/JT1078.Flv.Test/FlvEncoderTest.cs @@ -34,12 +34,15 @@ namespace JT1078.Flv.Test FlvEncoder encoder = new FlvEncoder(); var contents = encoder.CreateFlvFrame(nalus); + byte[] tmp = new byte[FlvEncoder.VideoFlvHeaderBuffer.Length+ contents.Length]; + Array.Copy(FlvEncoder.VideoFlvHeaderBuffer, tmp, FlvEncoder.VideoFlvHeaderBuffer.Length); + Array.Copy(contents,0,tmp, FlvEncoder.VideoFlvHeaderBuffer.Length, contents.Length); var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.flv"); if (File.Exists(filepath)) { File.Delete(filepath); } - File.WriteAllBytes(filepath, contents); + File.WriteAllBytes(filepath, tmp); } [Fact] @@ -75,7 +78,7 @@ namespace JT1078.Flv.Test File.Delete(filepath); } fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); - + fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer); var totalPage = (h264NALULs.Count + 10 - 1) / 10; for(var i=0;i< totalPage; i++) { @@ -133,6 +136,7 @@ namespace JT1078.Flv.Test } fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); + fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer); var totalPage = (h264NALULs.Count + 10 - 1) / 10; for (var i = 0; i < totalPage; i++) { @@ -189,6 +193,8 @@ namespace JT1078.Flv.Test } fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); + fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer); + var totalPage = (h264NALULs.Count + 10 - 1) / 10; for (var i = 0; i < totalPage; i++) { @@ -245,21 +251,26 @@ namespace JT1078.Flv.Test } var tmp1 = h264NALULs.Where(w => w.NALUHeader.NalUnitType == 7).ToList(); List tmpSpss = new List(); - foreach (var item in tmp1) - { - ExpGolombReader expGolombReader = new ExpGolombReader(item.RawData); - tmpSpss.Add(expGolombReader.ReadSPS()); - } - fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); - var totalPage = (h264NALULs.Count + 10 - 1) / 10; - for (var i = 0; i < totalPage; i++) + List times = new List(); + List type = new List(); + foreach (var item in h264NALULs) { - var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList()); - if (flv2.Length != 0) - { - fileStream.Write(flv2); - } + //ExpGolombReader expGolombReader = new ExpGolombReader(item.RawData); + type.Add(item.NALUHeader.NalUnitType); + times.Add(item.LastFrameInterval); + //tmpSpss.Add(expGolombReader.ReadSPS()); } + //fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); + //fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer); + //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) { diff --git a/src/JT1078.Flv/FlvEncoder.cs b/src/JT1078.Flv/FlvEncoder.cs index 5e0af70..abd2a10 100644 --- a/src/JT1078.Flv/FlvEncoder.cs +++ b/src/JT1078.Flv/FlvEncoder.cs @@ -13,74 +13,58 @@ namespace JT1078.Flv { public class FlvEncoder { - private static readonly byte[] VideoFlvHeaderBuffer; + struct FlvFrameInfo + { + public uint PreviousTagSize { get; set;} + public uint LastFrameInterval { get; set; } + } + public static readonly byte[] VideoFlvHeaderBuffer; private const uint PreviousTagSizeFixedLength = 4; - private static readonly ConcurrentDictionary PreviousTagSizeDict; - private static readonly ConcurrentDictionary FrameInitDict; private static readonly ConcurrentDictionary VideoSPSDict; private static readonly Flv.H264.H264Decoder H264Decoder; + private static readonly ConcurrentDictionary FlvFrameInfoDict; static FlvEncoder() { FlvHeader VideoFlvHeader = new FlvHeader(true, false); VideoFlvHeaderBuffer = VideoFlvHeader.ToArray().ToArray(); - PreviousTagSizeDict = new ConcurrentDictionary(); - FrameInitDict = new ConcurrentDictionary(); VideoSPSDict = new ConcurrentDictionary(); + FlvFrameInfoDict = new ConcurrentDictionary(); H264Decoder = new Flv.H264.H264Decoder(); } + /// /// /// - /// NalUnitType->7 - /// NalUnitType->8 - /// - public byte[] CreateFlvFirstFrame(H264NALU sps, H264NALU pps,bool isSendFlvHeader=true,uint previousTagSize=0) + private void CreateFlvKeyFrame(ref FlvMessagePackWriter flvMessagePackWriter, string key,byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, uint previousTagSize = 0) { - byte[] buffer = FlvArrayPool.Rent(65535); int currentMarkPosition = 0; int nextMarkPosition = 0; - try - { - FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); - if (isSendFlvHeader) - { - //flv header - flvMessagePackWriter.WriteArray(VideoFlvHeaderBuffer); - } - //SPS -> 7 - ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData); - var spsInfo = h264GolombReader.ReadSPS(); - - currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); - //flv body script tag - CreateScriptTagFrame(ref flvMessagePackWriter, spsInfo.width, spsInfo.height, previousTagSize); - nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); - - //flv body video tag - uint scriptTagFramePreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); - AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); - aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc; - aVCDecoderConfigurationRecord.ProfileCompatibility = (byte)spsInfo.profileCompat; - aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc; - aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1; - aVCDecoderConfigurationRecord.PPSBuffer = pps.RawData; - aVCDecoderConfigurationRecord.SPSBuffer = sps.RawData; + currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); + //flv body script tag + CreateScriptTagFrame(ref flvMessagePackWriter, spsInfo.width, spsInfo.height, previousTagSize); + nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); - currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); - CreateVideoTag0Frame(ref flvMessagePackWriter, scriptTagFramePreviousTagSize, aVCDecoderConfigurationRecord); - nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); + //flv body video tag + uint scriptTagFramePreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); + AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); + aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc; + aVCDecoderConfigurationRecord.ProfileCompatibility = (byte)spsInfo.profileCompat; + aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc; + aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1; + aVCDecoderConfigurationRecord.PPSBuffer = ppsRawData; + aVCDecoderConfigurationRecord.SPSBuffer = spsRawData; - //flv body video tag 0 - uint videoTag0PreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); - //cache PreviousTagSize - PreviousTagSizeDict.AddOrUpdate(sps.GetKey(), videoTag0PreviousTagSize, (a, b) => videoTag0PreviousTagSize); + currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); + CreateVideoTag0Frame(ref flvMessagePackWriter, scriptTagFramePreviousTagSize, aVCDecoderConfigurationRecord); + nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); - return flvMessagePackWriter.FlushAndGetArray(); - } - finally - { - FlvArrayPool.Return(buffer); - } + //flv body video tag 0 + uint videoTag0PreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); + //cache PreviousTagSize + FlvFrameInfoDict.AddOrUpdate(key, new FlvFrameInfo { PreviousTagSize= videoTag0PreviousTagSize }, (a, b) => { + b.PreviousTagSize = videoTag0PreviousTagSize; + return b; + }); } private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter, int width, int height,uint previousTagSize, double frameRate = 25d) { @@ -142,13 +126,10 @@ namespace JT1078.Flv flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; flvMessagePackWriter.WriteFlvTag(flvTags); } - - public static uint LastFrameInterval = 0; - - private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, H264NALU nALU) + private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, FlvFrameInfo flvFrameInfo, H264NALU nALU) { //flv body PreviousTagSize - flvMessagePackWriter.WriteUInt32(previousTagSize); + flvMessagePackWriter.WriteUInt32(flvFrameInfo.PreviousTagSize); //flv body video tag //flv body tag header FlvTags flvTags = new FlvTags(); @@ -160,10 +141,8 @@ namespace JT1078.Flv flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); flvTags.VideoTagsData.VideoData.CompositionTime = 0; flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw; - - LastFrameInterval += nALU.LastFrameInterval; - flvTags.Timestamp = LastFrameInterval; - if (nALU.NALUHeader.NalUnitType == 5) + flvTags.Timestamp = flvFrameInfo.LastFrameInterval; + if (nALU.NALUHeader.NalUnitType == 5 || nALU.DataType== JT1078DataType.视频I帧) { flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; } @@ -182,42 +161,63 @@ namespace JT1078.Flv FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); int currentMarkPosition = 0; int nextMarkPosition = 0; - 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) + H264NALU sps=null, pps=null; + SPSInfo spsInfo = new SPSInfo(); + foreach (var naln in nALUs) { - string key = sps.GetKey(); - ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData); - var spsInfo = h264GolombReader.ReadSPS(); - if(VideoSPSDict.TryGetValue(key,out var spsInfoCache)) + string key = naln.GetKey(); + if (sps != null && pps != null) { - if(spsInfoCache.height!= spsInfo.height && spsInfoCache.width!= spsInfo.width) + if (VideoSPSDict.TryGetValue(key, out var spsInfoCache)) + { + if (spsInfoCache.height != spsInfo.height && spsInfoCache.width != spsInfo.width) + { + if(FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo)) + { + CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo, flvFrameInfo.PreviousTagSize); + VideoSPSDict.TryUpdate(key, spsInfo, spsInfo); + flvFrameInfo.LastFrameInterval = 0; + FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo); + } + } + } + else { - uint previousTagSize = 0; - PreviousTagSizeDict.TryGetValue(key, out previousTagSize); - flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps,false, previousTagSize)); - VideoSPSDict.TryUpdate(key, spsInfo, spsInfo); + CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo, 0); + VideoSPSDict.TryAdd(key, spsInfo); } } - else + //7 8 6 5 1 1 1 1 7 8 6 5 1 1 1 1 1 7 8 6 5 1 1 1 1 1 + switch (naln.NALUHeader.NalUnitType) { - flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps)); - VideoSPSDict.TryAdd(key, spsInfo); + case 5:// IDR + case 1:// I/P/B + if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo)) + { + currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); + CreateVideoTagOtherFrame(ref flvMessagePackWriter, flvFrameInfo, naln); + nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); + uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); + flvFrameInfo.PreviousTagSize = tmpPreviousTagSize; + flvFrameInfo.LastFrameInterval += naln.LastFrameInterval; + FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo); + } + break; + case 7:// sps + sps = naln; + var rawData = H264Decoder.DiscardEmulationPreventionBytes(naln.RawData); + ExpGolombReader h264GolombReader = new ExpGolombReader(rawData); + spsInfo = h264GolombReader.ReadSPS(); + break; + case 8:// pps + pps = naln; + break; + case 6://SEI + break; + default: + break; } } - 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); - nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); - uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); - PreviousTagSizeDict.TryUpdate(key, tmpPreviousTagSize, previousTagSize); - } - } return flvMessagePackWriter.FlushAndGetArray(); } finally diff --git a/src/JT1078.Flv/JT1078.Flv.csproj b/src/JT1078.Flv/JT1078.Flv.csproj index 21f6a39..ea3d0e2 100644 --- a/src/JT1078.Flv/JT1078.Flv.csproj +++ b/src/JT1078.Flv/JT1078.Flv.csproj @@ -3,6 +3,21 @@ netstandard2.0;netstandard2.1; 8.0 + Copyright 2019. + SmallChi(Koike) + JT1078.Flv + JT1078.Flv + 基于JT1078的Flv视频包 + 基于JT1078的Flv视频包 + https://github.com/SmallChi/JT1078 + https://github.com/SmallChi/JT1078 + https://github.com/SmallChi/JT1078/blob/master/LICENSE + https://github.com/SmallChi/JT1078/blob/master/LICENSE + false + 1.0.0-preview1 + false + true + LICENSE @@ -12,5 +27,10 @@ - + + + True + + +