using JT1078.Flv.Enums; using JT1078.Flv.MessagePack; using JT1078.Flv.Metadata; using JT1078.Protocol.Enums; using JT1078.Protocol.H264; using JT1078.Protocol.MessagePack; using JT1078.Flv.Audio; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using JT1078.Protocol; [assembly: InternalsVisibleTo("JT1078.Flv.Test")] namespace JT1078.Flv { /// /// Flv编码器 /// 一个客户端对应一个实例 /// /// 当实例不适用时,尽量手动调用下 /// /// /// 手动编码 /// 0、 /// 1、插入 PriviousTagSize =0 always equal 0 /// 2、 /// 3、 /// 4、 /// 5、第二个参数传false /// 6、第二个参数传false /// 自动编码 /// 1、 /// 2、 /// 3、第二个参数传true /// 4、第二个参数传true /// public class FlvEncoder : IDisposable { readonly FaacEncoder faacEncoder; readonly H264Decoder h264Decoder = new H264Decoder(); public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false) { faacEncoder = new FaacEncoder(sampleRate, channels, sampleBit, adts); } /// /// 编码flv头 /// /// 是否有视频 /// 是否有音频 /// public byte[] EncoderFlvHeader(bool hasVideo = true, bool hasAudio = false) { var flvHeader = new FlvHeader(hasVideo, hasAudio); return flvHeader.ToArray().ToArray(); } /// /// 编码脚本Tag /// /// 解析后的sps信息 /// 是否有音频 /// 帧率 默认25d 即每秒25帧 /// 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 { 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); } } /// /// 编码首帧视频,即videoTag[0] /// /// sps 解析后的数据 /// /// /// /// public byte[] EncoderFirstVideoTag(SPSInfo spsInfo, H264NALU sps, H264NALU pps, H264NALU sei) { byte[] buffer = FlvArrayPool.Rent(2048); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //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.WriteFlvTag(flvTags); flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); return flvMessagePackWriter.FlushAndGetArray(); } finally { FlvArrayPool.Return(buffer); } } /// /// 编码首帧音频,即audioTag[0] /// /// /// 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.WriteFlvTag(flvTags); flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); return flvMessagePackWriter.FlushAndGetArray(); } finally { FlvArrayPool.Return(buffer); } } /// /// 编码视频 /// /// 1078包 /// 是否需要首帧视频 /// public byte[] EncoderVideoTag(JT1078Package package, bool needVideoHeader = false) { if (package.Label3.DataType == JT1078DataType.音频帧) return default; byte[] buffer = FlvArrayPool.Rent(65535); FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); var nalus = h264Decoder.ParseNALU(package); if (nalus != null && nalus.Count > 0) { var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 6); var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 7); var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 8); nalus.Remove(sps); nalus.Remove(pps); nalus.Remove(sei); if (needVideoHeader) { //flv header var flvHeader = EncoderFlvHeader(true, false); flvMessagePackWriter.WriteArray(flvHeader); // always 0 flvMessagePackWriter.WriteUInt32(0); //解析sps if (sps == null) { return null; } var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData); ExpGolombReader h264GolombReader = new ExpGolombReader(rawData); SPSInfo spsInfo = h264GolombReader.ReadSPS(); //script tag var scriptTag = EncoderScriptTag(spsInfo); flvMessagePackWriter.WriteArray(scriptTag); // first video tag var firstVideoTag = EncoderFirstVideoTag(spsInfo, sps, pps, sei); flvMessagePackWriter.WriteArray(firstVideoTag); } foreach (var naln in nalus) { var otherVideoTag = EncoderOtherVideoTag(naln); flvMessagePackWriter.WriteArray(otherVideoTag); } } return flvMessagePackWriter.FlushAndGetArray(); } /// /// 编码非首帧音频 /// /// /// 是否需要首帧音频 /// public byte[] EncoderAudioTag(JT1078Package package, bool needAacHeader = false) { if (package.Label3.DataType != JT1078DataType.音频帧) throw new Exception("Incorrect parameter, package must be audio frame"); FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(new byte[65536]); if (needAacHeader) { flvMessagePackWriter.WriteArray(EncoderFirstAudioTag(package.Timestamp)); } byte[] aacFrameData = null; switch (package.Label2.PT) { case Jt1078AudioType.ADPCM: ReadOnlySpan adpcm = package.Bodies; // 海思芯片编码的音频需要移除海思头,可能还有其他的海思头 if (adpcm.StartsWith(new byte[] { 0x00, 0x01, 0x52, 0x00 })) adpcm = adpcm.Slice(4); aacFrameData = faacEncoder.Encode(new AdpcmCodec().ToPcm(adpcm.Slice(4).ToArray(), new State() { Valprev = (short)((adpcm[1] << 8) | adpcm[0]), Index = adpcm[2], Reserved = adpcm[3] })); break; case Jt1078AudioType.G711A: aacFrameData = faacEncoder.Encode(new G711ACodec().ToPcm(package.Bodies)); break; case Jt1078AudioType.AACLC: aacFrameData = package.Bodies; break; } if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据 { // Data Tag Frame flvMessagePackWriter.WriteArray(EncoderAacAudioTag((uint)package.Timestamp, aacFrameData)); } return flvMessagePackWriter.FlushAndGetArray(); } /// /// 编码非首帧视频 /// /// /// public byte[] EncoderOtherVideoTag(H264NALU nALU) { byte[] buffer = FlvArrayPool.Rent(65535); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //flv body video tag //flv body tag header FlvTags flvTags = new FlvTags { Type = TagType.Video, //pts Timestamp = (uint)nALU.Timestamp, TimestampExt = 0, StreamId = 0, //flv body tag body VideoTagsData = new VideoTags() }; flvTags.VideoTagsData.VideoData = new AvcVideoPacke { AvcPacketType = AvcPacketType.Raw }; //1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧; //2: inter frame(for AVC, a non - seekable frame) —— H.264的普通I帧; //ref:https://www.cnblogs.com/chyingp/p/flv-getting-started.html if (nALU.NALUHeader.NalUnitType == 5) { flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; } else { flvTags.VideoTagsData.FrameType = FrameType.InterFrame; } flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastFrameInterval; flvTags.VideoTagsData.VideoData.MultiData = new List(); flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData); //忽略sei //if (sei != null && sei.RawData != null && sei.RawData.Length > 0) //{ // flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData); //} flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); return flvMessagePackWriter.FlushAndGetArray(); } finally { FlvArrayPool.Return(buffer); } } byte[] EncoderAacAudioTag(uint timestamp, byte[] aacFrameData) { byte[] buffer = FlvArrayPool.Rent(65535); try { FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); //flv body audio tag //flv body tag header FlvTags flvTags = new FlvTags { Type = TagType.Audio, Timestamp = timestamp, TimestampExt = 0, StreamId = 0, //flv body tag body AudioTagsData = new AudioTags(AACPacketType.AudioFrame, aacFrameData) }; flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); return flvMessagePackWriter.FlushAndGetArray(); } finally { FlvArrayPool.Return(buffer); } } public void Dispose() { faacEncoder.Dispose(); } } }