/// <summary> /// 编码首帧音频,即audioTag[0] /// <para> /// 注意:本方法已写入<see cref="previousTagSize"/> /// </para> /// </summary> /// <returns></returns> public byte[] EncoderFirstAudioTag(ulong timestamp) { byte[] buffer = FlvArrayPool.Rent(2048); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //flv body audio tag //flv body tag header FlvTags flvTags = new FlvTags { Type = TagType.Audio, Timestamp = (uint)timestamp, //flv body tag body AudioTagsData = new AudioTags(AACPacketType.AudioSpecificConfig) }; flvMessagePackWriter.WriteUInt32(previousTagSize); flvMessagePackWriter.WriteFlvTag(flvTags); var data = flvMessagePackWriter.FlushAndGetArray(); previousTagSize = (uint)(flvTags.DataSize + 11); return(data); } finally { FlvArrayPool.Return(buffer); } }
private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, H264NALU nALU) { //flv body PreviousTagSize 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(); if (nALU.NALUHeader.NalUnitType == 5) { flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; } else { 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); }
public void Test1() { Span <byte> 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 }
/// <summary> /// 编码脚本Tag /// </summary> /// <param name="spsInfo">解析后的sps信息</param> /// <param name="hasAudio">是否有音频</param> /// <param name="frameRate">帧率 默认25d 即每秒25帧</param> /// <returns></returns> public byte[] EncoderScriptTag(SPSInfo spsInfo, bool hasAudio = false, double frameRate = 25d) { byte[] buffer = FlvArrayPool.Rent(1024); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //flv body script tag //flv body tag header FlvTags flvTags = new FlvTags { Type = TagType.ScriptData, //flv body tag body DataTagsData = new Amf3 { Amf3Metadatas = new List <IAmf3Metadata> { new Amf3Metadata_Duration { Value = 0d }, new Amf3Metadata_VideoDataRate { Value = 0d }, new Amf3Metadata_VideoCodecId { Value = 7d }, new Amf3Metadata_FrameRate { Value = frameRate }, new Amf3Metadata_Width() { Value = spsInfo.width }, new Amf3Metadata_Height() { Value = spsInfo.height }, } } }; if (hasAudio) { flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioCodecId()); flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleRate()); flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleSize()); flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioStereo()); } flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); return(flvMessagePackWriter.FlushAndGetArray()); } finally { FlvArrayPool.Return(buffer); } }
public byte[] CreateFlvFrame(List <H264NALU> nALUs, int minimumLength = 10240) { byte[] buffer = FlvArrayPool.Rent(minimumLength); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); int currentMarkPosition = 0; int nextMarkPosition = 0; H264NALU?sps = null, pps = null; foreach (var naln in nALUs) { string key = naln.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; } } 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 { FlvArrayPool.Return(buffer); } }
/// <summary> /// 编码首帧视频,即videoTag[0] /// <para> /// 注意:本方法已写入<see cref="previousTagSize"/> /// </para> /// </summary> /// <param name="sps"></param> /// <param name="pps"></param> /// <param name="sei"></param> /// <returns></returns> public byte[] EncoderFirstVideoTag(H264NALU sps, H264NALU pps, H264NALU sei) { byte[] buffer = FlvArrayPool.Rent(2048); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData); ExpGolombReader h264GolombReader = new ExpGolombReader(rawData); SPSInfo spsInfo = h264GolombReader.ReadSPS(); //flv body video tag //flv body tag header FlvTags flvTags = new FlvTags { Type = TagType.Video, Timestamp = (uint)sps.Timestamp, TimestampExt = 0, StreamId = 0, //flv body tag body VideoTagsData = new VideoTags() }; flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; flvTags.VideoTagsData.VideoData = new AvcVideoPacke { AvcPacketType = AvcPacketType.SequenceHeader, CompositionTime = 0 }; AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord { AVCProfileIndication = spsInfo.profileIdc, ProfileCompatibility = (byte)spsInfo.profileCompat, AVCLevelIndication = spsInfo.levelIdc, NumOfPictureParameterSets = 1, PPSBuffer = pps.RawData, SPSBuffer = sps.RawData }; flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; flvMessagePackWriter.WriteUInt32(previousTagSize); flvMessagePackWriter.WriteFlvTag(flvTags); var data = flvMessagePackWriter.FlushAndGetArray(); previousTagSize = (uint)(flvTags.DataSize + 11); return(data); } finally { FlvArrayPool.Return(buffer); } }
/// <summary> /// /// </summary> /// <param name="sps">NalUnitType->7</param> /// <param name="pps">NalUnitType->8</param> /// <returns></returns> public byte[] CreateFlvFirstFrame(H264NALU sps, H264NALU pps) { byte[] buffer = FlvArrayPool.Rent(65535); int currentMarkPosition = 0; int nextMarkPosition = 0; try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //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); 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(); CreateVideoTag0Frame(ref flvMessagePackWriter, scriptTagFramePreviousTagSize, aVCDecoderConfigurationRecord); nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); //flv body video tag 0 uint videoTag0PreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); //cache PreviousTagSize PreviousTagSizeDict.AddOrUpdate(sps.GetKey(), videoTag0PreviousTagSize, (a, b) => videoTag0PreviousTagSize); return(flvMessagePackWriter.FlushAndGetArray()); } finally { FlvArrayPool.Return(buffer); } }
public void FlvMessagePackWriter_FlvBody_Test_1() { Span <byte> buffer = new byte[1024]; 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 <Flv.Metadata.IAmf3Metadata> { new Amf3Metadata_Duration { Value = 7.22100 }, new Amf3Metadata_FileSize { Value = 2005421.00000 }, new Amf3Metadata_FrameRate { Value = 29.16667 }, new Amf3Metadata_Height { Value = 960.00000 }, new Amf3Metadata_VideoCodecId { Value = 7.00000 }, new Amf3Metadata_VideoDataRate { Value = 0.00000 }, new Amf3Metadata_Width { Value = 544.00000 } } } } }; flvMessagePackWriter.WriteFlvBody(flvBody); var hex = flvMessagePackWriter.FlushAndGetArray().ToHexString(); Assert.Equal("00000000120000A00000000000000002000A6F6E4D65746144617461080000000700086475726174696F6E00401CE24DD2F1A9FC000866696C6573697A6500413E99AD0000000000096672616D657261746500403D2AAAE297396D000668656967687400408E000000000000000C766964656F636F646563696400401C000000000000000D766964656F646174617261746500000000000000000000057769647468004081000000000000", hex); }
private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter, int width, int height) { //flv body PreviousTagSize awalys 0 flvMessagePackWriter.WriteUInt32(0); //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 = new List <IAmf3Metadata>(); 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); }
private void CreateVideoTag0Frame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord) { //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); }
/// <summary> /// /// </summary> private void CreateFlvKeyFrame(ref FlvMessagePackWriter flvMessagePackWriter, string key, byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, uint previousTagSize = 0) { int currentMarkPosition = 0; int nextMarkPosition = 0; 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 = ppsRawData; aVCDecoderConfigurationRecord.SPSBuffer = spsRawData; currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); CreateVideoTag0Frame(ref flvMessagePackWriter, scriptTagFramePreviousTagSize, aVCDecoderConfigurationRecord); nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); //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); }); }
public void FlvMessagePackWriter_Amf3_Test_1() { Span <byte> buffer = new byte[1024]; FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); flvMessagePackWriter.WriteAmf3(new Flv.Metadata.Amf3 { Amf3Metadatas = new List <Flv.Metadata.IAmf3Metadata> { new Amf3Metadata_Duration { Value = 7.22100 }, new Amf3Metadata_FileSize { Value = 2005421.00000 }, new Amf3Metadata_FrameRate { Value = 29.16667 }, new Amf3Metadata_Height { Value = 960.00000 }, new Amf3Metadata_VideoCodecId { Value = 7.00000 }, new Amf3Metadata_VideoDataRate { Value = 0.00000 }, new Amf3Metadata_Width { Value = 544.00000 } } }); buffer = buffer.Slice(5);//amf3 5个字节 Amf3Metadata_Duration amf3Metadata = new Amf3Metadata_Duration(); amf3Metadata.Value = 7.22100; var hex = amf3Metadata.ToBuffer().ToArray().ToHexString(); Assert.Equal("00086475726174696F6E00401CE24DD2F1A9FC", buffer.Slice(0, hex.Length / 2).ToArray().ToHexString()); buffer = buffer.Slice(hex.Length / 2); Amf3Metadata_FileSize amf3Metadata_FileSize = new Amf3Metadata_FileSize(); amf3Metadata_FileSize.Value = 2005421.00000; var hex1 = amf3Metadata_FileSize.ToBuffer().ToArray().ToHexString(); Assert.Equal("000866696C6573697A6500413E99AD00000000", buffer.Slice(0, hex1.Length / 2).ToArray().ToHexString()); buffer = buffer.Slice(hex1.Length / 2); Amf3Metadata_FrameRate amf3Metadata_FrameRate = new Amf3Metadata_FrameRate(); amf3Metadata_FrameRate.Value = 29.16667; var hex2 = amf3Metadata_FrameRate.ToBuffer().ToArray().ToHexString(); Assert.Equal("00096672616D657261746500403D2AAAE297396D", buffer.Slice(0, hex2.Length / 2).ToArray().ToHexString()); buffer = buffer.Slice(hex2.Length / 2); Amf3Metadata_Height amf3Metadata_Height = new Amf3Metadata_Height(); amf3Metadata_Height.Value = 960.00000; var hex3 = amf3Metadata_Height.ToBuffer().ToArray().ToHexString(); Assert.Equal("000668656967687400408E000000000000", buffer.Slice(0, hex3.Length / 2).ToArray().ToHexString()); buffer = buffer.Slice(hex3.Length / 2); Amf3Metadata_VideoCodecId amf3Metadata_VideoCodecId = new Amf3Metadata_VideoCodecId(); amf3Metadata_VideoCodecId.Value = 7.00000; var hex4 = amf3Metadata_VideoCodecId.ToBuffer().ToArray().ToHexString(); Assert.Equal("000C766964656F636F646563696400401C000000000000", buffer.Slice(0, hex4.Length / 2).ToArray().ToHexString()); buffer = buffer.Slice(hex4.Length / 2); Amf3Metadata_VideoDataRate amf3Metadata_VideoDataRate = new Amf3Metadata_VideoDataRate(); amf3Metadata_VideoDataRate.Value = 0.00000; var hex5 = amf3Metadata_VideoDataRate.ToBuffer().ToArray().ToHexString(); Assert.Equal("000D766964656F6461746172617465000000000000000000", buffer.Slice(0, hex5.Length / 2).ToArray().ToHexString()); buffer = buffer.Slice(hex5.Length / 2); Amf3Metadata_Width amf3Metadata_Width = new Amf3Metadata_Width(); amf3Metadata_Width.Value = 544.00000; var hex6 = amf3Metadata_Width.ToBuffer().ToArray().ToHexString(); Assert.Equal("00057769647468004081000000000000", buffer.Slice(0, hex6.Length / 2).ToArray().ToHexString()); }
public byte[] CreateFlvFrame(List <H264NALU> nALUs, int minimumLength = 65535) { byte[] buffer = FlvArrayPool.Rent(minimumLength); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); int currentMarkPosition = 0; int nextMarkPosition = 0; H264NALU sps = null, pps = null; SPSInfo spsInfo = new SPSInfo(); foreach (var naln in nALUs) { string key = naln.GetKey(); if (sps != null && pps != null) { 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); FlvFirstFrameCache.TryRemove(key, out _); FlvFirstFrameCache.TryAdd(key, flvMessagePackWriter.FlushAndGetArray()); VideoSPSDict.TryUpdate(key, spsInfo, spsInfo); flvFrameInfo.LastFrameInterval = 0; FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo); } } sps = null; pps = null; } else { CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo, 0); FlvFirstFrameCache.TryAdd(key, flvMessagePackWriter.FlushAndGetArray()); VideoSPSDict.TryAdd(key, spsInfo); sps = null; pps = null; } } //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) { 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; } } return(flvMessagePackWriter.FlushAndGetArray()); } finally { FlvArrayPool.Return(buffer); } }