MergePusherBase.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using FFmpeg.AutoGen;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Vinno.FIS.TRTCClient.Common.Enum;
  8. using Vinno.vCloud.FIS.CrossPlatform.Common;
  9. using Vinno.vCloud.FIS.CrossPlatform.Common.Enum;
  10. using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo;
  11. using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo.Interface;
  12. namespace Vinno.vCloud.FIS.CrossPlatform.Windows.LiveVideo
  13. {
  14. public abstract unsafe class MergePusherBase : PusherBase, ILiveVideoPusherForSonopost
  15. {
  16. private readonly Dictionary<EnumLiveChannelCategory, ICapturer> _captures;
  17. private readonly IMergeUtil _mergeUtil;
  18. private readonly FormatConvertUtil _convertUtil;
  19. private readonly ManualResetEvent _handlingImageEvent = new ManualResetEvent(true);
  20. private CancellationTokenSource _tokenSource;
  21. private AVFrame* _srcFrame;
  22. private AVFrame* _destFrame;
  23. protected const int MergeWidth = 1920;
  24. protected const int MergeHeight = 1080;
  25. protected const int MergeFrameRate = 20;
  26. public event EventHandler<ChannelStateEventArgs> ChannelStateChanged;
  27. public MergePusherBase()
  28. {
  29. _captures = new Dictionary<EnumLiveChannelCategory, ICapturer>();
  30. _mergeUtil = new RGBMergeUtil(MergeWidth, MergeHeight);
  31. _convertUtil = new FormatConvertUtil();
  32. }
  33. public virtual bool StartPusher(IExtendedData pushParams, IEnumerable<CPVideoDeviceInfo> deviceInfos)
  34. {
  35. InitPushersAndMergeOffsets(pushParams, deviceInfos);
  36. StartCaptures();
  37. StartPostMergeImageDataThread();
  38. return true;
  39. }
  40. public abstract void SetMute(bool isMute);
  41. public virtual bool StopPusher()
  42. {
  43. StopPostMergeImageDataThread();
  44. StopCaptures();
  45. return true;
  46. }
  47. protected void InitCacheImage(AVPixelFormat destFormat)
  48. {
  49. _srcFrame = AvFrameOperateUtil.Create(MergeWidth, MergeHeight, AVPixelFormat.AV_PIX_FMT_BGRA);
  50. _destFrame = AvFrameOperateUtil.Create(MergeWidth, MergeHeight, destFormat);
  51. }
  52. protected virtual void DealWithFullBuffer(AVFrame* frame)
  53. {
  54. }
  55. protected void PushLiveStateChanged()
  56. {
  57. ChannelStateChanged?.Invoke(this, new ChannelStateEventArgs(EnumLiveChannelCategory.Main, EnumLiveStates.TerminalIsPushing));
  58. }
  59. private void InitPushersAndMergeOffsets(IExtendedData pushParams, IEnumerable<CPVideoDeviceInfo> deviceInfos)
  60. {
  61. var areaImageInfos = new List<AreaImageInfo>();
  62. if (pushParams is RtcExtendedData rtcExtendedData)
  63. {
  64. foreach (var user in rtcExtendedData.UserInfos)
  65. {
  66. var deviceInfo = deviceInfos.FirstOrDefault(c => c.Category == user.Category);
  67. if (deviceInfo != null)
  68. {
  69. var capturer = new Capturer(deviceInfo.Id, user.Width, user.Height, user.Category == EnumLiveChannelCategory.Main ? 20 : 15, user.Category, deviceInfo.Width, deviceInfo.Height, false);
  70. areaImageInfos.Add(new AreaImageInfo { Width = user.Width, Height = user.Height, Category = user.Category });
  71. _captures.Add(user.Category, capturer);
  72. }
  73. else
  74. {
  75. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"Can not find {user.Category} Device Info!");
  76. }
  77. }
  78. }
  79. else if (pushParams is RtmpExtendedData rtmpExtendedData)
  80. {
  81. foreach (var user in rtmpExtendedData.UserInfos)
  82. {
  83. var deviceInfo = deviceInfos.FirstOrDefault(c => c.Category == user.Category);
  84. if (deviceInfo != null)
  85. {
  86. var capturer = new Capturer(deviceInfo.Id, user.Width, user.Height, user.Category == EnumLiveChannelCategory.Main ? 20 : 15, user.Category, deviceInfo.Width, deviceInfo.Height, false);
  87. areaImageInfos.Add(new AreaImageInfo { Width = user.Width, Height = user.Height, Category = user.Category });
  88. _captures.Add(user.Category, capturer);
  89. }
  90. else
  91. {
  92. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"Can not find {user.Category} Device Info!");
  93. }
  94. }
  95. }
  96. _mergeUtil.InitAreaOffset(areaImageInfos);
  97. }
  98. private void StartCaptures()
  99. {
  100. foreach (var capturer in _captures.Values)
  101. {
  102. capturer.ImageFrameReceived += OnImageReceived;
  103. capturer.StartCapture();
  104. }
  105. }
  106. private void StopCaptures()
  107. {
  108. foreach (var capture in _captures.Values)
  109. {
  110. capture.ImageFrameReceived -= OnImageReceived;
  111. capture.StopCapture();
  112. capture.Dispose();
  113. }
  114. _captures.Clear();
  115. }
  116. private unsafe void StartPostMergeImageDataThread()
  117. {
  118. if (_tokenSource != null && !_tokenSource.IsCancellationRequested)
  119. {
  120. _tokenSource.Cancel();
  121. }
  122. _tokenSource = new CancellationTokenSource();
  123. Task.Run(() =>
  124. {
  125. while (!_tokenSource.IsCancellationRequested)
  126. {
  127. try
  128. {
  129. _handlingImageEvent.Reset();
  130. _mergeUtil.GetFullBuffer(new IntPtr(_srcFrame->data[0]));
  131. _convertUtil.Convert(_srcFrame, _destFrame);
  132. DealWithFullBuffer(_destFrame);
  133. }
  134. catch (Exception e)
  135. {
  136. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"Get Full Buffer Error:{e}");
  137. }
  138. finally
  139. {
  140. _handlingImageEvent.Set();
  141. Thread.Sleep(35);
  142. }
  143. }
  144. _convertUtil.Dispose();
  145. }, _tokenSource.Token);
  146. }
  147. private void StopPostMergeImageDataThread()
  148. {
  149. _tokenSource?.Cancel();
  150. }
  151. private void OnImageReceived(object sender, ImageFrameData e)
  152. {
  153. try
  154. {
  155. if (sender is ICapturer capture)
  156. {
  157. var area = GetAreaAndCategory(capture.Id, out var category);
  158. _mergeUtil.CopyToArea(area, e);
  159. CopyPreviewData(category, e);
  160. }
  161. }
  162. catch (Exception ex)
  163. {
  164. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"OnImageReceived Error:{ex}");
  165. }
  166. }
  167. private EnumArea GetAreaAndCategory(string id, out EnumLiveChannelCategory category)
  168. {
  169. foreach (var item in _captures)
  170. {
  171. if (item.Value.Id == id)
  172. {
  173. category = item.Key;
  174. return GetArea(category);
  175. }
  176. }
  177. throw new Exception("Can not get correct area");
  178. }
  179. protected abstract EnumArea GetArea(EnumLiveChannelCategory category);
  180. public override void DoDispose()
  181. {
  182. _handlingImageEvent.WaitOne();
  183. AvFrameOperateUtil.Destory(_srcFrame);
  184. AvFrameOperateUtil.Destory(_destFrame);
  185. _mergeUtil?.Dispose();
  186. base.DoDispose();
  187. }
  188. }
  189. }