You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 line
16 KiB

  1. using JT1078.Flv.Enums;
  2. using JT1078.Flv.MessagePack;
  3. using JT1078.Flv.Metadata;
  4. using JT1078.Protocol.Enums;
  5. using JT1078.Protocol.H264;
  6. using JT1078.Protocol.MessagePack;
  7. using JT1078.Flv.Audio;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Runtime.CompilerServices;
  12. using JT1078.Protocol;
  13. [assembly: InternalsVisibleTo("JT1078.Flv.Test")]
  14. namespace JT1078.Flv
  15. {
  16. /// <summary>
  17. /// Flv编码器
  18. /// 一个客户端对应一个实例
  19. /// <para>
  20. /// 当实例不适用时,尽量手动调用下<see cref="Dispose"/>
  21. /// </para>
  22. ///
  23. /// 手动编码
  24. /// 0、<see cref="EncoderFlvHeader"/>
  25. /// 1、插入 PriviousTagSize =0 always equal 0
  26. /// 2、<see cref="EncoderScriptTag"/>
  27. /// 3、<see cref="EncoderFirstVideoTag"/>
  28. /// 4、<see cref="EncoderFirstAudioTag"/>
  29. /// 5、<see cref="EncoderVideoTag"/>第二个参数传false
  30. /// 6、<see cref="EncoderAudioTag"/>第二个参数传false
  31. /// 自动编码
  32. /// 1、<see cref="EncoderFlvHeader"/>
  33. /// 2、<see cref="EncoderScriptTag"/>
  34. /// 3、<see cref="EncoderVideoTag"/>第二个参数传true
  35. /// 4、<see cref="EncoderAudioTag"/>第二个参数传true
  36. /// </summary>
  37. public class FlvEncoder : IDisposable
  38. {
  39. readonly FaacEncoder faacEncoder;
  40. readonly H264Decoder h264Decoder = new H264Decoder();
  41. public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false)
  42. {
  43. faacEncoder = new FaacEncoder(sampleRate, channels, sampleBit, adts);
  44. }
  45. /// <summary>
  46. /// 编码flv头
  47. /// </summary>
  48. /// <param name="hasVideo">是否有视频</param>
  49. /// <param name="hasAudio">是否有音频</param>
  50. /// <returns></returns>
  51. public byte[] EncoderFlvHeader(bool hasVideo = true, bool hasAudio = false)
  52. {
  53. var flvHeader = new FlvHeader(hasVideo, hasAudio);
  54. return flvHeader.ToArray().ToArray();
  55. }
  56. /// <summary>
  57. /// 编码脚本Tag
  58. /// </summary>
  59. /// <param name="spsInfo">解析后的sps信息</param>
  60. /// <param name="hasAudio">是否有音频</param>
  61. /// <param name="frameRate">帧率 默认25d 即每秒25帧</param>
  62. /// <returns></returns>
  63. public byte[] EncoderScriptTag(SPSInfo spsInfo, bool hasAudio = false, double frameRate = 25d)
  64. {
  65. byte[] buffer = FlvArrayPool.Rent(1024);
  66. try
  67. {
  68. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  69. //flv body script tag
  70. //flv body tag header
  71. FlvTags flvTags = new FlvTags
  72. {
  73. Type = TagType.ScriptData,
  74. //flv body tag body
  75. DataTagsData = new Amf3
  76. {
  77. Amf3Metadatas = new List<IAmf3Metadata>
  78. {
  79. new Amf3Metadata_Duration{Value = 0d},
  80. new Amf3Metadata_VideoDataRate{Value = 0d},
  81. new Amf3Metadata_VideoCodecId{Value = 7d},
  82. new Amf3Metadata_FrameRate{Value = frameRate},
  83. new Amf3Metadata_Width(){
  84. Value=spsInfo.width
  85. },
  86. new Amf3Metadata_Height(){
  87. Value=spsInfo.height
  88. },
  89. }
  90. }
  91. };
  92. if (hasAudio)
  93. {
  94. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioCodecId());
  95. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleRate());
  96. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleSize());
  97. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioStereo());
  98. }
  99. flvMessagePackWriter.WriteFlvTag(flvTags);
  100. flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
  101. return flvMessagePackWriter.FlushAndGetArray();
  102. }
  103. finally
  104. {
  105. FlvArrayPool.Return(buffer);
  106. }
  107. }
  108. /// <summary>
  109. /// 编码首帧视频,即videoTag[0]
  110. /// </summary>
  111. /// <param name="spsInfo">sps 解析后的数据</param>
  112. /// <param name="sps"></param>
  113. /// <param name="pps"></param>
  114. /// <param name="sei"></param>
  115. /// <returns></returns>
  116. public byte[] EncoderFirstVideoTag(SPSInfo spsInfo, H264NALU sps, H264NALU pps, H264NALU sei)
  117. {
  118. byte[] buffer = FlvArrayPool.Rent(2048);
  119. try
  120. {
  121. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  122. //flv body video tag
  123. //flv body tag header
  124. FlvTags flvTags = new FlvTags
  125. {
  126. Type = TagType.Video,
  127. Timestamp = (uint)sps.Timestamp,
  128. TimestampExt = 0,
  129. StreamId = 0,
  130. //flv body tag body
  131. VideoTagsData = new VideoTags()
  132. };
  133. flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
  134. flvTags.VideoTagsData.VideoData = new AvcVideoPacke
  135. {
  136. AvcPacketType = AvcPacketType.SequenceHeader,
  137. CompositionTime = 0
  138. };
  139. AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord
  140. {
  141. AVCProfileIndication = spsInfo.profileIdc,
  142. ProfileCompatibility = (byte)spsInfo.profileCompat,
  143. AVCLevelIndication = spsInfo.levelIdc,
  144. NumOfPictureParameterSets = 1,
  145. PPSBuffer = pps.RawData,
  146. SPSBuffer = sps.RawData
  147. };
  148. flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord;
  149. flvMessagePackWriter.WriteFlvTag(flvTags);
  150. flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
  151. return flvMessagePackWriter.FlushAndGetArray();
  152. }
  153. finally
  154. {
  155. FlvArrayPool.Return(buffer);
  156. }
  157. }
  158. /// <summary>
  159. /// 编码首帧音频,即audioTag[0]
  160. /// </summary>
  161. /// <param name="timestamp"></param>
  162. /// <returns></returns>
  163. public byte[] EncoderFirstAudioTag(ulong timestamp)
  164. {
  165. byte[] buffer = FlvArrayPool.Rent(2048);
  166. try
  167. {
  168. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  169. //flv body audio tag
  170. //flv body tag header
  171. FlvTags flvTags = new FlvTags
  172. {
  173. Type = TagType.Audio,
  174. Timestamp = (uint)timestamp,
  175. //flv body tag body
  176. AudioTagsData = new AudioTags(AACPacketType.AudioSpecificConfig)
  177. };
  178. flvMessagePackWriter.WriteFlvTag(flvTags);
  179. flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
  180. return flvMessagePackWriter.FlushAndGetArray();
  181. }
  182. finally
  183. {
  184. FlvArrayPool.Return(buffer);
  185. }
  186. }
  187. /// <summary>
  188. /// 编码视频
  189. /// </summary>
  190. /// <remarks><paramref name="package"/>必须是组包后的数据</remarks>
  191. /// <param name="package">1078包</param>
  192. /// <param name="needVideoHeader">是否需要首帧视频</param>
  193. /// <returns></returns>
  194. public byte[] EncoderVideoTag(JT1078Package package, bool needVideoHeader = false)
  195. {
  196. if (package.Label3.DataType == JT1078DataType.音频帧) return default;
  197. byte[] buffer = FlvArrayPool.Rent(package.Bodies.Length);
  198. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  199. var nalus = h264Decoder.ParseNALU(package);
  200. if (nalus != null && nalus.Count > 0)
  201. {
  202. var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 6);
  203. var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 7);
  204. var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 8);
  205. nalus.Remove(sps);
  206. nalus.Remove(pps);
  207. nalus.Remove(sei);
  208. if (needVideoHeader)
  209. {
  210. //flv header
  211. var flvHeader = EncoderFlvHeader(true, false);
  212. flvMessagePackWriter.WriteArray(flvHeader);
  213. // always 0
  214. flvMessagePackWriter.WriteUInt32(0);
  215. //解析sps
  216. if (sps == null)
  217. {
  218. return null;
  219. }
  220. var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
  221. ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
  222. SPSInfo spsInfo = h264GolombReader.ReadSPS();
  223. //script tag
  224. var scriptTag = EncoderScriptTag(spsInfo);
  225. flvMessagePackWriter.WriteArray(scriptTag);
  226. // first video tag
  227. var firstVideoTag = EncoderFirstVideoTag(spsInfo, sps, pps, sei);
  228. flvMessagePackWriter.WriteArray(firstVideoTag);
  229. }
  230. foreach (var naln in nalus)
  231. {
  232. var otherVideoTag = EncoderOtherVideoTag(naln);
  233. flvMessagePackWriter.WriteArray(otherVideoTag);
  234. }
  235. }
  236. return flvMessagePackWriter.FlushAndGetArray();
  237. }
  238. /// <summary>
  239. /// 编码非首帧音频
  240. /// </summary>
  241. /// <param name="package"></param>
  242. /// <param name="needAacHeader">是否需要首帧音频</param>
  243. /// <returns></returns>
  244. public byte[] EncoderAudioTag(JT1078Package package, bool needAacHeader = false)
  245. {
  246. if (package.Label3.DataType != JT1078DataType.音频帧) throw new Exception("Incorrect parameter, package must be audio frame");
  247. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(new byte[65536]);
  248. if (needAacHeader)
  249. {
  250. flvMessagePackWriter.WriteArray(EncoderFirstAudioTag(package.Timestamp));
  251. }
  252. byte[] aacFrameData = null;
  253. switch (package.Label2.PT)
  254. {
  255. case JT1078AVType.ADPCM:
  256. ReadOnlySpan<byte> adpcm = package.Bodies;
  257. // 海思芯片编码的音频需要移除海思头,可能还有其他的海思头
  258. if (adpcm.StartsWith(new byte[] { 0x00, 0x01, 0x52, 0x00 })) adpcm = adpcm.Slice(4);
  259. aacFrameData = faacEncoder.Encode(new AdpcmCodec().ToPcm(adpcm.Slice(4).ToArray(), new State()
  260. {
  261. Valprev = (short)((adpcm[1] << 8) | adpcm[0]),
  262. Index = adpcm[2],
  263. Reserved = adpcm[3]
  264. })); break;
  265. case JT1078AVType.G711A:
  266. aacFrameData = faacEncoder.Encode(new G711ACodec().ToPcm(package.Bodies));
  267. break;
  268. case JT1078AVType.AACLC:
  269. aacFrameData = package.Bodies;
  270. break;
  271. }
  272. if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据
  273. {
  274. // Data Tag Frame
  275. flvMessagePackWriter.WriteArray(EncoderAacAudioTag((uint)package.Timestamp, aacFrameData));
  276. }
  277. return flvMessagePackWriter.FlushAndGetArray();
  278. }
  279. /// <summary>
  280. /// 编码非首帧视频
  281. /// </summary>
  282. /// <param name="nALU"></param>
  283. /// <returns></returns>
  284. public byte[] EncoderOtherVideoTag(H264NALU nALU)
  285. {
  286. byte[] buffer = FlvArrayPool.Rent(nALU.RawData.Length);
  287. try
  288. {
  289. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  290. //flv body video tag
  291. //flv body tag header
  292. FlvTags flvTags = new FlvTags
  293. {
  294. Type = TagType.Video,
  295. //pts
  296. Timestamp = (uint)nALU.Timestamp,
  297. TimestampExt = 0,
  298. StreamId = 0,
  299. //flv body tag body
  300. VideoTagsData = new VideoTags()
  301. };
  302. flvTags.VideoTagsData.VideoData = new AvcVideoPacke
  303. {
  304. AvcPacketType = AvcPacketType.Raw
  305. };
  306. //1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧;
  307. //2: inter frame(for AVC, a non - seekable frame) —— H.264的普通I帧;
  308. //ref:https://www.cnblogs.com/chyingp/p/flv-getting-started.html
  309. if (nALU.NALUHeader.NalUnitType == 5)
  310. {
  311. flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
  312. }
  313. else
  314. {
  315. flvTags.VideoTagsData.FrameType = FrameType.InterFrame;
  316. }
  317. flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastFrameInterval;
  318. flvTags.VideoTagsData.VideoData.MultiData = new List<byte[]>();
  319. flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData);
  320. //忽略sei
  321. //if (sei != null && sei.RawData != null && sei.RawData.Length > 0)
  322. //{
  323. // flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData);
  324. //}
  325. flvMessagePackWriter.WriteFlvTag(flvTags);
  326. flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
  327. return flvMessagePackWriter.FlushAndGetArray();
  328. }
  329. finally
  330. {
  331. FlvArrayPool.Return(buffer);
  332. }
  333. }
  334. byte[] EncoderAacAudioTag(uint timestamp, byte[] aacFrameData)
  335. {
  336. byte[] buffer = FlvArrayPool.Rent(aacFrameData.Length);
  337. try
  338. {
  339. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  340. //flv body audio tag
  341. //flv body tag header
  342. FlvTags flvTags = new FlvTags
  343. {
  344. Type = TagType.Audio,
  345. Timestamp = timestamp,
  346. TimestampExt = 0,
  347. StreamId = 0,
  348. //flv body tag body
  349. AudioTagsData = new AudioTags(AACPacketType.AudioFrame, aacFrameData)
  350. };
  351. flvMessagePackWriter.WriteFlvTag(flvTags);
  352. flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
  353. return flvMessagePackWriter.FlushAndGetArray();
  354. }
  355. finally
  356. {
  357. FlvArrayPool.Return(buffer);
  358. }
  359. }
  360. public void Dispose()
  361. {
  362. faacEncoder.Dispose();
  363. }
  364. }
  365. }