MergePusherBaseV2.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. using Vinno.vCloud.FIS.CrossPlatform.Windows.LiveVideo.RTC;
  13. namespace Vinno.vCloud.FIS.CrossPlatform.Windows.LiveVideo
  14. {
  15. public abstract unsafe class MergePusherBaseV2 : PusherBase, ILiveVideoPusherV2
  16. {
  17. private readonly ManualResetEvent _handlingImageEvent = new ManualResetEvent(true);
  18. private readonly Dictionary<EnumLiveChannelCategory, ICapturer> _captures;
  19. private readonly IMergeUtil _mergeUtil;
  20. private readonly FormatConvertUtil _convertUtil;
  21. private AVFrame* _srcFrame;
  22. private AVFrame* _destFrame;
  23. private CancellationTokenSource _tokenSource;
  24. protected bool IsTRTCMode;
  25. protected const int MergeWidth = 1920;
  26. protected const int MergeHeight = 1080;
  27. protected const int MergeFrameRate = 20;
  28. public event EventHandler<ChannelStateEventArgsV2> ChannelStateChanged;
  29. public MergePusherBaseV2()
  30. {
  31. _captures = new Dictionary<EnumLiveChannelCategory, ICapturer>();
  32. _mergeUtil = new RGBMergeUtilV2(MergeWidth, MergeHeight);
  33. _convertUtil = new FormatConvertUtil();
  34. }
  35. public virtual bool StartPusher(IExtendedData pushParams, IEnumerable<CPVideoDeviceOutputInfo> deviceInfos)
  36. {
  37. InitPushersAndMergeOffsets(pushParams, deviceInfos);
  38. StartCaptures();
  39. StartPostMergeImageDataThread();
  40. return true;
  41. }
  42. public virtual bool StopPusher()
  43. {
  44. StopPostMergeImageDataThread();
  45. StopCaptures();
  46. return true;
  47. }
  48. public abstract void SetMute(bool isMute);
  49. public abstract void SwitchMic(string micId);
  50. protected void InitCacheImage(AVPixelFormat destFormat)
  51. {
  52. _srcFrame = AvFrameOperateUtil.Create(MergeWidth, MergeHeight, AVPixelFormat.AV_PIX_FMT_BGRA);
  53. _destFrame = AvFrameOperateUtil.Create(MergeWidth, MergeHeight, destFormat);
  54. }
  55. protected virtual void DealWithFullBuffer(AVFrame* frame)
  56. {
  57. }
  58. protected void PushLiveStateChanged()
  59. {
  60. ChannelStateChanged?.Invoke(this, new ChannelStateEventArgsV2(EnumLiveChannelCategory.Main, EnumDeviceLiveState.Pushing));
  61. }
  62. private void InitPushersAndMergeOffsets(IExtendedData pushParams, IEnumerable<CPVideoDeviceOutputInfo> deviceInfos)
  63. {
  64. var areaImageInfos = new List<AreaImageInfo>();
  65. if (pushParams is RtcExtendedData rtcExtendedData)
  66. {
  67. foreach (var user in rtcExtendedData.UserInfos)
  68. {
  69. var deviceInfo = deviceInfos.FirstOrDefault(c => c.Category == user.Category);
  70. if (deviceInfo != null)
  71. {
  72. ICapturer capturer;
  73. if (!CommonParameter.Instance.IsSonopost && user.Category == EnumLiveChannelCategory.Main)
  74. {
  75. capturer = new TerminalImageCapturerV2(deviceInfo.VideoDeviceId, user.Width, user.Height, EnumImageType.ImageFrameData);
  76. }
  77. else if (!CommonParameter.Instance.IsSonopost && user.Category != EnumLiveChannelCategory.Main)
  78. {
  79. capturer = new Capturer(deviceInfo.VideoDeviceId, user.Width, user.Height, 15, user.Category, deviceInfo.OutputWidth, deviceInfo.OutputHeight, true);
  80. }
  81. else
  82. {
  83. capturer = new Capturer(deviceInfo.VideoDeviceId, user.Width, user.Height, 20, user.Category, deviceInfo.OutputWidth, deviceInfo.OutputHeight, false);
  84. }
  85. areaImageInfos.Add(new AreaImageInfo { Width = user.Width, Height = user.Height, Category = user.Category });
  86. _captures.Add(user.Category, capturer);
  87. }
  88. else
  89. {
  90. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"MergePusherBaseV2 Can not find {user.Category} Device Info!");
  91. }
  92. }
  93. }
  94. else if (pushParams is RtmpExtendedData rtmpExtendedData)
  95. {
  96. foreach (var user in rtmpExtendedData.UserInfos)
  97. {
  98. var deviceInfo = deviceInfos.FirstOrDefault(c => c.Category == user.Category);
  99. if (deviceInfo != null)
  100. {
  101. ICapturer capturer;
  102. if (!CommonParameter.Instance.IsSonopost && user.Category == EnumLiveChannelCategory.Main)
  103. {
  104. capturer = new TerminalImageCapturerV2(deviceInfo.VideoDeviceId, user.Width, user.Height, EnumImageType.ImageFrameData);
  105. }
  106. else if (!CommonParameter.Instance.IsSonopost && user.Category != EnumLiveChannelCategory.Main)
  107. {
  108. capturer = new Capturer(deviceInfo.VideoDeviceId, user.Width, user.Height, 15, user.Category, deviceInfo.OutputWidth, deviceInfo.OutputHeight, true);
  109. }
  110. else
  111. {
  112. capturer = new Capturer(deviceInfo.VideoDeviceId, user.Width, user.Height, 20, user.Category, deviceInfo.OutputWidth, deviceInfo.OutputHeight, false);
  113. }
  114. areaImageInfos.Add(new AreaImageInfo { Width = user.Width, Height = user.Height, Category = user.Category });
  115. _captures.Add(user.Category, capturer);
  116. }
  117. else
  118. {
  119. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"MergePusherBaseV2 Can not find {user.Category} Device Info!");
  120. }
  121. }
  122. }
  123. _mergeUtil.InitAreaOffset(areaImageInfos);
  124. }
  125. private void StartCaptures()
  126. {
  127. foreach (var capturer in _captures.Values)
  128. {
  129. capturer.ImageFrameReceived += OnImageReceived;
  130. capturer.StartCapture();
  131. }
  132. }
  133. private void StopCaptures()
  134. {
  135. foreach (var capture in _captures.Values)
  136. {
  137. capture.ImageFrameReceived -= OnImageReceived;
  138. capture.StopCapture();
  139. capture.Dispose();
  140. }
  141. _captures.Clear();
  142. }
  143. private unsafe void StartPostMergeImageDataThread()
  144. {
  145. if (_tokenSource != null && !_tokenSource.IsCancellationRequested)
  146. {
  147. _tokenSource.Cancel();
  148. }
  149. _tokenSource = new CancellationTokenSource();
  150. Task.Run(() =>
  151. {
  152. while (!_tokenSource.IsCancellationRequested)
  153. {
  154. try
  155. {
  156. _handlingImageEvent.Reset();
  157. _mergeUtil.GetFullBuffer(new IntPtr(_srcFrame->data[0]));
  158. _convertUtil.Convert(_srcFrame, _destFrame);
  159. DealWithFullBuffer(_destFrame);
  160. }
  161. catch (Exception e)
  162. {
  163. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"Get Full Buffer Error:{e}");
  164. }
  165. finally
  166. {
  167. _handlingImageEvent.Set();
  168. Thread.Sleep(35);
  169. }
  170. }
  171. _convertUtil.Dispose();
  172. }, _tokenSource.Token);
  173. }
  174. private void StopPostMergeImageDataThread()
  175. {
  176. _tokenSource?.Cancel();
  177. }
  178. private void OnImageReceived(object sender, ImageFrameData e)
  179. {
  180. try
  181. {
  182. if (sender is ICapturer capture)
  183. {
  184. var area = GetAreaAndCategory(capture.Id, out var category);
  185. _mergeUtil.CopyToArea(area, e);
  186. CopyPreviewData(category, e);
  187. }
  188. }
  189. catch (Exception ex)
  190. {
  191. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"OnImageReceived Error:{ex}");
  192. }
  193. }
  194. private EnumArea GetAreaAndCategory(string id, out EnumLiveChannelCategory category)
  195. {
  196. foreach (var item in _captures)
  197. {
  198. if (item.Value.Id == id)
  199. {
  200. category = item.Key;
  201. return GetArea(category);
  202. }
  203. }
  204. throw new Exception("Can not get correct area");
  205. }
  206. protected abstract EnumArea GetArea(EnumLiveChannelCategory category);
  207. public bool StartSpeedTest(uint appId, string userId, string userSign)
  208. {
  209. if (!IsTRTCMode)
  210. {
  211. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"StartSpeedTest Fail,it only support TRTC Modes");
  212. return false;
  213. }
  214. TRTCPusher pusher = null;
  215. try
  216. {
  217. pusher = new TRTCPusher();
  218. return pusher.StartSpeedTest(appId, userId, userSign);
  219. }
  220. catch (Exception ex)
  221. {
  222. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"Rtc Single PusherV2 StartSpeedTest Error:{ex}");
  223. return false;
  224. }
  225. finally
  226. {
  227. if (pusher != null)
  228. {
  229. pusher.StopSpeedTest();
  230. pusher = null;
  231. }
  232. }
  233. }
  234. public override void DoDispose()
  235. {
  236. _handlingImageEvent.WaitOne();
  237. AvFrameOperateUtil.Destory(_srcFrame);
  238. AvFrameOperateUtil.Destory(_destFrame);
  239. _mergeUtil?.Dispose();
  240. base.DoDispose();
  241. }
  242. }
  243. }