如何用Windows Graphics Capture API录制指定屏幕区域?
实现指定区域的屏幕录制(基于Windows Graphics Capture API)
核心思路
Windows Graphics Capture API本身不直接支持区域录制,但可以通过捕获完整的显示器/窗口画面后,在编码阶段裁剪指定区域实现需求。核心步骤为:
- 捕获目标区域所属的显示器/窗口的完整画面
- 在编码前对每一帧进行裁剪,仅保留指定的x、y坐标及宽高范围的内容
修改现有代码实现区域录制
1. 新增带区域参数的录制函数
修改原RecordScreen函数,添加区域参数,并将编码器的宽高指定为目标区域的尺寸:
private async void RecordScreen(GraphicsCaptureItem item, int captureX, int captureY, int captureWidth, int captureHeight) { _device = Direct3D11Helpers.CreateDevice(); uint frameRate = 30; uint bitrate = 3 * 1000000; // 使用指定区域的宽高作为编码器输出尺寸 var width = (uint)captureWidth; var height = (uint)captureHeight; try { newFile = GetTempFile(); using (var stream = new FileStream(newFile, FileMode.CreateNew).AsRandomAccessStream()) // 初始化编码器时传入裁剪区域参数 using (_encoder = new Encoder(_device, item, captureX, captureY, captureWidth, captureHeight)) { await _encoder.EncodeAsync( stream, width, height, bitrate, frameRate); } } catch (Exception ex) { // 建议添加异常日志,便于调试排查 // Debug.WriteLine($"录制异常:{ex.Message}"); } }
2. 修改Encoder类,添加帧裁剪逻辑
针对你参考的Encoder类,新增裁剪参数并在帧处理阶段添加Direct3D裁剪操作:
// 在Encoder类中新增私有变量存储区域参数 private readonly int _captureX; private readonly int _captureY; private readonly int _captureWidth; private readonly int _captureHeight; private readonly GraphicsCaptureItem _item; private readonly IDirect3DDevice _device; // 修改构造函数,接收区域参数 public Encoder(IDirect3DDevice device, GraphicsCaptureItem item, int captureX, int captureY, int captureWidth, int captureHeight) { _device = device; _item = item; _captureX = captureX; _captureY = captureY; _captureWidth = captureWidth; _captureHeight = captureHeight; } // 帧处理方法中添加裁剪逻辑 private async Task ProcessFrame(Direct3DSurface surface) { // 将捕获的画面转换为Direct3D11纹理 using var sourceTexture = Direct3D11Helpers.CreateSharpDXTexture(surface, _device); // 创建对应裁剪尺寸的目标纹理 var textureDesc = sourceTexture.Description; textureDesc.Width = _captureWidth; textureDesc.Height = _captureHeight; textureDesc.MipLevels = 1; using var croppedTexture = new Texture2D(_device.AsSharpDXDevice(), textureDesc); // 执行区域复制(裁剪) var context = _device.AsSharpDXDevice().ImmediateContext; var sourceRect = new Rectangle(_captureX, _captureY, _captureWidth, _captureHeight); context.CopySubresourceRegion(croppedTexture, 0, 0, 0, 0, sourceTexture, 0, sourceRect); // 将裁剪后的纹理传入编码器 await _mediaEncoder.EncodeFrameAsync(croppedTexture.AsWindowsRuntimeTexture()); }
3. 获取目标区域所属的显示器(针对全局坐标)
如果输入的是全局屏幕坐标,需先找到对应显示器的句柄,再创建捕获项:
// 获取指定全局坐标所属的显示器句柄 public static IntPtr GetMonitorFromGlobalPoint(int x, int y) { return MonitorFromPoint(new POINT { X = x, Y = y }, MONITOR_DEFAULTTONEAREST); } // 导入Windows API [DllImport("user32.dll")] private static extern IntPtr MonitorFromPoint(POINT pt, uint dwFlags); private const uint MONITOR_DEFAULTTONEAREST = 0x00000002; [StructLayout(LayoutKind.Sequential)] private struct POINT { public int X; public int Y; }
调用示例:
// 示例:录制全局坐标(100,100)处,宽800高600的区域 int captureX = 100; int captureY = 100; int captureWidth = 800; int captureHeight = 600; var monitorHandle = GetMonitorFromGlobalPoint(captureX, captureY); var captureItem = CreateItemForMonitor(monitorHandle); RecordScreen(captureItem, captureX, captureY, captureWidth, captureHeight);
注意事项
- 需确保指定的区域完全在捕获的显示器/窗口范围内,避免裁剪出错
- 若录制窗口内的相对区域,需将窗口内坐标转换为全局坐标,或捕获窗口后使用窗口内相对坐标裁剪
- 裁剪操作会增加性能开销,可根据硬件配置调整帧率和比特率优化体验
内容的提问来源于stack exchange,提问作者Riz
相关产品推荐
相关产品推荐

