一种用于voip客户端多窗口视频渲染的实现思路

在我以往的opengl渲染视频的使用经验中,是由opengl渲染环境管理窗口句柄,将作为界面层传入的窗口句柄的子窗口嵌入其中进行视频图像显示。现在介绍的一种实现方式与此大相径庭:渲染的窗口句柄由上层界面管理,每个窗口句柄绑定一个opengl渲染环境进行视频的渲染,opengl渲染环境一旦创建就不再销毁(在此描述的只是客户端界面窗口与opengl渲染环境的关联的一种思路,并非描述opengl渲染的方法)

背景

最近有一个项目,客户端运行环境是麒麟x86系统,客户端点播监控图像,客户端的界面是用QT做的,图像显示时是分屏模式,最大分屏数为9分屏(包括4分屏,6分屏等),分屏模式间可以相互切换。每个分屏显示一路图像。9分屏如下图所示:

image
界面业务:

  1. 界面层是不会进行具体的图像渲染的,在点播会话建立成功后,界面层会将对应要显示图像的窗口句柄传入媒体库进行渲染
  2. 界面上针对单个分屏,是有放大,全屏,缩小功能的
  3. 会对监控进行轮询,一次自动点播指定分屏数量的监控图像,下次再自动点播其他监控的图像,自动轮询
  4. 可一键点播多路监控图像

媒体库渲染的实现:

  1. 渲染采用的是opengl,媒体库将opengl的渲染流程封装到一个叫Render的对象中
  2. 每次点播会话建立成功将会创建一个新的Render对象,结束会话会销毁Render对象
  3. 界面传入的句柄会传入Render对象用于渲染的显示

问题

对多路图像进行轮询点播时,媒体库会随机崩溃(有时候轮询一次就会崩溃,有时候轮询很长时间都不会崩溃),根据core文件中的堆栈信息,是崩溃在opengl渲染的API中

解决

分析

Render对象是对opengl渲染流程的封装,

  1. 每次会话建立后会创建一个Render对象,会话挂断时会被销毁,Render销毁是异步释放(会话被挂断销毁,Render可能正在释放)
  2. Render在Init时会根据传入的句柄,在窗口上创建opengl渲染环境,在销毁时会在close函数中销毁opengl渲染环境
  3. 每路图像渲染为一个线程,所以一个渲染线程中,有一个Render对象,一个窗口句柄,一个opengl的渲染环境
  4. 同一个Render对象中opengl渲染流程都是串行的

实现思路

因为崩溃的堆栈信息显示在opengl的渲染API上,所以最开始的时候怀疑是opengl不支持多窗口渲染(以往的opengl使用经验都是通过渲染模块自己创建窗口,然后将窗口作为子窗口嵌入界面指定的父窗口中,所以opengl渲染环境只有一个),在与界面的开发人员讨论解决方案时,得知界面分屏的实现机制:固定创建最大数目的窗口(这里是9分屏),不管分屏怎么变换,窗口是不会被销毁的。

然而对负责渲染的Render对象它是与会话绑定的,会话创建时它会创建,会话挂断时它会被销毁,但界面分屏窗口是恒存在的(显然同一个窗口上显示的不同的会话的图像,对不同的会话传入同一个窗口句柄),但是在挂断会话后,Render对象会被异步销毁,假如在同一个分屏窗口上点播一路视频,那么新的Render对象与正在被销毁的Render对象可能会使用的是同一个窗口句柄,如果被销毁的Render对象在释放opengl的渲染环境,而新的Render对象正在渲染,此时就会造成崩溃

按照这种思路,对渲染实现方式进行修改:Render对象只创建(不再销毁,按照业务的需求最大也就同时点9路,所以最多会产生9个Render对象)在界面传入窗口句柄时,传入一个index标识分屏序号。最终,每个分屏窗口都固定绑定了一个Render对象,Render对象不会被销毁。如此修改了,轮询点播时不再崩溃。

本质

  1. 问题的本质

崩溃的本质是不同的Render对象对同一个窗口句柄存在竞争关系,没做不同Render对象间的同步,必然会导致崩溃。

  1. 本质关系

会话,分屏窗口,Render对象,opengl环境的本质关系:会话挂断,分屏与Render对象还存在,这时Render对象的中的opengl渲染环境并没有被释放,而只是没有图像数据,不渲染图像罢了,该分屏上再建立另外一路会话,对Render对象来说只有又了图像数据,可以进行渲染。所以本质上,Render与opengl环境根本不需要与会话关联。

理清楚本质关系,其实并不需要对Render对象间进行同步,如果进行同步,必然会影响视频图像的帧率。

注意点

按照上面所说的实现方式有几点需要注意:

  1. 每路会话中,图像的分辨率可能会不一致,在渲染需要做判断,如果分辨率变化,需要重新分配纹理的空间
  2. 因为窗口句柄有上层界面掌握,在挂断后,界面后重新刷新窗口的背景色,这个时候opengl环境可能会变化,在渲染时需再重新设置窗口的渲染环境
  3. 分屏窗口会放大,缩小,此时需要改变渲染时的视口大小

总结

关键点,理清会话,分屏,Render对象(opengl渲染环境)之间的关系,Render对象应该与分屏绑定,不同会话图像的渲染对Render对象来说只是图像数据不同。

实现方式:Render对象只创建不销毁,通过Index标识与分屏窗口绑定,在分辨率变化时,纹理对象需重新分配,在分屏大小变化时,视口需变化

补充: Render对象对opengl的封装是基于 GLX库进行

GLX库资料

Xlib手册

GLX_and_Xlib

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章