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();
}
}
}