VideoProvider.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Threading.Tasks;
  4. using Vinno.FIS.TRTCClient.Common.Enum;
  5. using Vinno.IUS.Common.Log;
  6. using Vinno.IUS.Common.Network.Leaf;
  7. using Vinno.IUS.Common.Network.Transfer;
  8. using Vinno.vCloud.FIS.CrossPlatform.Common;
  9. using Vinno.vCloud.FIS.CrossPlatform.Common.Consultation.Interface;
  10. using Vinno.vCloud.FIS.CrossPlatform.Common.Enum;
  11. using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo;
  12. using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo.Interface;
  13. using Vinno.vCloud.Protocol.Messages.Live;
  14. namespace Vinno.vCloud.Common.FIS.Consultation
  15. {
  16. public abstract class VideoProvider : IDisposable
  17. {
  18. private readonly ConsultationVideoFrameData _consultationVideoFrameData;
  19. private readonly object _connectionKeeperLock = new object();
  20. private ConsultationConnectionKeeper _connectionKeeper;
  21. private string _liveServiceUrl;
  22. protected ConsultationMemberInfo _consultationMemberInfo;
  23. protected bool _disposed;
  24. public event EventHandler Offlined;
  25. public ConsultationMemberInfo ConsultationMemberInfo
  26. {
  27. get => _consultationMemberInfo;
  28. set
  29. {
  30. _consultationMemberInfo = value;
  31. }
  32. }
  33. /// <summary>
  34. /// Video frame arrived
  35. /// </summary>
  36. public event EventHandler<ConsultationVideoFrameData> VideoFrameArrived;
  37. public VideoProvider(ConsultationMemberInfo consultationMemberInfo)
  38. {
  39. _consultationMemberInfo = consultationMemberInfo;
  40. _consultationVideoFrameData = new ConsultationVideoFrameData(null, _consultationMemberInfo);
  41. }
  42. public abstract void Dispose();
  43. protected void UpdateFrame(CPVideoFrameData data)
  44. {
  45. _consultationVideoFrameData.VideoFrameData = data;
  46. VideoFrameArrived?.Invoke(this, _consultationVideoFrameData);
  47. }
  48. public void StartConnectionKeeper(string roomId, string userId, ClientLeaf leaf, bool isDelayStart = true)
  49. {
  50. lock (_connectionKeeperLock)
  51. {
  52. if (leaf == null)
  53. {
  54. Logger.WriteLineError("Start video provider Chat Connection Keeper failed,the leaf is null");
  55. return;
  56. }
  57. var liveServiceUrl = GetLiveServiceUrl(leaf);
  58. if (string.IsNullOrEmpty(liveServiceUrl))
  59. {
  60. Logger.WriteLineError($"Start video provider Chat Connection Keeper failed,the live Service Url is null!");
  61. return;
  62. }
  63. _connectionKeeper = new ConsultationConnectionKeeper(roomId, userId, liveServiceUrl);
  64. _connectionKeeper.Offlined += OnOfflined;
  65. _connectionKeeper.ClientLeafClosed += OnConnectionKeeperLeafClose;
  66. _connectionKeeper.Start(isDelayStart);
  67. }
  68. }
  69. /// <summary>
  70. /// Get live service url
  71. /// </summary>
  72. /// <returns></returns>
  73. private string GetLiveServiceUrl(ClientLeaf leaf)
  74. {
  75. try
  76. {
  77. if (string.IsNullOrEmpty(_liveServiceUrl))
  78. {
  79. using (var request = MessagePool.GetMessage<GetLiveServiceUrlRequest>())
  80. {
  81. var result = leaf.Send(request);
  82. if (result != null)
  83. {
  84. var resultMessage = GetLiveServiceUrlResult.Convert(result);
  85. if (resultMessage != null)
  86. {
  87. _liveServiceUrl = resultMessage.Url;
  88. }
  89. }
  90. }
  91. }
  92. return _liveServiceUrl;
  93. }
  94. catch (Exception ex)
  95. {
  96. Logger.WriteLineError($"Get live service url error: {ex}");
  97. }
  98. return null;
  99. }
  100. private void OnConnectionKeeperLeafClose(object sender, EventArgs e)
  101. {
  102. lock (_connectionKeeperLock)
  103. {
  104. _connectionKeeper.Offlined -= OnOfflined;
  105. _connectionKeeper.ClientLeafClosed -= OnConnectionKeeperLeafClose;
  106. _connectionKeeper.Stop();
  107. Offlined?.Invoke(this, e);
  108. }
  109. }
  110. private void OnOfflined(object sender, EventArgs e)
  111. {
  112. Offlined?.Invoke(this, e);
  113. Logger.WriteLineInfo($"Connection keeper offline ");
  114. }
  115. internal void StopConnectionKeeper()
  116. {
  117. if (_connectionKeeper != null)
  118. {
  119. _connectionKeeper.Offlined -= OnOfflined;
  120. _connectionKeeper.ClientLeafClosed -= OnConnectionKeeperLeafClose;
  121. _connectionKeeper?.Stop();
  122. _connectionKeeper = null;
  123. }
  124. }
  125. }
  126. public class RtcVideoProvider : VideoProvider
  127. {
  128. public RtcVideoProvider(ConsultationMemberInfo consultationMemberInfo) : base(consultationMemberInfo)
  129. {
  130. }
  131. public override void Dispose()
  132. {
  133. }
  134. }
  135. public class RtmpVideoProvider : VideoProvider
  136. {
  137. private IRtmpPlayer _smartPlayer;
  138. private int _videoBufferSize = 300;
  139. private bool _isClosed;
  140. private readonly object _locker = new object();
  141. public RtmpVideoProvider(ConsultationMemberInfo consultationMemberInfo) : base(consultationMemberInfo)
  142. {
  143. CreateSmartPlayer(consultationMemberInfo.PullRtmpUrl);
  144. }
  145. private void CreateSmartPlayer(string url)
  146. {
  147. if (!string.IsNullOrEmpty(url))
  148. {
  149. _smartPlayer = CrossPlatformHelper.Instance.RtmpPlayerCreator.CreateLivePlayer(url);
  150. _smartPlayer.Buffer = _videoBufferSize;
  151. _smartPlayer.VideoFrameReceived += OnVideoFrameReceived;
  152. Task.Run(async () =>
  153. {
  154. await Task.Delay(3000);
  155. lock (_locker)
  156. {
  157. if (!_isClosed)
  158. {
  159. _smartPlayer.Play();
  160. }
  161. }
  162. });
  163. }
  164. }
  165. private void CloseSmartPlayer()
  166. {
  167. if (_smartPlayer != null)
  168. {
  169. lock (_locker)
  170. {
  171. _isClosed = true;
  172. }
  173. _smartPlayer.VideoFrameReceived -= OnVideoFrameReceived;
  174. _smartPlayer.Stop();
  175. _smartPlayer = null;
  176. }
  177. }
  178. private void OnVideoFrameReceived(object sender, CPVideoFrameData e)
  179. {
  180. UpdateFrame(e);
  181. }
  182. public override void Dispose()
  183. {
  184. if (_disposed)
  185. return;
  186. CloseSmartPlayer();
  187. StopConnectionKeeper();
  188. _disposed = true;
  189. }
  190. ~RtmpVideoProvider()
  191. {
  192. }
  193. }
  194. public class RtmpVideoPusher : VideoProvider
  195. {
  196. private IRtmpPusherV2 _smartPublisher;
  197. private CPVideoFrameData _videoFrameData;
  198. public RtmpVideoPusher(ConsultationMemberInfo consultationMemberInfo, string videoDeviceId, string micDeviceId) : base(consultationMemberInfo)
  199. {
  200. var deviceInfo = new CPVideoDeviceOutputInfo
  201. {
  202. VideoDeviceId = videoDeviceId,
  203. IdForServer = "",
  204. VideoDeviceSourceType = EnumVideoDeviceSourceType.Camera,
  205. OutputWidth = 640,
  206. OutputHeight = 480,
  207. Category = EnumLiveChannelCategory.Main,
  208. };
  209. _smartPublisher = CrossPlatformHelper.Instance.RtmpPusherCreator.CreateLivePusherV2(deviceInfo, consultationMemberInfo.PushUrl, micDeviceId, 640, 480);
  210. _smartPublisher.ImageFrameReceived += OnImageFrameReceived;
  211. _smartPublisher.Start();
  212. }
  213. ~RtmpVideoPusher()
  214. {
  215. }
  216. public override void Dispose()
  217. {
  218. if (_disposed)
  219. return;
  220. if (_smartPublisher != null)
  221. {
  222. _smartPublisher.ImageFrameReceived -= OnImageFrameReceived;
  223. _smartPublisher.Stop();
  224. _smartPublisher.Dispose();
  225. _smartPublisher = null;
  226. }
  227. StopConnectionKeeper();
  228. _disposed = true;
  229. }
  230. private void OnImageFrameReceived(object sender, ImageFrameData e)
  231. {
  232. if (_videoFrameData == null)
  233. {
  234. _videoFrameData = new CPVideoFrameData(e.Width, e.Height, new byte[e.Size]);
  235. }
  236. else if (_videoFrameData.Height != e.Height || _videoFrameData.Width != e.Width)
  237. {
  238. _videoFrameData.Height = e.Height;
  239. _videoFrameData.Width = e.Width;
  240. _videoFrameData.Data = new byte[e.Size];
  241. }
  242. Marshal.Copy(e.Data, _videoFrameData.Data, 0, e.Size);
  243. UpdateFrame(_videoFrameData);
  244. }
  245. public void SetMute(bool isMute)
  246. {
  247. if (_smartPublisher != null)
  248. {
  249. _smartPublisher.SetMute(isMute);
  250. }
  251. }
  252. public void SwitchCamera()
  253. {
  254. if (_smartPublisher != null)
  255. {
  256. //TODOFelix
  257. }
  258. }
  259. }
  260. }