使用DX获取桌面图像主要有如下5步:
1 获取适配器;
2获取输出;
3创建设备;
4创建复制输出;
5创建图像纹理
使用的组件,Opencv使用mat 进行分辨率转换压缩
using 指令集合
using System; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using OpenCvSharp; using SharpDX; using SharpDX.Direct3D11; using SharpDX.DXGI; using Device = SharpDX.Direct3D11.Device; using MapFlags = SharpDX.Direct3D11.MapFlags; using Size = System.Drawing.Size; using ResultCode = SharpDX.DXGI.ResultCode;
详情(注意使用DXGI只能输出RGBA像素格式,以及注意创建适配和释放适配器,对性能的要求TimeOut很重要可根据具体情况调试设置)
1 创建
/// <summary> /// 捕获图像大小,默认为捕获源大小,建议按原始比例设置 /// </summary> public Size CaptureSize { get; set; }/// <summary> /// 输出数据像素格式,默认为BGRA32 /// </summary> public PixelFormat PixelFormat { get; set; } = PixelFormat.Bgra32;
/// <summary> /// 捕获超时时间 /// </summary> public int TimeOut { get; set; } = 100;
/// <summary> /// 捕获源类型 /// </summary> public SourceType SourceType => SourceType.Screen;
/// <summary> /// 捕获源大小 /// </summary> public Size SourceSize { get; }
// 设备接口表示一个虚拟适配器 private readonly Device _mDevice;
// 复制输出设备 private readonly OutputDuplication _mDeskDupl;
//图像纹理 private readonly Texture2D _desktopImageTexture;
//屏幕大小 private Size _screenSize;
private CancellationTokenSource _cancellationTokenSource;
private bool _isManualCaptureStop; /// <summary> /// 复制指定监视器的输出 /// </summary> /// <param name="whichMonitor">要复制的输出设备(即监视器)。以零开始,应于主监视器.</param> public DxgiCapture(int whichMonitor = 0) : this(0, whichMonitor) { }
/// <summary> /// 在指定的显卡适配器上复制指定监视器的输出 /// </summary> /// <remarks>1获取适配器;2获取输出;3创建设备;4创建复制输出; 5创建图像纹理</remarks> /// <param name="whichGraphicsCardAdapter">显卡适配器</param> /// <param name="whichOutputDevice">要复制的输出设备(即监视器)。以零开始,应于主监视器.</param> public DxgiCapture(int whichGraphicsCardAdapter, int whichOutputDevice) { var primaryAdapter = GetPrimaryAdapter(whichGraphicsCardAdapter); Output primaryOutput = GetOutput(primaryAdapter, whichOutputDevice);
// 获取输出对象的描述信息 var mOutputDesc = primaryOutput.Description; _screenSize = new Size { Width = Math.Abs(mOutputDesc.DesktopBounds.Right - mOutputDesc.DesktopBounds.Left), Height = Math.Abs(mOutputDesc.DesktopBounds.Bottom - mOutputDesc.DesktopBounds.Top) }; SourceSize = _screenSize; CaptureSize = SourceSize; //创建Direct3D设备 _mDevice = CreateDevice(primaryAdapter); _mDeskDupl = CreateOutputDuplication(primaryOutput, _mDevice);
// 创建共享资源 _desktopImageTexture = CreateTexture2D(_mDevice, _screenSize); } /// <summary> /// 根据显卡编号获取适配器 /// </summary> /// <remarks>获取输出设备(显卡、显示器),这里是主显卡和主显示器</remarks> /// <param name="whichGraphicsCardAdapter"></param> /// <returns></returns> private Adapter1 GetPrimaryAdapter(int whichGraphicsCardAdapter) { try { var adapter = new Factory1().GetAdapter1(whichGraphicsCardAdapter); return adapter; } catch (SharpDXException) { throw new CaptureException("找不到指定的显卡适配器"); } }
/// <summary> /// 根据显示适配器和输出设备获取输出 /// </summary> /// <param name="adapter">显示适配器</param> /// <param name="whichOutputDevice">输出设备(监视器)</param> /// <returns></returns> private Output GetOutput(Adapter1 adapter, int whichOutputDevice) { try { return adapter.GetOutput(whichOutputDevice); } catch (SharpDXException) { throw new CaptureException("找不到指定的输出设备"); } }
/// <summary> /// 根据显卡适配器(视频卡)创建设备 /// </summary> /// <param name="adapter"></param> /// <returns></returns> private Device CreateDevice(Adapter1 adapter) { return new Device(adapter); //return new Device(DriverType.Hardware, DeviceCreationFlags.VideoSupport); }
/// <summary> /// 根据输出和设备创建输出Duplication /// </summary> /// <param name="output">输出</param> /// <param name="mDevice">设备</param> /// <returns></returns> private OutputDuplication CreateOutputDuplication(Output output, Device mDevice) { try { var mDeskDupl = output.QueryInterface<Output1>().DuplicateOutput(mDevice); if (mDeskDupl == null) { throw new CaptureException(" Creates a desktop duplication interface Error"); }
return mDeskDupl; } catch (SharpDXException ex) { if (ex.ResultCode.Code == ResultCode.NotCurrentlyAvailable.Result.Code) { throw new CaptureException("使用桌面复制API运行的应用程序已达到最大数量,请关闭其中一个应用程序,然后重试"); } throw new CaptureException(ex.ToString()); } }
/// <summary> /// 创建2D纹理 /// </summary> /// <remarks>共享资源</remarks> /// <param name="mDevice"></param> /// <param name="screenSize"></param> /// <returns></returns> private Texture2D CreateTexture2D(Device mDevice, Size screenSize) { return new Texture2D(mDevice, new Texture2DDescription { CpuAccessFlags = CpuAccessFlags.Read, BindFlags = BindFlags.None, Format = Format.B8G8R8A8_UNorm, Width = screenSize.Width, Height = screenSize.Height, OptionFlags = ResourceOptionFlags.None, MipLevels = 1, ArraySize = 1, SampleDescription = { Count = 1, Quality = 0 }, Usage = ResourceUsage.Staging }); }
2 获取
[HandleProcessCorruptedStateExceptions] private CaptureFrame CaptureFrames() { try { var width = _desktopImageTexture.Description.Width; var height = _desktopImageTexture.Description.Height; var data = PixelFormat switch { PixelFormat.Bgra32 => new byte[CaptureSize.Width * CaptureSize.Height * 4], PixelFormat.Bgr24 => new byte[CaptureSize.Width * CaptureSize.Height * 3], _ => throw new ArgumentOutOfRangeException(nameof(PixelFormat), PixelFormat, null) }; var result = _mDeskDupl.TryAcquireNextFrame(TimeOut, out _, out var desktopResource); if (result.Failure) return null; using var tempTexture = desktopResource?.QueryInterface<Texture2D>(); _mDevice.ImmediateContext.CopyResource(tempTexture, _desktopImageTexture); //拷贝图像纹理:GPU硬件加速的纹理复制 desktopResource?.Dispose(); var desktopSource = _mDevice.ImmediateContext.MapSubresource(_desktopImageTexture, 0, MapMode.Read, MapFlags.None); using var inputRgbaMat = new Mat(_screenSize.Height, _screenSize.Width, MatType.CV_8UC4, desktopSource.DataPointer); if (CaptureSize.Width != _screenSize.Width || CaptureSize.Height != _screenSize.Height) { var size = new OpenCvSharp.Size(CaptureSize.Width, CaptureSize.Height); Cv2.Resize(inputRgbaMat, inputRgbaMat, size, interpolation: InterpolationFlags.Linear); } if (PixelFormat == PixelFormat.Bgr24) { using var inputRgbMat = new Mat(); Cv2.CvtColor(inputRgbaMat, inputRgbMat, ColorConversionCodes.BGRA2BGR); Marshal.Copy(inputRgbMat.Data, data, 0, data.Length); } else { Marshal.Copy(inputRgbaMat.Data, data, 0, data.Length); } var captureFrame = new CaptureFrame(CaptureSize, PixelFormat, data); _mDevice.ImmediateContext.UnmapSubresource(_desktopImageTexture, 0); ReleaseFrame(_mDeskDupl); return captureFrame; } catch (AccessViolationException) { return null; } catch (Exception) { return null; } }
3 释放
/// <summary> /// 释放资源 /// </summary> public void Dispose() { _mDevice?.Dispose(); _mDeskDupl?.Dispose(); _desktopImageTexture?.Dispose(); } /// <summary> /// 释放帧 /// </summary> /// <param name="mDeskDupl"></param> private void ReleaseFrame(OutputDuplication mDeskDupl) { try { mDeskDupl.ReleaseFrame(); } catch (SharpDXException ex) { if (ex.ResultCode.Failure) throw new CaptureException("释放帧失败"); throw; } }
4 创建回调获取方式
/// <summary> /// 开始捕获 /// </summary> public void StartCapture(bool startMonitor = true) { _isManualCaptureStop = false; _cancellationTokenSource = new CancellationTokenSource(); if (!startMonitor) return; Task.Run(() => { try { while (!_cancellationTokenSource.IsCancellationRequested) { var captureFrame = CaptureFrames(); if (captureFrame != null) { FrameArrived?.Invoke(this, captureFrame); } } } catch (Exception) { //ignore } }, _cancellationTokenSource.Token); } /// <summary> /// 停止捕获 /// </summary> public void StopCapture() { _cancellationTokenSource?.Cancel(); Dispose(); _isManualCaptureStop = true; } /// <summary> /// 获取下一帧图像 /// </summary> /// <param name="captureFrame"></param> /// <returns></returns> public bool TryGetNextFrame(out CaptureFrame captureFrame) { captureFrame = null; if (_isManualCaptureStop) return false; _cancellationTokenSource?.Cancel(); captureFrame = CaptureFrames(); return captureFrame != null; } /// <summary> /// 新帧到达事件 /// </summary> public event EventHandler<CaptureFrame> FrameArrived;
标签:截图,return,private,DX,using,var,new,Net,public From: https://www.cnblogs.com/terryK/p/18585945