存档

‘Win32’ 分类的存档

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 标签:

Win32选择保存文件路径和打开文件夹的函数

2014年9月30日 评论已被关闭

有时候我们需要拾取打开或者保存的路径,浏览文件夹,下面是win32的接口。

MSDN:GetSaveFileName
MSDN:SHBrowseForFolder

分类: Win32 标签:

使用librtmp进行H264与AAC直播

2014年1月3日 评论已被关闭

libx264版本是128
libfaac版本是1.28

1、帧的划分

1.1 H.264帧

对于H.264而言每帧的界定符为00 00 00 01或者00 00 01。

比如下面的h264文件片断这就包函三帧数据

00 00 00 01 67 42 C0 28 DA 01 E0 08 9F 96 10 00
00 03 00 10 00 00 03 01 48 F1 83 2A 00 00 00 01
68 CE 3C 80 00 00 01 06 05 FF FF 5D DC 45 E9 BD
E6 D9 48 B7 96 2C D8 20 D9 23 EE EF …

第一帧是00 00 00 01 67 42 C0 28 DA 01 E0 08 9F 96 10 00 00 03 00 10 00 00 03 01 48 F1 83 2A
第二帧是00 00 00 01 68 CE 3C 80
第三帧是00 00 01 06 05 FF FF 5D DC 45 E9 BD E6 D9 48 B7 96 2C D8 20 D9 23 EE EF ..

帧类型有:
NAL_SLICE = 1
NAL_SLICE_DPA = 2
NAL_SLICE_DPB = 3
NAL_SLICE_DPC = 4
NAL_SLICE_IDR = 5
NAL_SEI = 6
NAL_SPS = 7
NAL_PPS = 8
NAL_AUD = 9
NAL_FILLER = 12,

我们发送RTMP数据时只需要知道四种帧类型,其它类型我都把它规类成非关键帧。
分别是
NAL_SPS(7), sps帧
NAL_PPS(8), pps帧
NAL_SLICE_IDR(5), 关键帧
NAL_SLICE(1) 非关键帧

帧类型的方式判断为界面符后首字节的低四位。
第一帧的帧类型为: 0x67 & 0x1F = 7,这是一个SPS帧
第二帧的帧类型为: 0x68 & 0x1F = 8,这是一个PPS帧
第三帧的帧类型为: 0x06 & 0x1F = 6,这是一个SEI帧

以上是我们利用帧界定符划分帧,并可以判断每一个帧的类型。

注意:如果是压缩图像成H264帧,我们就可不必进行帧界定,因为每一次压缩的输出都明确了该帧的大小(包括界定符),每一次的压缩的结果可能包函多帧。一会具体讨论。

1.2 AAC帧

对于AAC帧它的界定符是FF F1

这里我就不举例了,可通过查看AAC的二进制文件可以看到如下的帧结构。
FF F1 50 80 24 9F FD DE 04 00 00 6C 69 62 66 61 61 63 20 31 2E 32 38 00 00 42 15 95 ..

注意:那么对于AAC而言加上界定符每一帧的前7字节是帧的描述信息,也就是说AAC的祼数据是除去前面的7个字节的,在发送RTMP时,我们要去掉这7个字节。同样,如果我们是一边压缩一边发送RTMP,我们同样不需要界定帧,因为libfaac每次压缩完成的输出就是一个完整的帧数据,我们只需要将该帧打包发送即可。

综合上面的所述,如果我们只是一边压缩一边将压缩结果发送到RTMP服务器,那我们就可以不用对帧进行界定,如果我们是发送H264与AAC文件,那我们就要对帧进行界定。

2.视频与音频的编码信息

如果我们只是简答的将压缩数据打包发送给RTMP服务器,那么RTMP服务器是不可以对数据进行解码和播放的,在这之前我们要将音视频的视频的编码信息发送给RTMP服务器。很多人可能苦于寻找下面的三个编码参数而不得要领。其实要想得到也是很简单的。 阅读全文…

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

GBK与UTF-8之间的转化

2013年11月11日 没有评论

GBK编码与UTF-8之间的转化。
第一种是先将 GBK->Unicode->UTF-8;
第二种是通过iconv库,直接GBK->UTF-8;
Unicode编码包含所有字符集,而GBK(GB2312是GBK的一个子集),UTF-8等等都称为多字节集(Multibyte),两个多字节字符集之间的转换首先将源字符集映射到Unicode上,再将Unicode映射到目标多字节字符集上。有一个问题就是源字符集与目标字符集不可能是一一对应的,所以在转换的时候往往会有一个参数表示如果未找到对应的字符的处理情况。另外Unicode中的表示字符用wchar_t,而多字节的字符用char表示(也有多个char表示一个字符)。

windows下

C语言中有与WideCharToMultByte对应的接口是wctomb,MultiByteToWideChar对应的接口是mbtowc,但是对应的转化不能将正确的转换UTF-8。具体实现是先将当前语言为源字节集,将源字节集转化成Unicode,然后将语言设置成目标字节集将Unicode转化成目标字节集,具体参见:
setlocale: http://msdn.microsoft.com/en-us/library/aa272906(v=vs.60).aspx

利用第三方库实现,它就是大名鼎鼎的libiconv,有一个值得小心的地方,就是当目标字符集中没有源串中对的字符是可以选择继续还是忽略,所以及在open时会带上参数”//IGNORE”,要不然就遇到这种情况会停止转化。

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

使用原子操作实现自旋锁

2013年10月14日 没有评论

原子操作
在windows平台有InterlockedCompareExchange原子操作接口。实现对内存的互斥修改

如果对函数的解释有点疑惑,可以看一下该函数的大致实现。

自旋锁
如果我们所1代表“加锁”,0代表“未加锁”。加锁的过程是将内存值0改成1,所以我们可以利用这特性实现加锁操作和解锁操作。

知道trylock只是尝试去将0改成1,如果之前已经是1说明,锁被占用了。所以我们实现了自旋等待锁被释放。

更好的实现,用Sleep切换CPU。

对于基于linux的gcc也是有相应的“比较修改”接口__sync_bool_compare_and_swap(lock, old, set),它的参数与windows的接口有两点不一样,参数不一致,返回类型也不一样。它的trylock的实现:

example

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

VirtualBox从U盘启动方法

2011年12月25日 1 条评论

#进入命令行

#获取磁盘信息(这个最关键)

#打开d:/diskdrive.html ,查看DeviceID栏,获取到你的U盘DeviceID,如\\.\PHYSICALDRIVE1

#为U盘创建启动文件

# 添加磁盘d:\UsbDisk.vmdk, F12启动虚拟机,选择这个磁盘即可磁盘

分类: C/C++, Linux, Win32, 翻译, 计算机网络, 转载 标签: