关于视频编码:基于-SPICE-协议的硬编推流整合方案在云游戏中的应用

52次阅读

共计 6774 个字符,预计需要花费 17 分钟才能阅读完成。

背景

随着虚拟化技术如模拟器,容器化等技术等倒退,在安卓云游戏 / 云手机场景中,能够在服务宿主侧虚构出更多更小颗粒度的 Android 实例。其中比拟外围的技术是图形虚拟化技术,如何最大限度利用宿主侧的 GPU 资源进行渲染和编码,不思考软编等利用 CPU 资源进行渲染编码是因为效率带来的提早问题。

Linux 图形栈

先看一个比拟通用的 linux 图形栈:

  • X 协定:比拟早的协定,X server 间接治理 GPU 内的 framebuffer 和 X Client 提交命令,通过 XClient(Xlib 或 XCB)向 Xserver(Xorg)提交相干命令实现,且有很多扩大协定,然而弊病须要一个额定的 Windows Manager 来解决多个利用。目前曾经被 Wayland 这种扩大协定取代,composer 解决输出,窗口,合成显示等性能。
  • GLX:因为是用来做间接渲染,做了两个工作:1)将 OpenGL 和 X window API 绑定 2)通过 X server 转发 GL 的调用。实质还是 X 协定那一套。
  • FB driver:历史遗留显示子系统,提供了 Framebuffer 获取,图像操作原语,电源治理等性能。
  • OpenGL:对立的 3D 图形渲染 API 接口,各支流厂商(Intel、Nvidia、AMD、Qualcomm 等)都反对的接口,支流实现的是开源的 mesa。Mesa 3D 是其最支流的开源实现,值得注意的是 Mesa 不仅反对 OpenGL,还反对 Vulkan, Direct 3D 等渲染 API。
  • DRM:Direct Rendering Manager, 目前支流的 GPU 显示子系统,用户态应用 libDRM 的 DRM API 来操作 DRM 设施,对 GPU 通过 ioctl 等标准文件操作来通信,实现:

    • framebuffer 治理。
    • 用户态形象渲染能力:如 Buffer Object 治理,GPU 作业命令提交等,个别和具体 driver 相干。
    • Virtual Driver 反对:蕴含 vmwgfx(VMware 桥接设备)和 virgl(Virto 桥接设备)
    • Prime Zero-copy memory,buffer 通过用户态的 fd 文件描述符代表了理论显存中的 DMA buffer,通过 Prime API 导出 FD,能够在 IPC 之间传递。

蕴含 Wayland 比拟支流的所有图像栈变得异样简单:

每种利用的图像数据流都比较复杂,但大抵流线是:利用(显示 Client)->(显示 Server)->OpenGL/EGL->Mesa 3D->libDRM->(内核)DRM->GPU 驱动。

Android 图形栈

以 SurfaceFlinger 为外围,保护了所有 app 窗口的交叠笼罩关系:

  • OpenGL ES:2D/3D 渲染走的门路,应用 drm 所有性能进行绘制渲染。
  • Gralloc FB: 应用 drm 为 app 提供显存治理等性能。
  • HWComposer: 调用 openGL 窗口合成 RGB 或者 YUV,实现屏幕绘制。

综合 Linux 图形栈和 Android 图形栈能够发现在底层都是基于 drm 实现,实现硬编计划的核心思想就是渲染和编码都利用宿主侧的 GPU,并且渲染和编码 Zero-copy,所以有两类技术:

  1. virto-gpu 技术将 OpenGLES 命令导出(virgl)之后再反过来调用宿主侧的 virglrenderer,又将其翻译回 OpenGL 和 GLSL,而后再调用宿主的 OpenGL,这部分技术代表是 Qemu 计划。应用假的形象 GPU。在形象层 GPU 层进行拦挡,并调用宿主侧的 GPU。
  1. 间接导出 DRM 句柄,利用 DRM 的 Zero-copy 的个性进行渲染和编码,渲染和编码通过 IPC 技术传递 fd, 这部分代表是 AiC(Android in Container)容器化技术。

以上两类技术因为最终都是 drm 图像 buffer,故都能够通过 IPC 技术在渲染过程和编码过程之间通过 IPC 传递。渲染过程个别在容器 / 模拟器内,编码过程个别在容器外。

多媒体编解码相干

以上图形栈波及显示和渲染,在云游戏的场景中,还需思考编码设计的技术栈。就编码而言:

  • 在 Linux 中 ffmepg 或者 gstreamer 等规范多媒体框架对上封装了利用接口,对下对接了硬件提供编解码如 CUDA NVEnc 接口或 VAAPI 接口。
  • 在 Android 中应用 OMX 作为其多媒体框架,MediaCodec 驱动对接 vendor 驱动来实现硬编解码能力。

如果想要在 Android 内应用硬件编解码,要么实现一套 OMX 到 ffmpeg 的转换翻译,要么厂商对接实现 OMX 的 vendor 驱动,否则很难在 Android(容器或模拟器中)内硬件编码。比拟正当的形式是导出 libdrm 的 FD,渲染和编码在不同的过程中,编码抉择在 host 中调用 ffmepg 或者 vender 的编码 API 进行编码,进而实现整个推流。

计划

硬编目前精力放在解决过程间传递图像 prime FD,还有相应的音频,双向 input 通信等。采纳对立的 Spice 协定或者革新的 Spice 协定对立 Android 虚拟化和容器化整合计划。

spice 协定

SPICE,Simple Protocol for Independent Computing Environment,是 Redhat 公司开源的一套远程桌面虚拟化协定,旨在提供商业级别的远程桌面体验。Spice 协定具备如下长处:

  1. 开源:易于扩大和性能定制;
  2. 跨平台:Windows/Linux/Mac OS 平台全兼容;
  3. 反对外接设备:除罕用 USB 设施外,打印机和扫描仪等设施也能在近程应用;
  4. 丰盛的媒体反对:包含视频、音频、图像;
  5. 更小的带宽占用:Spice 里内置图像压缩算法,无效缩小数据传输时的带宽占用;
  6. 更平安的数据传输:Spice 能够应用 OpenSSL 加密传输数据。

概述

蕴含四个局部:协定、客户端侧、服务端侧和虚拟机侧。其中:

  1. 协定:是客户端侧、服务端侧和虚拟机侧三个局部交互时所遵循的准则;
  2. 客户端:负责接管并转换虚拟机数据,以及发送用户输出数据到虚拟机,从而使得用户可能与虚拟机进行交互;
  3. 服务端:集成在 Hypervisor 外部的一个用户层组件,使得 Hypervisor(如 QEMU)反对 Spice 协定;
  4. 虚拟机侧:指所有部署在虚拟机外部的必须组件,如 QXL 驱动、Spice Agent 等。

图像流

上图示意了整个图像从 Guest OS 到客户端图像传输通路,其中:

  • QEMU: 虚拟机环境,目前应用
  • Guest OS: 运行在虚机中的操作系统
  • Client OS: 运行在 host 侧的应用程序
  • GDI/X:Graphics Device Interface,图像引擎,图像栈提供的显示接口(如 mesa)
  • QXL:设施驱动,提供了套动静 设施须要客户机的 QXL 驱动来施展全副作用。然而,当没有驱动的时候,规范的 VGA 也能反对该设施。这个模式还能显示虚拟机启动的疏导阶段。QXL 设施通过命令和指针环,显示中断,指针事件,I/O 端口来与驱动交互。

QXL 设施的其余性能包含:

  • 初始化和映射设施 ROM,RAM 和 VRAM 到物理内存
  • 映射 I/O 端口,解决读写来治理:区域更新,命令,指针告诉,IRQ 更新,模式设置,设施重置,记录日志等。
  • 环 - 初始化和保护命令和指针环,从环获取命令和指针命令,期待告诉。保护资源环。
  • 应用 QXLWorker 接口与相应的 red worker 通信,这是在 red dispatcher 中实现的,它把设施调用翻译为音讯写到 red worker 通道,或者从 red worker 通道中读取音讯。
  • 注册 QXL 接口来使 worker 能与设施通信。这个接口包含 PCI 信息和性能(如附丽一个 worker,从环中获取显示和指针命令,显示和指针告诉,模式扭转告诉等)。
  • 定义反对 QXL 模式和扭转以后模式(如 VGA:所有监听器反映一个繁多设施)
  • 解决在 VGA 模式中显示的初始化,更新,扭转尺寸和刷新。

VDagent 命令流

Spice Server

Spice 协定革新

Spice Client 收到 Spice Server 端发过来的 main,display,playback 等通道后:

  1. 显示通道在默认的 display 上调用 GTK widget 相干组件将图像画在屏幕上。
  2. 获取 playback 音频数据,通过 gstreamer 的 pipeline,调用 alsa 播放音频。
  3. 其余鼠标键盘等解决,不作任何解决。

为适宜咱们的推流革新如下:

对原协定改变比拟大的:

  1. Display Channel 通道,这部分在获取到 FD 之后,本来画在 GTK 的流程通过 HwFrame 适配模块,转换成 RTC 编码所需的数据源(YUV 或者 RGBA)。
  2. Main Channel 通道减少 VDAgent 类型减少自定义类型传输 RTC 近程调用指令,反向通过封装将 RTC 的事件和 DataChannel 传递给 GameService(游戏治理服务过程)。

Spice 协定抓包

能够通过 socat 等工具代理 domain socket 来剖析 spice 协定,对一个残缺的 Spice 协定交互流程,通过 TCP dump 抓取 wireshark 日志如下:

先通过 main channel 建设连贯,认证,而后顺次建设 Spice Display, Spice PLAYBACK, SPICE RECORD, SPICE INPUT 等通道,最初通过各通道发送特定的音讯。

这里重点关注以下:

  1. Main Channel 的 VDAgent 通道,在 CLIPBOARD 和 FILE_XFER 之外增加 VD_AGENT_VENDOR_DATA,为近程 gRPC 调用,接管 android 侧的封装数据。
  1. Display Channel

    • GL_SCANOUT_UNIX, 屏幕初始化 / 改分辨率后的音讯,个别用在初始化的时候。
  • GL_DRAW_DONE, 当屏幕内容有变动的时候传递此音讯,能够认为是每一幅安卓画面。
  1. Playback Channel,android 零碎的声音音讯,如音量变动,声音开始与进行等。

​编辑

QEMU 计划

QEMU 计划能够间接复用社区的 qemu+kvm 计划,除了针对不同硬件导出不同的 fd 之外。

AiC 容器

相比于虚拟化,容器化的非凡之处在于 qemu 曾经集成 Spice Server 组件,虚拟化的容器须要容器外编码的话须要导出音频,视频和管制通道,而后实现一套相似 Spice 协定的架构,为对立兼容性通过减少下图的转发模块 Adapter/SpiceServer,将 IPC 通道再次转发至 Spice 通道,实现 QEMU/AiC 计划的对立。

计划参数

在整个整合计划中,有如下因素和参数需思考:

  1. 模拟器或者容器环境辨别。如果导出的 fd 在模拟器和容器计划统一,不须要辨别,否则须要通过环境变量或者传入启动参数来辨别。
  2. DRM device 指定。在携带 GTK 的版本中须要指定 Display 的 device,移除 Xorg 依赖后需指定 Render node。
  3. 编码显卡硬件指定,因为不同硬件编码不同,在编码模块须要通过以后硬件信息来确定编码方式。

图形导出

从虚机或者容器导出,有两种类型的图形显存的 fd:

KHR_STREAM

渲染到宿主侧的 surface,suface 导出 EGLSTREAM,通过 eglCreateStreamKHR 和 eglGetStreamFileDescriptorKHR 导出对应的 EGLStreamKHR 文件描述符,实用于 NVIDIA 显卡。生产侧通过 eglStreamConsumerAcquireKHR 导出对应的 stream,但编码不能间接应用 stream 类型,CUDA 提供了 OpenGL 与 CUDA 互操作 API,将 texture 或者 render buffer 绑定 CUDA 资源之后,CuGraphicsSubResourceGetMappedArray 映射出 CUarray 指针供编码器应用。

MESA_IMAGE

渲染到宿主侧的 texture,texture 导出为 DMA buffer,通过 eglExportDMABUFImageQueryMESA,eglExportDMABUFImageMESA 导出,实用于 AMD/Intel 显卡。生产侧通过此创立 EGLImage, 并绑定 2D 纹理,将此纹理的 textureID 传递给编码器 VAAPI 通过此编码器进行编码。

优化与演进

代码重构

随着撑持的计划类型减少,整个工程在满足性能状况下逐步难以保护,通过 C++ 重写各个模块,将 HwFrame 模块形象,对日志 /SRE 各模块划分重构,将工程模块化。

移除 Xorg

Xorg 作为 X(11) 协定中的 Server 的实现,Spice Client 的通过调用 GTK API 端做 client,存在弊病:

  1. 默认会将导出的 fd, 通过 GTK widget 画在默认的 Display 上,然而理论推流过程并不需要这个步骤。
  2. 部署 Xorg 也减少了复杂度。

须要将依赖和 GTK 的组件移除,升高组件依赖复杂性和性能耗费。

具体而言:

  1. Display channel 相干的 GTK widget 依赖移除,。
  2. 替换原有 Display,对 Nvidia,getPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, (EGLNativeDisplayType)dev[num], NULL) 导出。
  3. 替换原有 Display,对 VAAPI 的 AMD 或者 Intel 显卡,因为应用的 mesa 图形栈,getPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA 导出,须要留神的一点是在 VAAPI 接口中,将初始化用的 Display 换成 DRM 导出。
#if ENABLE_GTK
int vaapi_init() {x11_display = XOpenDisplay(g_getenv("DISPLAY"));
    va_display = vaGetDisplay(x11_display);
#else
int vaapi_init(int drm_fd) {va_display = (uint64_t)vaGetDisplayDRM(drm_fd);
    g_message("drm_fd:%d va_dpy:%p", drm_fd, va_display);

#endif
    int major_ver, minor_ver;
    va_status = vaInitialize(va_display, &major_ver, &minor_ver);
    return 0;
}

移除 gstreamer

音频的 pipeline 应用了 gstreamer,这部分依赖能够去掉。

图形转换优化

总体想法就是图像的 Zero-copy,缩小在 CPU 与 GPU 之间的拷贝与图形格局之间转换。

编码卡反对

反对主机通过 PCIE 外插硬件编码卡进行硬件编码。

混合硬件编码反对

总体想法就是利用 host 渲染能力,将渲染后的 RGBA 或者 YUV 导出给编码卡,达到最大限度利用渲染资源,进步并发路数的工作。

自降级

通过 Host Gameservice 过程自我降级固件,不依赖整体部署 pod 节点镜像更新,能够灵便实现降级。

监控与 SRE

对系统指标的打点和性能的监控,欠缺 SRE 等监控体系,治理过程解体,卡死,内测透露等检测。

其余

整个云游戏的视频流硬编码方案的实现和上线部署离不开跨部门的单干,再次感激一起将整个计划从开始设计到到上线的外部兄弟团队如根底零碎部门 STE,多媒体 RTC 等部门,通过团队合作推动整个计划上线以及后续线上继续优化和治理。

对于咱们

作为字节跳动的视频中台部门,视频架构反对了字节全系产品的点播、直播、实时通信、图片、云游戏、多媒体业务倒退,指标成为业界多媒体解决方案领导者,构建极致的视频技术 / 产品服务体验!

视频架构 - 设施与服务团队聚焦多媒体 +IoT/5G 相干畛域,孵化可能赋能业务的新场景和核心技术,打造极致的、软硬件联合的技术解决方案,上线了云游戏、云手机、视联网、多屏互动等多款服务,反对了抖音、西瓜等泛滥外部产品,同时也通过火山引擎提供 toB 服务。欢送更多同学退出咱们,构建行业顶尖的视频翻新技术,分割 luxuguang@bytedance.com 注明“设施与服务方向”。

参考

https://en.wikipedia.org/wiki/Direct_Rendering_Manager
https://en.wikipedia.org/wiki/Direct_Rendering_Infrastructure
https://en.wikipedia.org/wiki/Mesa_(computer_graphics)
https://source.android.com/devices/graphics
https://www.opengl.org/
https://www.mesa3d.org/
https://www.spice-space.org/
https://www.qemu.org/
https://docs.nvidia.com/cuda/cuda-driver-api/index.html

正文完
 0