using Lennox.LibYuvSharp; using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Vinno.vCloud.FIS.CrossPlatform.Common; using Vinno.vCloud.FIS.CrossPlatform.Common.Enum; using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo; using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo.Interface; namespace Vinno.vCloud.FIS.CrossPlatform.Windows.LiveVideo { internal class TerminalImageCapturerV2 : ICapturer { private readonly object _lock = new object(); private readonly string _id; private readonly int _width; private readonly int _height; private bool _isCapturing; private int loggerCount = 0; private CPVideoFrameData _videoFrameData; private ImageFrameData _imageFrameData; private byte[] _screenTempBuffer; private byte[] _screenBuffer; private EnumImageType _imageType; private int _lastSendDataTime; private int _lastCaptureTime; private readonly ManualResetEvent _handlingImageEvent = new ManualResetEvent(true); public string Id => _id; public int OutputWidth => _width; public int OutputHeight => _height; public bool IsCapturing => _isCapturing; public event EventHandler ImageFrameReceived; public event EventHandler VideoFrameDataReceived; public TerminalImageCapturerV2(string id, int terminalWidth, int terminalHeight, EnumImageType imageType) { _id = id; _imageType = imageType; _width = terminalWidth; _height = terminalHeight; switch (_imageType) { case EnumImageType.ImageFrameData: _imageFrameData = new ImageFrameData(OutputWidth, OutputHeight); break; case EnumImageType.CPVideoFrameData: _videoFrameData = new CPVideoFrameData(OutputWidth, OutputHeight, null); break; } _imageFrameData = new ImageFrameData(terminalWidth, terminalHeight); CrossPlatformHelper.Instance.LogWriter?.WriteLineInfo($"TerminalImageCaptureV2 Resolution:{terminalWidth} x {terminalHeight} "); } public void StartCapture() { if (_isCapturing) { CrossPlatformHelper.Instance.LogWriter?.WriteLineWarn($"TerminalImageCaptureV2 Has Been In Capturing!"); return; } _isCapturing = true; ImageDataCapturer.ImdageDataCaptured += OnImageDataCaptured; Task.Run(() => { while (_isCapturing) { try { _handlingImageEvent.Reset(); if (Math.Abs(_lastSendDataTime - DateTime.Now.Millisecond) <= 40) { continue; } else { _lastSendDataTime = DateTime.Now.Millisecond; } switch (_imageType) { case EnumImageType.ImageFrameData: lock (_lock) { if (_screenTempBuffer == null) { Thread.Sleep(10); continue; } Marshal.Copy(_screenTempBuffer, 0, _imageFrameData.Data, _screenTempBuffer.Length); } ImageFrameReceived?.Invoke(this, _imageFrameData); Thread.Sleep(20); break; case EnumImageType.CPVideoFrameData: lock (_lock) { if (_screenTempBuffer == null) { Thread.Sleep(10); continue; } ResizeBuffer(ref _screenBuffer, _screenTempBuffer.Length); Buffer.BlockCopy(_screenTempBuffer, 0, _screenBuffer, 0, _screenTempBuffer.Length); } _videoFrameData.Data = _screenBuffer; VideoFrameDataReceived?.Invoke(this, _videoFrameData); Thread.Sleep(20); break; } } finally { _handlingImageEvent.Set(); Thread.Sleep(10); } } }); CrossPlatformHelper.Instance.LogWriter?.WriteLineInfo($"TerminalImageCaptureV2:{Id} StartCapture"); } private void ResizeBuffer(ref byte[] buffer, int size) { if (buffer == null) { buffer = new byte[size]; } else if (buffer.Length != size) { Array.Resize(ref buffer, size); } } private void OnImageDataCaptured(object sender, CaptureDataInfo e) { try { if (Math.Abs(_lastCaptureTime - DateTime.Now.Millisecond) < 30) { return; } _lastCaptureTime = DateTime.Now.Millisecond; var width = e.Width; var height = e.Height; var dataSize = e.Width * e.Height * 4; if (loggerCount < 1) { CrossPlatformHelper.Instance.LogWriter?.WriteLineInfo($"TerminalImageCaptureV2: Capture main window:{width}*{height}, Resize :{OutputWidth}*{OutputHeight}"); loggerCount++; } if (width == OutputWidth && height == OutputHeight) { lock (_lock) { ResizeBuffer(ref _screenTempBuffer, dataSize); Buffer.BlockCopy(e.Data, 0, _screenTempBuffer, 0, dataSize); } } else { lock (_lock) { ResizeBuffer(ref _screenTempBuffer, OutputWidth * OutputHeight * 4); unsafe { fixed (byte* captureData = e.Data) { fixed (byte* tempData = _screenTempBuffer) { LibYuv.ARGBScale(captureData, e.Width * 4, e.Width, e.Height, tempData, OutputWidth * 4, OutputWidth, OutputHeight, FilterMode.None); } } } } } } catch (Exception ex) { CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"TerminalImageCaptureV2 OnImageDataCaptured Error:{ex}"); } } public void StopCapture() { try { if (_isCapturing) { _isCapturing = false; ImageDataCapturer.ImdageDataCaptured -= OnImageDataCaptured; CrossPlatformHelper.Instance.LogWriter?.WriteLineInfo($"TerminalImageCaptureV2:{Id} StopCapture"); } } catch (Exception e) { CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"TerminalImageCaptureV2 Stop Capture Error:{e}"); } } public void Dispose() { StopCapture(); _handlingImageEvent.WaitOne(); _imageFrameData?.Dispose(); } } }