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.

230 line
11 KiB

  1. using JT1078.Flv.Enums;
  2. using JT1078.Flv.H264;
  3. using JT1078.Flv.MessagePack;
  4. using JT1078.Flv.Metadata;
  5. using JT1078.Protocol.Enums;
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Text;
  11. namespace JT1078.Flv
  12. {
  13. public class FlvEncoder
  14. {
  15. struct FlvFrameInfo
  16. {
  17. public uint PreviousTagSize { get; set;}
  18. public uint LastFrameInterval { get; set; }
  19. }
  20. public static readonly byte[] VideoFlvHeaderBuffer;
  21. private const uint PreviousTagSizeFixedLength = 4;
  22. private static readonly ConcurrentDictionary<string, SPSInfo> VideoSPSDict;
  23. private static readonly Flv.H264.H264Decoder H264Decoder;
  24. private static readonly ConcurrentDictionary<string, FlvFrameInfo> FlvFrameInfoDict;
  25. static FlvEncoder()
  26. {
  27. FlvHeader VideoFlvHeader = new FlvHeader(true, false);
  28. VideoFlvHeaderBuffer = VideoFlvHeader.ToArray().ToArray();
  29. VideoSPSDict = new ConcurrentDictionary<string, SPSInfo>();
  30. FlvFrameInfoDict = new ConcurrentDictionary<string, FlvFrameInfo>();
  31. H264Decoder = new Flv.H264.H264Decoder();
  32. }
  33. /// <summary>
  34. ///
  35. /// </summary>
  36. private void CreateFlvKeyFrame(ref FlvMessagePackWriter flvMessagePackWriter, string key,byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, uint previousTagSize = 0)
  37. {
  38. int currentMarkPosition = 0;
  39. int nextMarkPosition = 0;
  40. currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
  41. //flv body script tag
  42. CreateScriptTagFrame(ref flvMessagePackWriter, spsInfo.width, spsInfo.height, previousTagSize);
  43. nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
  44. //flv body video tag
  45. uint scriptTagFramePreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
  46. AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord();
  47. aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc;
  48. aVCDecoderConfigurationRecord.ProfileCompatibility = (byte)spsInfo.profileCompat;
  49. aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc;
  50. aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1;
  51. aVCDecoderConfigurationRecord.PPSBuffer = ppsRawData;
  52. aVCDecoderConfigurationRecord.SPSBuffer = spsRawData;
  53. currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
  54. CreateVideoTag0Frame(ref flvMessagePackWriter, scriptTagFramePreviousTagSize, aVCDecoderConfigurationRecord);
  55. nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
  56. //flv body video tag 0
  57. uint videoTag0PreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
  58. //cache PreviousTagSize
  59. FlvFrameInfoDict.AddOrUpdate(key, new FlvFrameInfo { PreviousTagSize= videoTag0PreviousTagSize }, (a, b) => {
  60. b.PreviousTagSize = videoTag0PreviousTagSize;
  61. return b;
  62. });
  63. }
  64. private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter, int width, int height,uint previousTagSize, double frameRate = 25d)
  65. {
  66. //flv body PreviousTagSize awalys 0
  67. flvMessagePackWriter.WriteUInt32(previousTagSize);
  68. //flv body script tag
  69. //flv body tag header
  70. FlvTags flvTags = new FlvTags();
  71. flvTags.Type = TagType.ScriptData;
  72. flvTags.Timestamp = 0;
  73. flvTags.TimestampExt = 0;
  74. flvTags.StreamId = 0;
  75. //flv body tag body
  76. flvTags.DataTagsData = new Amf3();
  77. flvTags.DataTagsData.Amf3Metadatas = new List<IAmf3Metadata>();
  78. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Duration
  79. {
  80. Value = 0d
  81. });
  82. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoDataRate
  83. {
  84. Value = 0d
  85. });
  86. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoCodecId
  87. {
  88. Value = 7d
  89. });
  90. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_FrameRate
  91. {
  92. Value = frameRate
  93. });
  94. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Width
  95. {
  96. Value = width
  97. });
  98. flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Height
  99. {
  100. Value = height
  101. });
  102. flvMessagePackWriter.WriteFlvTag(flvTags);
  103. }
  104. private void CreateVideoTag0Frame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord)
  105. {
  106. //flv body PreviousTagSize ScriptTag
  107. flvMessagePackWriter.WriteUInt32(previousTagSize);
  108. //flv body video tag
  109. //flv body tag header
  110. FlvTags flvTags = new FlvTags();
  111. flvTags.Type = TagType.Video;
  112. flvTags.Timestamp = 0;
  113. flvTags.TimestampExt = 0;
  114. flvTags.StreamId = 0;
  115. //flv body tag body
  116. flvTags.VideoTagsData = new VideoTags();
  117. flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
  118. flvTags.VideoTagsData.VideoData = new AvcVideoPacke();
  119. flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.SequenceHeader;
  120. flvTags.VideoTagsData.VideoData.CompositionTime = 0;
  121. flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord;
  122. flvMessagePackWriter.WriteFlvTag(flvTags);
  123. }
  124. private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, FlvFrameInfo flvFrameInfo, H264NALU nALU)
  125. {
  126. //flv body PreviousTagSize
  127. flvMessagePackWriter.WriteUInt32(flvFrameInfo.PreviousTagSize);
  128. //flv body video tag
  129. //flv body tag header
  130. FlvTags flvTags = new FlvTags();
  131. flvTags.Type = TagType.Video;
  132. flvTags.TimestampExt = 0;
  133. flvTags.StreamId = 0;
  134. //flv body tag body
  135. flvTags.VideoTagsData = new VideoTags();
  136. flvTags.VideoTagsData.VideoData = new AvcVideoPacke();
  137. flvTags.VideoTagsData.VideoData.CompositionTime = 0;
  138. flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw;
  139. flvTags.Timestamp = flvFrameInfo.LastFrameInterval;
  140. if (nALU.NALUHeader.NalUnitType == 5 || nALU.DataType== JT1078DataType.视频I帧)
  141. {
  142. flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
  143. }
  144. else
  145. {
  146. flvTags.VideoTagsData.FrameType = FrameType.InterFrame;
  147. }
  148. flvTags.VideoTagsData.VideoData.Data = nALU.RawData;
  149. flvMessagePackWriter.WriteFlvTag(flvTags);
  150. }
  151. public byte[] CreateFlvFrame(List<H264NALU> nALUs, int minimumLength = 65535)
  152. {
  153. byte[] buffer = FlvArrayPool.Rent(minimumLength);
  154. try
  155. {
  156. FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
  157. int currentMarkPosition = 0;
  158. int nextMarkPosition = 0;
  159. H264NALU sps=null, pps=null;
  160. SPSInfo spsInfo = new SPSInfo();
  161. foreach (var naln in nALUs)
  162. {
  163. string key = naln.GetKey();
  164. if (sps != null && pps != null)
  165. {
  166. if (VideoSPSDict.TryGetValue(key, out var spsInfoCache))
  167. {
  168. if (spsInfoCache.height != spsInfo.height && spsInfoCache.width != spsInfo.width)
  169. {
  170. if(FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo))
  171. {
  172. CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo, flvFrameInfo.PreviousTagSize);
  173. VideoSPSDict.TryUpdate(key, spsInfo, spsInfo);
  174. flvFrameInfo.LastFrameInterval = 0;
  175. FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo);
  176. }
  177. }
  178. }
  179. else
  180. {
  181. CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo, 0);
  182. VideoSPSDict.TryAdd(key, spsInfo);
  183. }
  184. }
  185. //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
  186. switch (naln.NALUHeader.NalUnitType)
  187. {
  188. case 5:// IDR
  189. case 1:// I/P/B
  190. if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo))
  191. {
  192. currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
  193. CreateVideoTagOtherFrame(ref flvMessagePackWriter, flvFrameInfo, naln);
  194. nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
  195. uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
  196. flvFrameInfo.PreviousTagSize = tmpPreviousTagSize;
  197. flvFrameInfo.LastFrameInterval += naln.LastFrameInterval;
  198. FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo);
  199. }
  200. break;
  201. case 7:// sps
  202. sps = naln;
  203. var rawData = H264Decoder.DiscardEmulationPreventionBytes(naln.RawData);
  204. ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
  205. spsInfo = h264GolombReader.ReadSPS();
  206. break;
  207. case 8:// pps
  208. pps = naln;
  209. break;
  210. case 6://SEI
  211. break;
  212. default:
  213. break;
  214. }
  215. }
  216. return flvMessagePackWriter.FlushAndGetArray();
  217. }
  218. finally
  219. {
  220. FlvArrayPool.Return(buffer);
  221. }
  222. }
  223. }
  224. }