ScreenRecorder 源码分析
简介
ScreenRecorder 是一个使用 Windows 原生 C++ API 开发的屏幕录制库,通过 C++/CLI 封装供 C# 调用。它提供了高性能、低延迟的屏幕录制能力,支持多种输入源和输出格式。
核心特性
- 支持将屏幕录制成视频、图片、长截图、gif
- 支持多源输入:窗口、显示器、视频、图片、GIF 等
- 支持 WGC (Windows Graphics Capture) 和 DXGI 两种捕获方式
- 提供 C# 友好的 API 接口
项目架构概览
┌─────────────────────────────────────────────────────────────┐
│ C# Application │
│ (ScreenRecorder.dll) │
└─────────────────────────┬───────────────────────────────────┘
│ C++/CLI 封装层
┌─────────────────────────▼───────────────────────────────────┐
│ Native C++ Core │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │RecordingMgr │ │ CaptureMgr │ │ OutputMgr │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ TextureMgr │ │ MouseMgr │ │ AudioMgr │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
│ Windows APIs
┌─────────────────────────▼───────────────────────────────────┐
│ WGC API │ DXGI Desktop Duplication │ MediaFoundation │
└─────────────────────────────────────────────────────────────┘
核心组件职责:
- RecordingManager:录制流程总控,协调各组件
- ScreenCaptureManager:屏幕捕获,支持多源叠加
- OutputManager:音视频编码输出
- TextureManager:DirectX 纹理处理
- MouseManager:鼠标指针渲染
- AudioManager:音频采集与同步
RecorderOptions 配置体系
RecorderOptions 是录制的核心配置类,控制所有录制参数。开始录制前需创建并设置 RecorderOptions。
cpp
property VideoEncoderOptions^ VideoEncoderOptions;
property SourceOptions^ SourceOptions;
property OutputOptions^ OutputOptions;
property AudioOptions^ AudioOptions;
property MouseOptions^ MouseOptions;
property OverLayOptions^ OverlayOptions;
property SnapshotOptions^ SnapshotOptions;
property LogOptions^ LogOptions;
VideoEncoderOptions - 视频编码参数
控制视频编码质量、帧率、码率等核心参数。
SourceOptions - 输入源参数
控制录制内容来源。支持显示器、窗口、相机、图片、视频、GIF 作为输入源,可添加多个输入源实现叠加效果。
常用属性/方法:
cpp
property List<RecordingSourceBase^>^ RecordingSources;
RecordingSources.Add() // 添加一个输入源
RecordingSources.Clear() // 移除所有输入源
注意 :新创建的 SourceOptions 中 RecordingSources 列表为空,需添加输入源后才能录制。也可直接使用
SourceOptions.MainMonitor录制主显示器。
OutputOptions - 输出控制
| 属性 | 说明 |
|---|---|
| SourceRect | 录制区域,为空录制整个区域,否则按指定大小裁剪 |
| IsVideoCaptureEnabled | 是否录制视频,false 时只录制音频 |
| IsVideoFramePreviewEnabled | 是否为每帧生成 Bitmap 预览 |
| VideoFramePreviewSize | 预览 Bitmap 的尺寸(像素) |
| StretchMode | 图像缩放模式:None/Fill/Uniform/UniformToFill |
| OutputFrameSize | 输出视频帧尺寸,可在 SourceRect 基础上缩放 |
| RecorderMode | 录制模式:Video/Slideshow/Screenshot |
AudioOptions - 音频参数
| 属性 | 说明 |
|---|---|
| IsAudioEnabled | 是否启用音频录制 |
| Bitrate | 音频比特率 |
| AudioChannels | 通道数量 |
| IsOutputDeviceEnabled | 是否录制输出音频(默认开启) |
| IsInputDeviceEnabled | 是否录制输入音频(默认关闭) |
| InputVolume / OutputVolume | 输入/输出音量(0.0~1.0) |
| ForceInputDeviceMono | 修复"伪立体声、单边有声"问题 |
MouseOptions - 鼠标显示
| 属性 | 说明 |
|---|---|
| IsMousePointerEnabled | 显示鼠标指针(默认开启) |
| IsMouseClicksDetected | 点击时显示彩色圆点提示 |
| MouseLeftClickDetectionColor | 左键提示颜色(默认黄色 #FFFF00) |
| MouseClickDetectionRadius | 提示圆点半径(默认 20 像素) |
| MouseClickDetectionDuration | 提示持续时间(默认 150ms) |
SnapshotOptions - 截图设置
| 属性 | 说明 |
|---|---|
| SnapshotFormat | 图片格式:PNG/JPEG/TIFF/BMP |
| SnapshotsWithVideo | 录制视频时同时保存截图 |
| SnapshotsIntervalMillis | 截图间隔(毫秒) |
| SnapshotsDirectory | 截图保存目录 |
RecordingSource 录制源
录制源定义"录什么",支持多种类型:
| 录制源 | 说明 |
|---|---|
| WindowRecordingSource | 窗口录制 |
| DisplayRecordingSource | 显示器录制(支持 WGC/DXGI) |
| VideoCaptureRecordingSource | 相机录制 |
| VideoRecordingSource | 视频文件录制 |
| ImageRecordingSource | 图片文件录制 |
| RecordableCamera | 相机录制源(继承自 VideoCaptureRecordingSource) |
| RecordableWindow | 窗口录制源(继承自 WindowRecordingSource) |
| RecordableDisplay | 显示器录制源(继承自 DisplayRecordingSource) |
核心流程分析
录制入口
cpp
RecordingManager::BeginRecording(_In_opt_ std::wstring path, _In_opt_ IStream *stream)
流程步骤
- 检查状态:验证是否正在录制
- 检查依赖:确认系统支持录制 API
- 设置输出路径 :
ConfigureOutputDir(path) - 启动录制任务:创建异步任务执行录制
- 初始化各管理器 :
- TextureManager:纹理处理
- OutputManager:编码输出
- ScreenCaptureManager:屏幕捕获
- MouseManager:鼠标渲染
- 启动录制循环 :
StartRecorderLoop() - 启动屏幕捕获 :
m_CaptureManager->StartCapture()
录制循环核心逻辑
while (true) {
1. 处理错误事件
2. 暂停处理
3. 捕获一帧:AcquireNextFrame()
4. 前处理:Overlay叠加 → 鼠标渲染 → 裁剪/缩放
5. 写入输出:OutputManager->RenderFrame()
}
多源叠加原理
ScreenRecorderLib 支持多输入源叠加,实现画中画、水印等效果。
叠加流程
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Source 1 │ │ Source 2 │ │ Overlay │
│ (主显示器) │ │ (摄像头) │ │ (Logo) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────┐
│ TextureManager (GPU合成) │
│ 按位置/层级将纹理绘制到共享画布 │
└──────────────────────────┬───────────────────────┘
│
▼
┌───────────────┐
│ 最终合成纹理 │
└───────────────┘
关键代码:
cpp
// 处理叠加层
m_CaptureManager->ProcessOverlays(pTexture, &updatedOverlaysCount);
// 纹理变换:裁剪 → 缩放 → 居中填充
ProcessTextureTransforms(pTexture, &processedTexture,
videoInputFrameRect, videoOutputFrameSize);
每个 Source/Overlay 可独立配置位置、大小、拉伸模式,由 TextureManager 统一合成。
音视频同步机制
采用外部时钟源 + 音频时间修正策略,本质是视频同步音频。
核心算法
1. 获取媒体时钟时间戳
2. 计算理论视频帧时长 = 当前时间 - 上帧开始时间
3. 抓取对应时长的音频数据
4. 根据实际音频长度计算时间偏差
5. 修正视频帧时间戳
关键代码:
cpp
// 视频帧时长 ≈ 音频长度
model.Duration = duration100Nanos + diff;
// PTS 向音频时间轴对齐
model.StartPos = lastFrameStartPos100Nanos + totalDiff;
时间轴设计
- 基准时间轴 :
lastFrameStartPos100Nanos严格按帧率递增 - 修正时间轴 :
totalDiff累计音频误差
通过这种方式,即使音频采样存在微小偏差,视频也能保持与音频同步。
图片保存与 WIC
WIC (Windows Imaging Component)
WIC 是 Windows 提供的图像编解码系统组件,支持多种格式的读写、转换、缩放。
IStream 抽象
cpp
// 继承关系
IUnknown → IStream → IWICStream
// 核心方法
Read() // 读字节
Write() // 写字节
Seek() // 定位
Stat() // 获取流大小
IStream 提供统一的流抽象,支持文件、内存、网络等数据源,图片编码器将数据写入 IStream,便于实现 GIF、滚动截图等功能。
C++/CLI 封装设计
设计目标
- 性能:核心逻辑使用原生 C++,避免托管开销
- 易用性:提供 C# 友好的 API
- 跨语言:支持 C#、VB.NET 等 .NET 语言调用
封装结构
cpp
// C++/CLI 托管类
public ref class RecorderOptions {
property VideoEncoderOptions^ VideoEncoderOptions;
property SourceOptions^ SourceOptions;
// ...
};
// 调用原生 C++ 实现
class RecordingManager {
// 原生实现
};
类型映射
| C++ 类型 | C++/CLI 托管类型 |
|---|---|
std::wstring |
System::String^ |
std::vector |
System::Collections::Generic::List^ |
| 原生指针 | IntPtr 或托管包装类 |
性能调优建议
1. 选择合适的捕获 API
| API | 适用场景 | 特点 |
|---|---|---|
| WGC | Windows 10 1903+ | 低延迟,支持 UWP 窗口 |
| DXGI | 全兼容 | 高性能,需 Vista+ |
csharp
// 推荐:让库自动选择
var options = new SourceOptions();
// 库会根据系统版本自动选择最优 API
2. 合理设置帧率和分辨率
csharp
// 演示录制:15-24 fps 足够
options.VideoEncoderOptions.Framerate = 24;
// 游戏录制:30-60 fps
options.VideoEncoderOptions.Framerate = 30;
// 控制输出分辨率,减少编码压力
options.OutputOptions.OutputFrameSize = new Size(1280, 720);
3. 码率与编码器选择
csharp
// H.264 编码,质量优先
options.VideoEncoderOptions.IsFixedFramerate = true;
options.VideoEncoderOptions.BitrateMode = BitrateControlMode.Quality;
// 网络传输场景,码率优先
options.VideoEncoderOptions.BitrateMode = BitrateControlMode.UnconstrainedVBR;
options.VideoEncoderOptions.Bitrate = 2_000_000; // 2 Mbps
4. 避免不必要的预览
csharp
// 仅在需要实时预览时启用
options.OutputOptions.IsVideoFramePreviewEnabled = false;
// 如果需要预览,缩小预览尺寸
options.OutputOptions.VideoFramePreviewSize = new Size(320, 180);
5. 纹理复用
底层使用 TextureManager 复用 D3D11 纹理,避免频繁分配。对于长时间录制,确保:
- 不要频繁修改 OutputFrameSize
- 避免动态添加/移除大量 Overlay
常见问题
Q: 录屏出现绿色区域
现象:指定区域录制时,出现绿色空白区域。
原因:DPI Awareness 问题。Windows 对 DPI-unaware 进程进行虚拟化,导致获取的分辨率不正确。
解决方法 :在项目中添加 app.manifest:
xml
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
PerMonitorV2
</dpiAwareness>
</windowsSettings>
</application>
总结
ScreenRecorder 是一个设计精良的录屏库,其核心优势在于:
- 多源叠加:灵活的输入源组合,支持复杂场景
- 高性能架构:DirectX 11 + Media Foundation,充分利用 GPU
- C++/CLI 封装:兼顾性能与易用性
- 音视频同步:精确的时间轴控制