云游戏技术之高速截屏和GPU硬编码 (1) 捕获-预处理-编码流水线

在我们深入研究代码的细节之前,最重要的事情是理解整个应用程序的核心工作流程。

想象一下,你想实现一个屏幕录制软件。你面临的第一个问题是:"我如何将屏幕上看到的动态画面,变成一个可以播放的 .mp4 或 .h264 视频文件呢?"

这个过程可以分为三个主要步骤,就像一个工厂里的流水线:

  1. 捕获 (Capture):首先,你需要一种方法来"抓住"屏幕上显示的图像。
  2. 预处理 (Pre-process):抓到的原始图像数据可能不是最适合视频压缩的格式。你需要对其进行一些处理,比如转换颜色格式,使其"原料"更优质。
  3. 编码 (Encode):最后,你需要将处理好的图像一帧一帧地压缩,并按照特定的视频格式(如 H.264)打包,最终生成视频文件。

这三个步骤------"捕获-预处理-编码"------构成了我们这个项目的核心 流水线。它不是一个具体的 C++ 类,而是贯穿整个项目的核心思想。数据就像在传送带上一样,从一个环节流向下个环节,直到最终产品(视频文件)被生产出来。

我们可以用一个更生动的比喻来理解它:瓶装饮料生产线

生产线步骤 nvEncDXGI 流水线 作用
抓取空瓶子 捕获 使用 桌面复制接口 (DDAImpl) 从屏幕上获取原始图像帧。
灌装饮料 预处理 使用 色彩空间转换器 (RGBToNV12)将图像从 RGBA 格式转换为 NV12 格式,这是编码器最高效的格式。
封盖贴标 编码 使用 NVENC 硬件编码器封装 (NvEncoderD3D11) 将 NV12 图像帧压缩成 H.264 视频流。

理解这条流水线,你就掌握了理解 nvEncDXGI 项目运作方式的钥匙。

流水线概览

让我们用一张图来清晰地展示数据是如何在这条流水线中流动的:

graph LR A[屏幕实时画面] --> B(捕获); B -- 原始RGBA图像 --> C(预处理); C -- 优化的NV12图像 --> D(编码); D -- H.264视频数据 --> E[视频文件]; subgraph "第1步:由 DDAImpl 执行" B end subgraph "第2步:由 RGBToNV12 执行" C end subgraph "第3步:由 NvEncoderD3D11 执行" D end

代码中的流水线

理论讲完了,现在让我们看看这条流水线在实际代码中是如何体现的。打开 main.cpp 文件,找到 Grab60FPS 函数。这个函数内部有一个 do-while 循环,这个循环的每一次迭代,都完整地执行了一遍我们的流水线操作。

这个循环的核心逻辑可以简化为以下三个步骤:

1. 捕获一帧

cpp 复制代码
// 从 DDA 获取一帧画面
hr = Demo.Capture(wait);

这行代码启动了流水线的第一步。Demo.Capture() 会调用内部的 DDAImpl 对象,从你的显示器上捕获当前时刻的图像。如果成功,图像数据会存放在一个临时的显存区域。

2. 预处理该帧

cpp 复制代码
// 为编码做预处理
hr = Demo.Preproc(); 

紧接着,流水线进入第二步。Demo.Preproc() 会调用 RGBToNV12 对象,将上一步捕获到的 RGBA 格式图像转换为 NV12 格式。这一步是性能优化的关键,因为 NV12 格式更受硬件视频编码器的青睐。

3. 编码该帧

cpp 复制代码
// 编码处理后的帧
hr = Demo.Encode();

最后,流水线进入收尾阶段。Demo.Encode() 会将预处理好的 NV12 图像帧交给 NvEncoderD3D11 对象。后者利用 NVIDIA GPU 的硬件编码单元(NVENC)将其高效地压缩成 H.264 视频数据,并写入到最终的文件中。

就是这样!Capture -> Preproc -> Encode。这个简单的三步曲在循环中不断重复,直到录制到指定数量的帧,最终就构成了一个完整的视频。

内部工作流程

为了更深入地理解,我们来看一下当录制一帧画面时,各个组件之间是如何相互协作的。

sequenceDiagram participant 主循环 participant App as DemoApplication participant DDA as DDAImpl participant Preproc as RGBToNV12 participant Encoder as NvEncoderD3D11 主循环->>App: 请求处理下一帧 App->>DDA: Capture() Note right of DDA: 使用 DirectX 从屏幕抓取
一张 RGBA 格式的图像 DDA-->>App: 返回 RGBA 图像 App->>Preproc: Preproc() Note right of Preproc: 在 GPU 上执行
颜色空间转换 Preproc-->>App: 返回 NV12 格式的图像 App->>Encoder: Encode() Note right of Encoder: 调用 NVIDIA 硬件编码器
压缩 NV12 图像 Encoder-->>App: 返回编码后的 H.264 数据包 App->>App: 将数据包写入文件

这个图表清晰地展示了 DemoApplication 类(我们将在下一章详细介绍)作为总指挥,依次调用 DDAImplRGBToNV12NvEncoderD3D11 这三个专业组件,共同完成了一帧画面的录制任务。

总结

在本章中,我们学习了 nvEncDXGI 项目最核心的概念:捕获-预处理-编码流水线

  • 我们知道了屏幕录制可以分解为捕获预处理编码三个阶段。
  • 我们了解了每个阶段分别由哪个核心组件负责:DDAImplRGBToNV12NvEncoderD3D11
  • 我们通过分析 main.cpp 中的核心循环,看到了这个流水线在代码中的实际体现。
相关推荐
重启的码农7 小时前
云游戏技术之高速截屏和GPU硬编码 (2) 应用程序主控
c++·云计算·音视频开发
沐怡旸8 小时前
【C++基础知识】深入剖析C和C++在内存分配上的区别
c++
studytosky8 小时前
C语言数据结构之双向链表
c语言·数据结构·c++·算法·链表·c
沐怡旸8 小时前
【底层机制】malloc 在实现时为什么要对大小内存采取不同策略?
c++
HABuo9 小时前
【C++进阶篇】学习C++就看这篇--->多态超详解
c语言·开发语言·c++·后端·学习
1白天的黑夜19 小时前
哈希表-1.两数之和-力扣(LeetCode)
c++·leetcode·哈希表
哼?~10 小时前
list模拟实现
开发语言·c++
春花秋月夏海冬雪10 小时前
代码随想录刷题Day47
c++·平衡二叉树的构建·二叉树中序遍历·代码随想录刷题
七牛云行业应用10 小时前
私有化存储架构演进:从传统NAS到一体化数据平台
大数据·人工智能·架构·云计算·七牛云存储