No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

374 líneas
15 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. /// <param name="package">1078包</param>
  191. /// <param name="needVideoHeader">是否需要首帧视频</param>
  192. /// <returns></returns>
  193. public byte[] EncoderVideoTag(JT1078Package package, bool needVideoHeader = false)
  194. {
  195. if (package.Label3.DataType == JT1078DataType.音频帧) return default;
  196. byte[] buffer = FlvArrayPool.Rent(65535);
  197. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  198. var nalus = h264Decoder.ParseNALU(package);
  199. if (nalus != null && nalus.Count > 0)
  200. {
  201. var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 6);
  202. var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 7);
  203. var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 8);
  204. nalus.Remove(sps);
  205. nalus.Remove(pps);
  206. nalus.Remove(sei);
  207. if (needVideoHeader)
  208. {
  209. //flv header
  210. var flvHeader = EncoderFlvHeader(true, false);
  211. flvMessagePackWriter.WriteArray(flvHeader);
  212. // always 0
  213. flvMessagePackWriter.WriteUInt32(0);
  214. //解析sps
  215. if (sps == null)
  216. {
  217. return null;
  218. }
  219. var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
  220. ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
  221. SPSInfo spsInfo = h264GolombReader.ReadSPS();
  222. //script tag
  223. var scriptTag = EncoderScriptTag(spsInfo);
  224. flvMessagePackWriter.WriteArray(scriptTag);
  225. // first video tag
  226. var firstVideoTag = EncoderFirstVideoTag(spsInfo, sps, pps, sei);
  227. flvMessagePackWriter.WriteArray(firstVideoTag);
  228. }
  229. foreach (var naln in nalus)
  230. {
  231. var otherVideoTag = EncoderOtherVideoTag(naln);
  232. flvMessagePackWriter.WriteArray(otherVideoTag);
  233. }
  234. }
  235. return flvMessagePackWriter.FlushAndGetArray();
  236. }
  237. /// <summary>
  238. /// 编码非首帧音频
  239. /// </summary>
  240. /// <param name="package"></param>
  241. /// <param name="needAacHeader">是否需要首帧音频</param>
  242. /// <returns></returns>
  243. public byte[] EncoderAudioTag(JT1078Package package, bool needAacHeader = false)
  244. {
  245. if (package.Label3.DataType != JT1078DataType.音频帧) throw new Exception("Incorrect parameter, package must be audio frame");
  246. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(new byte[65536]);
  247. if (needAacHeader)
  248. {
  249. flvMessagePackWriter.WriteArray(EncoderFirstAudioTag(package.Timestamp));
  250. }
  251. byte[] aacFrameData = null;
  252. switch (package.Label2.PT)
  253. {
  254. case Jt1078AudioType.ADPCM:
  255. ReadOnlySpan<byte> adpcm = package.Bodies;
  256. // 海思芯片编码的音频需要移除海思头,可能还有其他的海思头
  257. if (adpcm.StartsWith(new byte[] { 0x00, 0x01, 0x52, 0x00 })) adpcm = adpcm.Slice(4);
  258. aacFrameData = faacEncoder.Encode(new AdpcmCodec().ToPcm(adpcm.Slice(4).ToArray(), new State()
  259. {
  260. Valprev = (short)((adpcm[1] << 8) | adpcm[0]),
  261. Index = adpcm[2],
  262. Reserved = adpcm[3]
  263. })); break;
  264. case Jt1078AudioType.G711A:
  265. aacFrameData = faacEncoder.Encode(new G711ACodec().ToPcm(package.Bodies));
  266. break;
  267. case Jt1078AudioType.AACLC:
  268. aacFrameData = package.Bodies;
  269. break;
  270. }
  271. if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据
  272. {
  273. // Data Tag Frame
  274. flvMessagePackWriter.WriteArray(EncoderAacAudioTag((uint)package.Timestamp, aacFrameData));
  275. }
  276. return flvMessagePackWriter.FlushAndGetArray();
  277. }
  278. /// <summary>
  279. /// 编码非首帧视频
  280. /// </summary>
  281. /// <param name="nALU"></param>
  282. /// <returns></returns>
  283. public byte[] EncoderOtherVideoTag(H264NALU nALU)
  284. {
  285. byte[] buffer = FlvArrayPool.Rent(65535);
  286. try
  287. {
  288. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  289. //flv body video tag
  290. //flv body tag header
  291. FlvTags flvTags = new FlvTags
  292. {
  293. Type = TagType.Video,
  294. //pts
  295. Timestamp = (uint)nALU.Timestamp,
  296. TimestampExt = 0,
  297. StreamId = 0,
  298. //flv body tag body
  299. VideoTagsData = new VideoTags()
  300. };
  301. flvTags.VideoTagsData.VideoData = new AvcVideoPacke
  302. {
  303. AvcPacketType = AvcPacketType.Raw
  304. };
  305. //1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧;
  306. //2: inter frame(for AVC, a non - seekable frame) —— H.264的普通I帧;
  307. //ref:https://www.cnblogs.com/chyingp/p/flv-getting-started.html
  308. if (nALU.NALUHeader.NalUnitType == 5)
  309. {
  310. flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
  311. }
  312. else
  313. {
  314. flvTags.VideoTagsData.FrameType = FrameType.InterFrame;
  315. }
  316. flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastFrameInterval;
  317. flvTags.VideoTagsData.VideoData.MultiData = new List<byte[]>();
  318. flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData);
  319. //忽略sei
  320. //if (sei != null && sei.RawData != null && sei.RawData.Length > 0)
  321. //{
  322. // flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData);
  323. //}
  324. flvMessagePackWriter.WriteFlvTag(flvTags);
  325. flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
  326. return flvMessagePackWriter.FlushAndGetArray();
  327. }
  328. finally
  329. {
  330. FlvArrayPool.Return(buffer);
  331. }
  332. }
  333. byte[] EncoderAacAudioTag(uint timestamp, byte[] aacFrameData)
  334. {
  335. byte[] buffer = FlvArrayPool.Rent(65535);
  336. try
  337. {
  338. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  339. //flv body audio tag
  340. //flv body tag header
  341. FlvTags flvTags = new FlvTags
  342. {
  343. Type = TagType.Audio,
  344. Timestamp = timestamp,
  345. TimestampExt = 0,
  346. StreamId = 0,
  347. //flv body tag body
  348. AudioTagsData = new AudioTags(AACPacketType.AudioFrame, aacFrameData)
  349. };
  350. flvMessagePackWriter.WriteFlvTag(flvTags);
  351. flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
  352. return flvMessagePackWriter.FlushAndGetArray();
  353. }
  354. finally
  355. {
  356. FlvArrayPool.Return(buffer);
  357. }
  358. }
  359. public void Dispose()
  360. {
  361. faacEncoder.Dispose();
  362. }
  363. }
  364. }