Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

296 lignes
14 KiB

  1. using JT1078.FMp4.Enums;
  2. using JT1078.FMp4.MessagePack;
  3. using JT1078.FMp4.Samples;
  4. using JT1078.Protocol;
  5. using JT1078.Protocol.Enums;
  6. using JT1078.Protocol.H264;
  7. using JT1078.Protocol.MessagePack;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Text;
  12. namespace JT1078.FMp4
  13. {
  14. /// <summary>
  15. /// FMp4编码
  16. /// fmp4
  17. /// stream data
  18. /// ftyp
  19. /// moov
  20. /// styp 1
  21. /// moof 1
  22. /// mdat 1
  23. /// ...
  24. /// styp n
  25. /// moof n
  26. /// mdat n
  27. /// mfra
  28. /// ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing
  29. /// </summary>
  30. public class FMp4Encoder
  31. {
  32. Dictionary<string, TrackInfo> TrackInfos;
  33. const uint DefaultSampleDuration = 40u;
  34. const uint DefaultSampleFlags = 0x1010000;
  35. const uint FirstSampleFlags = 33554432;
  36. const uint TfhdFlags = 0x2003a;
  37. //const uint TrunFlags = 0x205;
  38. const uint TrunFlags = 0x205;
  39. const uint SampleDescriptionIndex = 1;
  40. const uint TrackID = 1;
  41. /// <summary>
  42. ///
  43. /// </summary>
  44. public FMp4Encoder()
  45. {
  46. TrackInfos = new Dictionary<string, TrackInfo>(StringComparer.OrdinalIgnoreCase);
  47. }
  48. /// <summary>
  49. /// 编码ftyp盒子
  50. /// </summary>
  51. /// <returns></returns>
  52. public byte[] FtypBox()
  53. {
  54. byte[] buffer = FMp4ArrayPool.Rent(1024);
  55. FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
  56. try
  57. {
  58. //ftyp
  59. FileTypeBox fileTypeBox = new FileTypeBox();
  60. fileTypeBox.MajorBrand = "isom";
  61. fileTypeBox.MinorVersion = "\0\0\u0002\0";
  62. fileTypeBox.CompatibleBrands.Add("isom");
  63. fileTypeBox.CompatibleBrands.Add("iso2");
  64. fileTypeBox.CompatibleBrands.Add("avc1");
  65. fileTypeBox.CompatibleBrands.Add("mp41");
  66. fileTypeBox.CompatibleBrands.Add("iso5");
  67. fileTypeBox.CompatibleBrands.Add("iso6");
  68. fileTypeBox.ToBuffer(ref writer);
  69. var data = writer.FlushAndGetArray();
  70. return data;
  71. }
  72. finally
  73. {
  74. FMp4ArrayPool.Return(buffer);
  75. }
  76. }
  77. /// <summary>
  78. /// 编码moov盒子
  79. /// </summary>
  80. /// <returns></returns>
  81. public byte[] VideoMoovBox(in H264NALU sps, in H264NALU pps)
  82. {
  83. byte[] buffer = FMp4ArrayPool.Rent(sps.RawData.Length + pps.RawData.Length + 1024);
  84. FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
  85. try
  86. {
  87. ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData);
  88. var spsInfo = h264GolombReader.ReadSPS();
  89. //moov
  90. MovieBox movieBox = new MovieBox();
  91. movieBox.MovieHeaderBox = new MovieHeaderBox(0, 2);
  92. movieBox.MovieHeaderBox.CreationTime = 0;
  93. movieBox.MovieHeaderBox.ModificationTime = 0;
  94. movieBox.MovieHeaderBox.Duration = 0;
  95. movieBox.MovieHeaderBox.Timescale = 1000;
  96. movieBox.MovieHeaderBox.NextTrackID = 99;
  97. movieBox.TrackBox = new TrackBox();
  98. movieBox.TrackBox.TrackHeaderBox = new TrackHeaderBox(0, 3);
  99. movieBox.TrackBox.TrackHeaderBox.CreationTime = 0;
  100. movieBox.TrackBox.TrackHeaderBox.ModificationTime = 0;
  101. movieBox.TrackBox.TrackHeaderBox.TrackID = TrackID;
  102. movieBox.TrackBox.TrackHeaderBox.Duration = 0;
  103. movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false;
  104. movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width;
  105. movieBox.TrackBox.TrackHeaderBox.Height = (uint)spsInfo.height;
  106. movieBox.TrackBox.MediaBox = new MediaBox();
  107. movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox();
  108. movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 0;
  109. movieBox.TrackBox.MediaBox.MediaHeaderBox.ModificationTime = 0;
  110. //movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1200000;
  111. movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1000;
  112. movieBox.TrackBox.MediaBox.MediaHeaderBox.Duration = 0;
  113. movieBox.TrackBox.MediaBox.HandlerBox = new HandlerBox();
  114. movieBox.TrackBox.MediaBox.HandlerBox.HandlerType = HandlerType.vide;
  115. movieBox.TrackBox.MediaBox.HandlerBox.Name = "VideoHandler";
  116. movieBox.TrackBox.MediaBox.MediaInformationBox = new MediaInformationBox();
  117. movieBox.TrackBox.MediaBox.MediaInformationBox.VideoMediaHeaderBox = new VideoMediaHeaderBox();
  118. movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox = new DataInformationBox();
  119. movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox = new DataReferenceBox();
  120. movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>();
  121. movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1));
  122. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox();
  123. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox();
  124. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>();
  125. AVC1SampleEntry avc1 = new AVC1SampleEntry();
  126. avc1.AVCConfigurationBox = new AVCConfigurationBox();
  127. //h264
  128. avc1.Width = (ushort)movieBox.TrackBox.TrackHeaderBox.Width;
  129. avc1.Height = (ushort)movieBox.TrackBox.TrackHeaderBox.Height;
  130. avc1.AVCConfigurationBox.AVCLevelIndication = spsInfo.levelIdc;
  131. avc1.AVCConfigurationBox.AVCProfileIndication = spsInfo.profileIdc;
  132. avc1.AVCConfigurationBox.ProfileCompatibility = (byte)spsInfo.profileCompat;
  133. avc1.AVCConfigurationBox.PPSs = new List<byte[]>() { pps.RawData };
  134. avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { sps.RawData };
  135. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries.Add(avc1);
  136. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.TimeToSampleBox = new TimeToSampleBox();
  137. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SyncSampleBox = new SyncSampleBox();
  138. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox();
  139. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox();
  140. movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox();
  141. movieBox.MovieExtendsBox = new MovieExtendsBox();
  142. movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>();
  143. TrackExtendsBox trex = new TrackExtendsBox();
  144. trex.TrackID = TrackID;
  145. trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex;
  146. trex.DefaultSampleDuration = 0;
  147. trex.DefaultSampleSize = 0;
  148. trex.DefaultSampleFlags = 0;
  149. movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex);
  150. movieBox.ToBuffer(ref writer);
  151. var data = writer.FlushAndGetArray();
  152. return data;
  153. }
  154. finally
  155. {
  156. FMp4ArrayPool.Return(buffer);
  157. }
  158. }
  159. /// <summary>
  160. /// styp
  161. /// </summary>
  162. /// <returns></returns>
  163. public byte[] StypBox()
  164. {
  165. byte[] buffer = FMp4ArrayPool.Rent(1024);
  166. FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
  167. try
  168. {
  169. SegmentTypeBox stypTypeBox = new SegmentTypeBox();
  170. stypTypeBox.MajorBrand = "isom";
  171. stypTypeBox.MinorVersion = "\0\0\0\0";
  172. stypTypeBox.CompatibleBrands.Add("isom");
  173. stypTypeBox.CompatibleBrands.Add("mp42");
  174. stypTypeBox.CompatibleBrands.Add("msdh");
  175. stypTypeBox.CompatibleBrands.Add("msix");
  176. stypTypeBox.CompatibleBrands.Add("iso5");
  177. stypTypeBox.CompatibleBrands.Add("iso6");
  178. stypTypeBox.ToBuffer(ref writer);
  179. var data = writer.FlushAndGetArray();
  180. return data;
  181. }
  182. finally
  183. {
  184. FMp4ArrayPool.Return(buffer);
  185. }
  186. }
  187. /// <summary>
  188. /// 编码其他视频数据盒子
  189. /// </summary>
  190. /// <returns></returns>
  191. public byte[] OtherVideoBox(in List<H264NALU> nalus)
  192. {
  193. byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.RawData.Length + s.StartCodePrefix.Length) + 4096);
  194. FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
  195. try
  196. {
  197. var truns = new List<TrackRunBox.TrackRunInfo>();
  198. List<byte[]> rawdatas = new List<byte[]>();
  199. uint iSize = 0;
  200. ulong lastTimestamp = 0;
  201. string key = string.Empty;
  202. for (var i = 0; i < nalus.Count; i++)
  203. {
  204. var nalu = nalus[i];
  205. if (string.IsNullOrEmpty(key))
  206. {
  207. key = nalu.GetKey();
  208. }
  209. rawdatas.Add(nalu.RawData);
  210. if (nalu.DataType == Protocol.Enums.JT1078DataType.视频I帧)
  211. {
  212. iSize += (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length);
  213. }
  214. else
  215. {
  216. if (iSize > 0)
  217. {
  218. truns.Add(new TrackRunBox.TrackRunInfo()
  219. {
  220. SampleDuration=40,
  221. SampleSize = iSize,
  222. });
  223. iSize = 0;
  224. }
  225. truns.Add(new TrackRunBox.TrackRunInfo()
  226. {
  227. SampleDuration = 40,
  228. SampleSize = (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length),
  229. });
  230. }
  231. if (i == (nalus.Count - 1))
  232. {
  233. lastTimestamp = nalu.Timestamp;
  234. }
  235. }
  236. if (TrackInfos.TryGetValue(key, out TrackInfo trackInfo))
  237. {
  238. if (trackInfo.SN == uint.MaxValue)
  239. {
  240. trackInfo.SN = 1;
  241. }
  242. trackInfo.SN++;
  243. }
  244. else
  245. {
  246. trackInfo = new TrackInfo { SN = 1, DTS = 0 };
  247. TrackInfos.Add(key, trackInfo);
  248. }
  249. var movieFragmentBox = new MovieFragmentBox();
  250. movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox();
  251. movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN;
  252. movieFragmentBox.TrackFragmentBox = new TrackFragmentBox();
  253. movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags);
  254. movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID;
  255. movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex;
  256. movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration;
  257. movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize;
  258. movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags;
  259. movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox();
  260. movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.DTS;
  261. trackInfo.DTS += (ulong)(truns.Count * DefaultSampleDuration);
  262. TrackInfos[key] = trackInfo;
  263. //trun
  264. movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: TrunFlags);
  265. movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FirstSampleFlags;
  266. movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns;
  267. movieFragmentBox.ToBuffer(ref writer);
  268. var mediaDataBox = new MediaDataBox();
  269. mediaDataBox.Data = rawdatas;
  270. mediaDataBox.ToBuffer(ref writer);
  271. var data = writer.FlushAndGetArray();
  272. return data;
  273. }
  274. finally
  275. {
  276. FMp4ArrayPool.Return(buffer);
  277. }
  278. }
  279. struct TrackInfo
  280. {
  281. public uint SN { get; set; }
  282. public ulong DTS { get; set; }
  283. }
  284. }
  285. }