diff --git a/src/JT1078.Flv.Test/FlvEncoderTest.cs b/src/JT1078.Flv.Test/FlvEncoderTest.cs index 1315ae4..e17fa81 100644 --- a/src/JT1078.Flv.Test/FlvEncoderTest.cs +++ b/src/JT1078.Flv.Test/FlvEncoderTest.cs @@ -12,8 +12,8 @@ namespace JT1078.Flv.Test public void FlvMuxer_Test_1() { FlvEncoder encoder = new FlvEncoder(); - var buff = encoder.FlvFirstFrame(); - var hex= buff.ToHexString(); + //var buff = encoder.FlvFirstFrame(); + //var hex= buff.ToHexString(); } } } diff --git a/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj b/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj index f1c2b37..4e254bc 100644 --- a/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj +++ b/src/JT1078.Flv.Test/JT1078.Flv.Test.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0 @@ -15,10 +15,6 @@ - - - - Always diff --git a/src/JT1078.Flv.Test/MessagePack/FlvMessagePackWriter_FlvBody_Test.cs b/src/JT1078.Flv.Test/MessagePack/FlvMessagePackWriter_FlvBody_Test.cs index 2082303..a012890 100644 --- a/src/JT1078.Flv.Test/MessagePack/FlvMessagePackWriter_FlvBody_Test.cs +++ b/src/JT1078.Flv.Test/MessagePack/FlvMessagePackWriter_FlvBody_Test.cs @@ -18,14 +18,14 @@ namespace JT1078.Flv.Test.MessagePack FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); FlvBody flvBody = new FlvBody() { - PreviousTagSize=0, - Tag = new FlvTags - { - Type = Enums.TagType.ScriptData, - DataSize = 156, - DataTagsData = new Amf3 - { - Amf3Metadatas = new List { + PreviousTagSize = 0, + Tag = new FlvTags + { + Type = Enums.TagType.ScriptData, + DataSize = 156, + DataTagsData = new Amf3 + { + Amf3Metadatas = new List { new Amf3Metadata_Duration{ Value=7.22100 }, @@ -48,11 +48,11 @@ namespace JT1078.Flv.Test.MessagePack Value=544.00000 } } - } - } + } + } }; flvMessagePackWriter.WriteFlvBody(flvBody); - var hex=flvMessagePackWriter.FlushAndGetArray().ToHexString(); + var hex = flvMessagePackWriter.FlushAndGetArray().ToHexString(); Assert.Equal("00000000120000A00000000000000002000A6F6E4D65746144617461080000000700086475726174696F6E00401CE24DD2F1A9FC000866696C6573697A6500413E99AD0000000000096672616D657261746500403D2AAAE297396D000668656967687400408E000000000000000C766964656F636F646563696400401C000000000000000D766964656F646174617261746500000000000000000000057769647468004081000000000000", hex); } } diff --git a/src/JT1078.Flv.Test/Metadata/AVCDecoderConfigurationRecordTest.cs b/src/JT1078.Flv.Test/Metadata/AVCDecoderConfigurationRecordTest.cs new file mode 100644 index 0000000..2cef540 --- /dev/null +++ b/src/JT1078.Flv.Test/Metadata/AVCDecoderConfigurationRecordTest.cs @@ -0,0 +1,41 @@ +using JT1078.Flv.MessagePack; +using JT1078.Flv.Metadata; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +using JT1078.Flv.Extensions; + +namespace JT1078.Flv.Test.Metadata +{ + public class AVCDecoderConfigurationRecordTest + { + [Fact] + public void Test1() + { + Span buffer = new byte[1024]; + FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); + AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); + aVCDecoderConfigurationRecord.AVCProfileIndication = 0x64; + aVCDecoderConfigurationRecord.ProfileCompatibility = 0; + aVCDecoderConfigurationRecord.AVCLevelIndication =0x1F; + aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1; + aVCDecoderConfigurationRecord.SPSBuffer = new byte[] { + 0x67,0x64,0x00,0x1F, + 0xAC,0xD9,0x40,0x88, + 0x1E,0x68,0x40,0x00, + 0x00,0x03,0x01,0x80, + 0x00,0x00,0x57,0x83, + 0xC6,0x0C,0x65,0x80 + }; + aVCDecoderConfigurationRecord.PPSBuffer = new byte[] { + 0x68,0xEB,0xE3,0xCB,0x22,0xC0 + }; + flvMessagePackWriter.WriteAVCDecoderConfigurationRecord(aVCDecoderConfigurationRecord); + var hexData = flvMessagePackWriter.FlushAndGetArray().ToHexString(); + Assert.Equal("0164001FFFE100186764001FACD940881E6840000003018000005783C60C658001000668EBE3CB22C0", hexData); + //0164001FFFE100186764001FACD940881E6840000003018000005783C60C658001000668EBE3CB22C0 + //0164001FFFE100186764001FACD940881E6840000003018000005783C60C658001000668EBE3CB22C0 + } + } +} diff --git a/src/JT1078.Flv/FlvEncoder.cs b/src/JT1078.Flv/FlvEncoder.cs index ea2bb2b..988d154 100644 --- a/src/JT1078.Flv/FlvEncoder.cs +++ b/src/JT1078.Flv/FlvEncoder.cs @@ -1,6 +1,10 @@ -using JT1078.Flv.MessagePack; +using JT1078.Flv.Enums; +using JT1078.Flv.H264; +using JT1078.Flv.MessagePack; +using JT1078.Flv.Metadata; using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace JT1078.Flv @@ -8,7 +12,8 @@ namespace JT1078.Flv public class FlvEncoder { private static readonly FlvHeader VideoFlvHeader = new FlvHeader(true, false); - public byte[] FlvFirstFrame() + private static readonly H264Decoder h264Decoder = new H264Decoder(); + public byte[] FlvFirstFrame(List nALUs) { byte[] buffer = FlvArrayPool.Rent(10240); try @@ -16,15 +21,142 @@ namespace JT1078.Flv FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //flv header flvMessagePackWriter.WriteArray(VideoFlvHeader.ToArray()); - //flv body + //SPS -> 7 + var spsNALU = nALUs.FirstOrDefault(n => n.NALUHeader.NalUnitType == 7); + spsNALU.RawData = h264Decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); + ExpGolombReader h264GolombReader = new ExpGolombReader(spsNALU.RawData); + var spsInfo = h264GolombReader.ReadSPS(); + //PPS -> 8 + var ppsNALU = nALUs.FirstOrDefault(n => n.NALUHeader.NalUnitType == 8); + //IDR -> 5 关键帧 + var idrNALU = nALUs.FirstOrDefault(n => n.NALUHeader.NalUnitType == 5); + //SEI -> 6 + //var seiNALU = nALUs.FirstOrDefault(n => n.NALUHeader.NalUnitType == 6); + //flv body script tag + var scriptTag = CreateScriptTagFrame(spsInfo.width, spsInfo.height); + //flv body video tag + AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); + aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc; + aVCDecoderConfigurationRecord.ProfileCompatibility =(byte)spsInfo.profileCompat; + aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc; + aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1; + aVCDecoderConfigurationRecord.PPSBuffer = spsNALU.RawData; + aVCDecoderConfigurationRecord.SPSBuffer = ppsNALU.RawData; + var videoTag = CreateVideoTag0Frame((uint)scriptTag.Length, aVCDecoderConfigurationRecord); + + + return flvMessagePackWriter.FlushAndGetArray(); + } + finally + { + FlvArrayPool.Return(buffer); + } + } + + public byte[] CreateScriptTagFrame(int width,int height) + { + byte[] buffer = FlvArrayPool.Rent(1024); + try + { + FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //flv body PreviousTagSize awalys 0 flvMessagePackWriter.WriteUInt32(0); - //flv body tag - + //flv body script tag //flv body tag header + FlvTags flvTags = new FlvTags(); + flvTags.Type = TagType.ScriptData; + flvTags.Timestamp = 0; + flvTags.TimestampExt = 0; + flvTags.StreamId = 0; + //flv body tag body + flvTags.DataTagsData = new Amf3(); + flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Duration + { + Value = 0d + }); + flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoDataRate + { + Value = 0d + }); + flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoCodecId + { + Value = 7d + }); + flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_FrameRate + { + Value = 25d + }); + flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Width + { + Value = width + }); + flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Height + { + Value = height + }); + flvMessagePackWriter.WriteFlvTag(flvTags); + return flvMessagePackWriter.FlushAndGetArray(); + } + finally + { + FlvArrayPool.Return(buffer); + } + } + public byte[] CreateVideoTag0Frame(uint previousTagSize,AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord) + { + byte[] buffer = FlvArrayPool.Rent(1024); + try + { + FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); + //flv body PreviousTagSize ScriptTag + flvMessagePackWriter.WriteUInt32(previousTagSize); + //flv body video tag + //flv body tag header + FlvTags flvTags = new FlvTags(); + flvTags.Type = TagType.Video; + flvTags.Timestamp = 0; + flvTags.TimestampExt = 0; + flvTags.StreamId = 0; //flv body tag body + flvTags.VideoTagsData = new VideoTags(); + flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; + flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); + flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.SequenceHeader; + flvTags.VideoTagsData.VideoData.CompositionTime = 0; + flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; + flvMessagePackWriter.WriteFlvTag(flvTags); + return flvMessagePackWriter.FlushAndGetArray(); + } + finally + { + FlvArrayPool.Return(buffer); + } + } + public byte[] CreateVideoTagOtherFrame(uint previousTagSize, H264NALU nALU) + { + byte[] buffer = FlvArrayPool.Rent(2048); + try + { + FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); + //flv body PreviousTagSize ScriptTag + flvMessagePackWriter.WriteUInt32(previousTagSize); + //flv body video tag + //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.FrameType = FrameType.InterFrame; + 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); return flvMessagePackWriter.FlushAndGetArray(); } finally @@ -32,6 +164,7 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } + public byte[] FlvOtherFrame() { byte[] buffer = FlvArrayPool.Rent(10240); diff --git a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs index 1b93ad7..21a6232 100644 --- a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs +++ b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs @@ -80,13 +80,11 @@ namespace JT1078.Flv.MessagePack videoPacke.CompositionTime = 0; WriteUInt24(videoPacke.CompositionTime); //AVCDecoderConfigurationRecord -#warning AVCDecoderConfigurationRecord - WriteArray(videoPacke.Data); + WriteAVCDecoderConfigurationRecord(videoPacke.AVCDecoderConfiguration); } else if(videoPacke.AvcPacketType == AvcPacketType.Raw) { WriteUInt24(videoPacke.CompositionTime); -#warning One or more NALUs //One or more NALUs WriteArray(videoPacke.Data); } @@ -104,7 +102,8 @@ namespace JT1078.Flv.MessagePack WriteByte(configurationRecord.AVCProfileIndication); WriteByte(configurationRecord.ProfileCompatibility); WriteByte(configurationRecord.AVCLevelIndication); - WriteByte((byte)configurationRecord.LengthSizeMinusOne); +#warning reserved(6bits)+LengthSizeMinusOne(2bits) + WriteByte(0xFF); WriteByte((byte)configurationRecord.NumOfSequenceParameterSets); WriteUInt16((ushort)configurationRecord.SPSBuffer.Length); WriteArray(configurationRecord.SPSBuffer); diff --git a/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs b/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs index a279f92..f9c7fd5 100644 --- a/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs +++ b/src/JT1078.Flv/Metadata/AVCDecoderConfigurationRecord.cs @@ -38,9 +38,9 @@ namespace JT1078.Flv.Metadata public byte ProfileCompatibility { get; set; } public byte AVCLevelIndication { get; set; } public int LengthSizeMinusOne { get; set; } - public int NumOfSequenceParameterSets { get; set; } + public int NumOfSequenceParameterSets { get; set; }= 0xE0 | 1; public byte[] SPSBuffer { get; set; } - public byte NumOfPictureParameterSets { get; set; } = 1; + public byte NumOfPictureParameterSets { get; set; } public byte[] PPSBuffer { get; set; } #region Just for non-spec-conform encoders ref:org.mp4parser.boxes.iso14496.part15.AvcDecoderConfigurationRecord public const int LengthSizeMinusOnePaddingBits = 63; diff --git a/src/JT1078.Flv/Metadata/Amf3Metadata_VideoCodecId.cs b/src/JT1078.Flv/Metadata/Amf3Metadata_VideoCodecId.cs index 9372420..f944a36 100644 --- a/src/JT1078.Flv/Metadata/Amf3Metadata_VideoCodecId.cs +++ b/src/JT1078.Flv/Metadata/Amf3Metadata_VideoCodecId.cs @@ -11,6 +11,9 @@ namespace JT1078.Flv.Metadata public ushort FieldNameLength { get; set; } public string FieldName { get; set; } = "videocodecid"; public byte DataType { get; set; } = 0x00; + /// + /// + /// public object Value { get; set; } public ReadOnlySpan ToBuffer() diff --git a/src/JT1078.Flv/Metadata/AvcVideoPacke.cs b/src/JT1078.Flv/Metadata/AvcVideoPacke.cs index 415e521..b93a14c 100644 --- a/src/JT1078.Flv/Metadata/AvcVideoPacke.cs +++ b/src/JT1078.Flv/Metadata/AvcVideoPacke.cs @@ -11,6 +11,8 @@ namespace JT1078.Flv.Metadata public uint CompositionTime { get; set; } + public AVCDecoderConfigurationRecord AVCDecoderConfiguration { get; set; } + public byte[] Data { get; set; } } }