存档

作者存档

OBS的插件-画板实现(基于Qt)

2023年8月28日 评论已被关闭

简介

OBS基于OpenGL和D3D11实现了一个通用的图形库,可以利用这个图形库来实现简单的画板,至于更复杂的画板实现的原理也是一样的。实现图如下。

 原理

说明一下,因为OBS自带的做图非常麻烦且无法满足要求,这里使用Qt自带的QPainter进行内存做画,然后输出RGBA像素数据。

在使用插件之前我们要对插件进行注册

实现方式一:obs_source_output_video

使用异步视频源,注意插件的output_flags使用OBS_SOURCE_ASYNC_VIDEO这个标志,当画面需要更新时,使用obs_source_output_video将帧输出源中。

大致代码如下:

上述代码很好地展示了绘制后立即输出到源上,但上面的代码有一定的性能问题。

问题:当鼠标事件瞬时非常多时,会出现fps过高导致CPU和内存暴涨,那么这时候,我们可以加一个定时器解决,比如我们限定最多30fps。

我们修改了updateRect代码,使得将更新的频率固定住,这样不管鼠标事件多么的多,我们最多只会33ms更新一次(fps=30)。

上面的代码看上去比之前还要好了,但还是有优化的空间,因为每一次更新的是局部图片,但是输出却是完整的像素,会重复拷贝内存像素数据到显存,分辨率较小时体现不出来,一是分辨率稍微大一点也会有严重的内存和CPU性能问题,此时这种方法已经无法满足要求了。

实现方式二:使用纹理

第一种方式使用了异步视频源,这种方式在需要的时候输出帧就可以了,但是有一定的性能问题,那么可以可以使用纹理贴图的方法。如果要使用纹理贴图我们首先要指定插件的渲染回调函数。

接下来,我们将将内存数据贴到纹理上。

这样就实现了对纹理的贴图,但是上面的方法还有一个性能问题。问题在于,只要渲染就将所有像素数据拷贝到显存,显然是没有必要的。我们应该使用局部更新,在需要更新的时候将那些需要更新的像素拷贝到显卡。也就是dirtyRect那个部分。很遗憾的事OBS没有这个接口,所以我们自己实现一个。

在实现之前我们看一下全局更新的代码,gs_texture_set_image如下。

通过观察代码,我们看到obs使用内存映射的方式进行拷贝,并且区分了是否翻转,这里我们没有这个需求,正着拷贝就可以了,所以我们就可以按照上面的方式使用局部拷贝。核心操作无非就是按行复制,需要注意整张图的行大小和局部矩形的行大小,以及起始地址的对齐。

然后在调用的时间传入dirtyRect就可以了。

到此,所以优化就结束啦。

实现

代码整理中,即将扩充各种图形,联系nie950@gmail.com

分类: C/C++, OBS, Win32 标签: