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

时间:2025-09-04 15:00:03来源:互联网

下面小编就为大家分享一篇云游戏技术之高速截屏和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. 捕获一帧

// 从 DDA 获取一帧画面
hr = Demo.Capture(wait);

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

2. 预处理该帧

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

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

3. 编码该帧

// 编码处理后的帧
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 从屏幕抓取<br/>一张 RGBA 格式的图像
    DDA-->>App: 返回 RGBA 图像
    App->>Preproc: Preproc()
    Note right of Preproc: 在 GPU 上执行<br/>颜色空间转换
    Preproc-->>App: 返回 NV12 格式的图像
    App->>Encoder: Encode()
    Note right of Encoder: 调用 NVIDIA 硬件编码器<br/>压缩 NV12 图像
    Encoder-->>App: 返回编码后的 H.264 数据包
    App->>App: 将数据包写入文件

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

总结

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

  • 我们知道了屏幕录制可以分解为捕获预处理编码三个阶段。
  • 我们了解了每个阶段分别由哪个核心组件负责:DDAImplRGBToNV12NvEncoderD3D11
  • 我们通过分析 main.cpp 中的核心循环,看到了这个流水线在代码中的实际体现。
本站部分内容转载自互联网,如果有网站内容侵犯了您的权益,可直接联系我们删除,感谢支持!