要点综述
通常,在 Windows* OS,使用 Direct3D 进行视频处理。 但是,也有许多应用使用 OpenGL*,因为它具备出色的跨平台能力,能够在不同的平台上提供相同的 GUI 和外观与体验。 最新的英特尔图形驱动程序支持使用 NV_DX_interop 从 D3D 向 OpenGL 曲面共享,并可与英特尔® 媒体软件开发套件结合使用。 英特尔® 媒体软件开发套件可配置使用 Direct3D 和加入 NV_DX_interop,OpenGL 可使用英特尔媒体软件开发套件的帧缓冲区,而不用执行将纹理从 GPU 复制至 CPU 再复制回 GPU 这一昂贵过程。 本示例代码和白皮书演示了如何设置英特尔® 媒体软件开发套件以使用 D3D 进行编码和解码,从 NV12 色域(媒体软件开发套件的自然颜色格式)向 RGBA 色域(OpenGL 的自然颜色格式)进行颜色转换,然后将 D3D 曲面应设置 OpenGL 纹理。 本管线完全绕过将纹理从 GPU 复制至 CPU 这一过程,该过程曾经是配合英特尔® 媒体软件开发套件使用 OpenGL 时的最大瓶颈。
系统要求
本示例代码使用 Visual Studio* 2013 编写,旨在 (1) 展示 Miracast 和 (2) 利用英特尔® 媒体软件开发套件实现英特尔® 媒体软件开发套件/ OpenGL 纹理共享,其中不用进行任何复制即可与 OpenGL 纹理分享解码曲面,这能够显著地提升效率。 MJPEG 解码器面向 Haswell 和更高版本的处理器执行了硬件加速,较低版本的处理器中的媒体软件开发套件可自动使用软件解码器。 在任何情况下,它都需要与 MJPEG 兼容的摄像头(无论是板载还是 USB 摄像头)。
除了辨认 Miracast 连接类型以外,该示例代码和白皮书中使用的大部分技术都适用于 Visual Studio 2012。 该示例代码基于 Intel Media SDK 2014 for Client,可通过以下链接进行下载:(https://software.intel.com/sites/default/files/MediaSDK2014Clients.zip。) 安装媒体软件开发套件后,将会为 Visual Studio 创建一套环境变量,以便为头文件和库查找正确的路径。
应用概述
该应用将摄像头作为 MJPEG 输入,并对 MJPEG 视频解码,将视频流编码至 H264,然后再对 H264 进行解码。 MJPEG 摄像头视频流(解码后)和最终以 H264 标准解码的视频流将会在基于 MFC 的 GUI 上显示。 在 Haswell 系统上,2 个解码器和 1 个编码器(1080P 分辨率)将会按顺序运行以提供出色的可读性,但是由于硬件加速,它们的速度非常快,这使得摄像头速度成为 fps 的唯一限制因素。 在实际情况下,编码器和解码器将会在不同的线程中运行,性能不会成为障碍。
在单个显示器配置上,摄像头馈入将会在基于 OpenGL 的 GUI 中以 H264 标准解码的视频上方的 PIP 中显示(图 1)。 当 Miracast 连接时,软件将会自动辨认与 Miracast 相连的显示器,并使用全屏窗口播放以 H264 标准解码的视频,而 GUI 将显示原始摄像头视频,因此原始视频和加密视频之间的区别便清晰可见。 最后,查看->监控拓扑(View->Monitor Topology)菜单不仅能够检测到当前的显示器拓扑,还能够更改拓扑。 但是很可惜,它无法启动 Miracast 连接。 它只能在 OS charm 菜单上完成(从右侧滑入-> 设备->项目),目前尚且没有能够创建 Miracast 连接的 API 。 有趣的是,通过将显示器拓扑设置为仅限内部使用可以将 Miracast 连接断开。 如果通过线路连接了多台显示器,菜单可以随时更改拓扑。
图 1 单个显示器拓扑。 MJPEG 摄像头在右下角显示。 以 H264 标准加密的视频在 GUI 中播放。 当启用多台显示器(如 Miracast)时,该软件可检测到变化,MJPEG 摄像头视频和以 H264 加密的视频将会自动分至不同的显示器。
管线设置的主要接入点
本示例代码基于 MFC,设置管线的主要接入点是 CChildView::OnCreate (),它能够启动摄像头,从 MJPEG 转码至 H264,使用 H264 解码,并将转码器和解码器上的纹理与 OpenGL 渲染器绑定。 转码器仅为在基础解码器上添加编码器的解码器子类。 最后,OnCreate 可启动持续获取序列化摄像头馈入的线程。 在工作线程中读取摄像头馈入后,它可向 OnCamRead 函数发送消息,该函数可对 MJPEG 进行解码,编码至 H264,解码 H264,并将纹理更新至 OpenGL 渲染器。 从最高层面而言,整个管线整洁、简单,易于执行。
启动解码器/转码器
解码器和转码器都是使用 D3D9Ex 进行启动。 我们可对英特尔® 媒体软件开发套件进行配置以使用软件、D3D9 或 D3D11。 在本示例中,D3D9 用于简化颜色转换。 英特尔® 媒体软件开发套件的自然颜色格式是 NV12;IDirect3DDevice9::StretchRect 和 IDirectXVideoProcessor::VideoProcessBlt 都可用来将色域转换至 RGBA。 简单起见,本白皮书使用的是 StretchRect,但是一般情况下,我们推荐使用 VideoProcessBlt,因为它在后期处理过程中有出色的附加功能。 可惜的是,D3D11 不支持 StretchRect,因此颜色转换可能非常复杂。 此外,本文中的解码器和转码器使用了独立的 D3D 设备进行各种实验,如混合软件和硬件,但是 D3D 设备可以在二者之间进行共享以节约内存。 以该方式设置管线后,解码输出将设置为 (mfxFrameSurface1 *) 类型。 这仅为面向 D3D9 的封装,mfxFrameSurface1-> Data.MemId 可换算为 (IDirect3DSurface9 *),并在解码后用于 CDecodeD3d9::ColorConvert 函数中的 StretchRect 或 VideoProcessBlt。 媒体软件开发套件的输出曲面不可共享,但是必须要进行颜色转换才能供 OpenGL 使用,并且需要创建一个共享曲面存储颜色转换的结果。
启动转码器
转码器的解码结果将会直接输入编码器,并确保在分配曲面时使用该 MFX_MEMTYPE_FROM_DECODE。
绑定 D3D 和 OpenGL 之间的纹理
绑定纹理的代码位于 CRenderOpenGL::BindTexture 函数中。 请确保对 WGLEW_NV_DX_interop 进行了定义,然后使用 wglDxOpenDeviceNV、wglDXSetResourceShareHandleNV 和 wglDXRegisterObjectNV。 这将会把 D3D 曲面绑定至 OpenGL 纹理。 但是,它无法自动更新纹理,而且调用 wglDXLockObjectsNV / wglDXUnlockObjectsNV 将会更新纹理(CRenderOpenGL::UpdateCamTexture 和 CRenderOpenGL::UpdateDecoderTexture)。 对纹理进行更新后,便可像使用 OpenGL 中的其他纹理一样使用它。
多显示器拓扑变更注意事项
理论上,在外置显示器中提供另一个窗口并根据拓扑变化检测对其进行控制似乎相当简单。 但是实际情况下,从操作系统开始转换,到显示器配置完成,以及内容显示都需要时间。 当结合使用编码器 / 解码器 / D3D / OpenGL 及其优势时,调试将会非常复杂。 本示例尝试重新使用切换过程中的大部分管线,但是实际上,关闭整个管线并重新启动将会更简单,因为添加显示器需要 10 多秒钟的时间,在这期间可能会出现任何故障 — 甚至 HDMI 或 VGA 连接故障。
未来展望
本白皮书中的示例代码面向 D3D9,且不可为 D3D11 实施提供支持。 目前我们尚不清楚,在没有 StretchRect 或 VideoProcessBlt 时,哪种方式能够最有效地将色域从 NV12 转换至 RGBA。 D3D11 实施退出后,白皮书/代码将进行更新。
贡献
感谢 Petter Larsson、Michel Jeronimo、Thomas Eaton 和 Piotr Bialecki 对本文的贡献。
英特尔、Intel 标识、Xeon 和至强是英特尔在美国和其他国家的商标。
* 其他的名称和品牌可能是其他所有者的资产
英特尔公司© 2013 年版权所有。 所有权保留。