using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using Vinno.FIS.TRTCClient.Common.Enum; using Vinno.IUS.Common.Log; using Vinno.vCloud.Common.FIS.LiveVideos; using Vinno.vCloud.FIS.CrossPlatform.Common; using Vinno.vCloud.FIS.CrossPlatform.Common.Consultation.Interface; using Vinno.vCloud.FIS.CrossPlatform.Common.Enum; using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo; using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo.Interface; using WingInterfaceLibrary.LiveConsultation; namespace Vinno.vCloud.Common.FIS.Consultation { public abstract class VideoProviderV2 : IDisposable { private readonly ConsultationVideoFrameData _consultationVideoFrameData; private readonly object _connectionKeeperLock = new object(); private PushHeartRateKeeperV2 _connectionKeeper; protected ConsultationMemberInfo _consultationMemberInfo; protected bool _disposed; public event EventHandler Offlined; public ConsultationMemberInfo ConsultationMemberInfo { get => _consultationMemberInfo; set { _consultationMemberInfo = value; } } /// /// Video frame arrived /// public event EventHandler VideoFrameArrived; public VideoProviderV2(ConsultationMemberInfo consultationMemberInfo) { _consultationMemberInfo = consultationMemberInfo; _consultationVideoFrameData = new ConsultationVideoFrameData(null, _consultationMemberInfo); } public abstract void Dispose(); protected void UpdateFrame(CPVideoFrameData data) { _consultationVideoFrameData.VideoFrameData = data; VideoFrameArrived?.Invoke(this, _consultationVideoFrameData); } public void StartConsultationConnectionKeeper(string token, string consultationCode, int heartRateInterval, ILiveConsultationService liveConsultationService) { lock (_connectionKeeperLock) { _connectionKeeper = new PushHeartRateKeeperV2(token, consultationCode, heartRateInterval, liveConsultationService); _connectionKeeper.Offlined += OnOfflined; _connectionKeeper.Start(); } } public void ChangeConsultationCode(string consultationCode) { lock (_connectionKeeperLock) { if (_connectionKeeper != null) { _connectionKeeper.SetHeartRateCode(consultationCode); Logger.WriteLineInfo($"Consultation Connection Keeper Change Consultation Code {consultationCode}"); } } } private void OnOfflined(object sender, EventArgs e) { Offlined?.Invoke(this, e); Logger.WriteLineInfo($"Connection keeper offline "); } internal void DisposeConnectionKeeper() { if (_connectionKeeper != null) { _connectionKeeper.Offlined -= OnOfflined; _connectionKeeper.Stop(); _connectionKeeper = null; } } } public class RtcVideoProviderV2 : VideoProviderV2 { public RtcVideoProviderV2(ConsultationMemberInfo consultationMemberInfo) : base(consultationMemberInfo) { } public override void Dispose() { if (_disposed) return; DisposeConnectionKeeper(); _disposed = true; } } public class RtmpVideoProviderV2 : VideoProviderV2 { public RtmpVideoProviderV2(ConsultationMemberInfo consultationMemberInfo) : base(consultationMemberInfo) { } public override void Dispose() { if (_disposed) return; DisposeConnectionKeeper(); _disposed = true; } } public class RtmpVideoPlayerV2 : VideoProviderV2 { private IRtmpPlayer _smartPlayer; private int _videoBufferSize = 300; private bool _isClosed; private readonly object _locker = new object(); public RtmpVideoPlayerV2(ConsultationMemberInfo consultationMemberInfo) : base(consultationMemberInfo) { CreateSmartPlayer(consultationMemberInfo.PullRtmpUrl); } private void CreateSmartPlayer(string url) { if (!string.IsNullOrEmpty(url)) { _smartPlayer = CrossPlatformHelper.Instance.RtmpPlayerCreator.CreateLivePlayer(url); _smartPlayer.Buffer = _videoBufferSize; _smartPlayer.VideoFrameReceived += OnVideoFrameReceived; Task.Run(async () => { await Task.Delay(3000); lock (_locker) { if (!_isClosed) { _smartPlayer.Play(); } } }); } } private void CloseSmartPlayer() { if (_smartPlayer != null) { lock (_locker) { _isClosed = true; } _smartPlayer.VideoFrameReceived -= OnVideoFrameReceived; _smartPlayer.Stop(); _smartPlayer = null; } } private void OnVideoFrameReceived(object sender, CPVideoFrameData e) { UpdateFrame(e); } public override void Dispose() { if (_disposed) return; DisposeConnectionKeeper(); CloseSmartPlayer(); _disposed = true; } ~RtmpVideoPlayerV2() { } } public class RtmpVideoPusherV2 : VideoProviderV2 { private IRtmpPusherV2 _smartPublisher; private CPVideoFrameData _videoFrameData; public RtmpVideoPusherV2(ConsultationMemberInfo consultationMemberInfo, string videoDeviceId, string micDeviceId, bool onlyForRtmpPushing) : base(consultationMemberInfo) { var deviceInfo = new CPVideoDeviceOutputInfo { VideoDeviceId = videoDeviceId, IdForServer = "", VideoDeviceSourceType = EnumVideoDeviceSourceType.Camera, OutputWidth = 640, OutputHeight = 480, Category = EnumLiveChannelCategory.Main, }; _smartPublisher = CrossPlatformHelper.Instance.RtmpPusherCreator.CreateLivePusherV2(deviceInfo, consultationMemberInfo.PushUrl, micDeviceId, 640, 480); if (!onlyForRtmpPushing) { _smartPublisher.ImageFrameReceived += OnImageFrameReceived; } _smartPublisher.Start(); } ~RtmpVideoPusherV2() { } private void OnImageFrameReceived(object sender, ImageFrameData e) { if (_videoFrameData == null) { _videoFrameData = new CPVideoFrameData(e.Width, e.Height, new byte[e.Size]); } else if (_videoFrameData.Height != e.Height || _videoFrameData.Width != e.Width) { _videoFrameData.Height = e.Height; _videoFrameData.Width = e.Width; _videoFrameData.Data = new byte[e.Size]; } Marshal.Copy(e.Data, _videoFrameData.Data, 0, e.Size); UpdateFrame(_videoFrameData); } public override void Dispose() { if (_disposed) return; LiveVideoStatusChecker.Instance.IsConsultationLivingInCurrentMachine = false; if (_smartPublisher != null) { _smartPublisher.ImageFrameReceived -= OnImageFrameReceived; _smartPublisher.Stop(); _smartPublisher.Dispose(); _smartPublisher = null; } DisposeConnectionKeeper(); _disposed = true; } public void SetMute(bool isMute) { if (_smartPublisher != null) { _smartPublisher.SetMute(isMute); } } public void SwitchCamera() { if (_smartPublisher != null) { //TODOFelix } } } }