2020-09-15 8165 2
# GueeRecorder (Guee 录屏机) 前言
这是一个为了学习Linux下的应用程序开发,而编写的一个实验性的对屏幕和摄像头录像,然后保存为视频文件或直播到网络的软件。
这是我第一次在Linux上开发的软件,在此之前我从未实践过Linux下的软件开发,甚至于在购买基于国产CPU龙芯3A4000的电脑之前,我对Linux系统的累计的使用时间大概也只有几天。
2020春节期间,在快递把龙芯3A4000送到之后,尝试编译了一款名为“SimpleScreenRecorder”的软件,但它的功能太简单,并且由于源码中对x86有大量SIMD汇编优化,但龙芯却只能使用兼容的C++代码,在龙芯CPU上的性能表现也就有些不堪。我数年前开发过Windows平台下的录屏软件,不过那是公司的产品,各种功能实现与Windows及x86也关联得较深,无法直接移植,于是我就萌生了在直接龙芯平台上重新开发一款录屏软件的想法。只是我也知道最大的性能瓶颈是视频编码库没有优化,可我能力有限,能够控制的只是把图像送入视频编码库之前的代码。但我还是想要尝试一下,性能优化有困难,但是在功能方面做得比它完善一些,应该不难。虽然我知道同类软件中有个大名鼎鼎的OBS,我预计要完成的功能只是OBS的子集,但作为第一次在Linux上开发的程序,只要能把预计的功能完成,并且在龙芯平台上的运行效率能稍高于SimpleScreenRecorder,那就是胜利。
然后一边学一边做大约花了半个月时间写了一些基础的代码,只是由于众所周知的原因,不久后孩子开始上网课,公司也要求远程工作,于是每天疲于奔命,就中断了开发。后来虽然稍有了一些空闲时间,但每天琐事仍然很多,自造轮子这种不太紧要的事情也就一直没有重新拾起。在连续的繁忙之后,每天颓废得连上网水帖看新闻都没有精力,开发中断之后,龙芯的电脑我都有半年没有开过机。
本来很怀疑这个软件又会像我以往的众多半成品一样,一旦开发过程被打断,就会永远躺在硬盘里成为一个归档文件。但这毕竟是我第一次在龙芯电脑上开发软件,还是不愿就这么半途而废。即使只是为了熟悉Linux下应用软件开发,我也应该把它完成。近期工作生活都逐渐像疫情前那样按步就班有了条理,因此我决定继续开发这个软件。由于这个软件总体上算是学习过程中的实验品,无论功能、界面还是代码等都感觉有点凌乱,因此打算在把它完成之后,再重写所有的代码,继续提高软件的性能和完善软件功能,还需重新设计软件界面。大概,未来计划中的2.0版才会是令我自己基本满意的作品。
# 开发日记(日期逆序):
2020-11-01
没想到bug还是不少,这不,这几天又改了一些。现在的版本号是 1.0.1-4,表示这是1.0.1版的第4个包。因为改动比较频繁,而我又没有太多时间来测试,就造成现在的情况。不过现在的bug应该比较少了吧,基本上处于稳定状态——如果不继续增加功能的话!!
先休息一段时间,这几天的改动都不大,休息好了之后再继续增加功能。其实是公司的工作有点忙,这种个人项目只能往后放放了!
新增了UOS for ARM的安装包,这个是在树莓派4b上用KVM安装了一个UOS,在里面编译和测试的。我觉得我需要一个打包工具,现在打包太麻烦了,三种CPU架构,两种操作系统,编译麻烦那是没办法,打包也这么麻烦就说不过去了!!!下一个项目暂定写一个简单的打包工具吧。
1、修改了添加新图层没有被自动选中的bug;
2、修改了图层缩放到全屏时,鼠标移动它会恢复原先大小的bug;
3、修改了对图像的颜色只修改色相时不生效的bug;
4、修改了在录像开始后,在预览界面上拖动图层,偶尔造成编码的视频中,图层边缘部分有残留的问题;
5、修改了删除图层偶尔导致程序崩溃的问题;
6、给托盘图标的菜单增加了图标;
7、增加了ARM的安装包。
2020-10-24
1、修改了在Fedora28上截屏图像由于存在透明通道,而绘制不正确的问题。
2、测试UOS、Fedora28、Loongnix的二进制兼容性,优化了打包方案。
3、尝试检测CPU型号,在3A4000和3A3000上使用不同的库文件。
4、修改了初始化时音频码率设置不正确的问题。
5、把程序图标更改为了矢量图标。
2020-10-19
再度优化了性能。在UOS + 3A4000 上实测,关闭桌面特效之后,录制 1920*1080@30fps 的视频并同时录制音频时,最低CPU占用率降低到了20%左右(本程序窗口最小化,桌面只保留系统监视器)。这是在 R5 230 上的测试结果,把显卡换成 RX 580 后,CPU占用率最低还能再降 2%。
修改了结束录制时程序偶尔崩溃的问题。
修改了当编译配置较高导致编码速度过慢时,丢弃帧数据会导致视频画面出现未更新的小方块的问题。
版本号就不改了,仍然是 1.0.1,反正也没人来下载。重新打了包,并增加了 Fedora28 的安装程序,但在 Fedora28 上的性能表现要比UOS差一些,主要原因是 Fedora28 中自带的GCC编译器不支持 -march=gs464e,我编译 x264 时不能使用较优化的编译参数。
2020-10-18
改善了程序启动时枚举录音设备以及打开录音时,会等待较长时间的问题。优化了在AMD R5 230上的性能表现,现在也能稳定地录屏1080@30fps了,并且CPU占用率与在 AMD RX580 上基本一样。试验了一些方法,发现有降低性能的风险,还需要需要进一步测试,反正现在的状态应该还算不错了。
2020-10-14
又改了一点小bug。现在主要是测试对x264的调用优化,是调用和调参方面的优化,不是优化x264的编译和代码,期待再降低一些CPU占用率,测试有效,但还有问题,暂时改回,有空再详细研究。
另外,初始化声卡录音时,不知为何耗时很长,会等待1秒钟以上。并且在UOS/龙芯上,录音的CPU占用率也比较高,不知是否是Qt的问题,因为录音部分是使用的Qt提供的类。录音暂停时也有CPU消耗,这方面如果能有改进,在龙芯上的CPU占用率应该还能降低3到5个百分点。
在Intel i5-4460上分别测试了SimpleScreenRecorder和我的程序,与在龙芯3A3000上的表现进行对比。无论是在i5上,还是在龙芯上,我的程序录屏时(1080@30fps)的最低CPU占用率,都是 25~30%,3A3000和i5的差距不大。SimpleScreenRecorder则是一个天上一个地下,在i5上最低14%(不开预览,打开预览后与我的程序基本一样),在3A3000上则是至少70%,主要原因是没有使用编译优化的x264库,反正直接下载安装的就是这个模样。
2020-10-11
程序在龙芯7A1000集显上的性能表现,我暂时没法改善了……不知道我的电脑是不是集显坏了。在UOS上查询集显的制造商居然是“VMware, Inc.”,居然是个虚拟机的显卡驱动。而 Fedora28 则进入不了桌面,输完了密码之后,屏幕上就只剩一个鼠标光标,黑屏。用 Fedora28 的安装盘启动,也是一样的现象,以前都是好好的……
然后我想起来,7A1000中的集显,驱动都不全,OpenGL版本支持低,性能也差劲……暂时放弃吧!!!
2020-10-10
由于系统自带的x264库优化不足,视频编码时CPU占用率一直居高不下。我去下载了x264的最新源码,本来打算详细调校编译参数自己编译一个性能好一点的动态库出来,结果居然有惊喜。自行编译的x264,终于让这个程序有了实用价值。
学习了怎么打deb安装包,比想象的要简单,在 UOS 龙芯版和AMD64版上,测试安装已经通过。
接下来,着重改善在龙芯7A1000的集显上的性能吧,总不能说在龙芯上开发的录屏软件,在龙芯桥片的集显上录屏,每秒只能录1帧……
2020-10-8
在国庆假期结束前,终于挤出时间把窗口截图的代码调试通过,其余时间主要是用于完善编辑界面。现在编辑界面感觉好用多了,以前图像元素的边框,是用户拖了多大,就显示多大,如果对图像锁定了比例,边上就会空出一大块,能透过它看到下一层,但就是不能点到下一层。现在改成了图像是多少,边框就是多大,没有多余的部分。预览区域的有效区域之外,增加了方块网格显示,但边界更明显。当图像拖出了有效区域之后,增加了替代图像,以免看起来很突兀。图层管理的界面也有修改,正在逐步往更好用的方向发展。
增加了调整图像颜色和透明度的功能,感觉代码还需要优化优化。
修改了在添加摄像头的界面中,当有多个摄像头时,仅仅切换界面也会导致摄像头重新初始化的问题。
2020-10-03
前几天比较忙,对这个软件的开发没有顾得上。现在放了国庆假,可是需要到处跑,可能开发也是断断续续的,不太顾得上。
使用Xcomposite方式截取窗口图像的实验已经通过,代码还需要继续完善。截取窗口的代码我写了两遍,可能熟悉linux开发的程序员觉得简单,但我是linux新手,之前对x11没有一丁点儿概念。不得不吐槽x11的文档真的是简陋,远远比不上MSDN中关于窗口API的部分。看着文档琢磨了很久,做了不少实验,甚至去翻看了libx11的源码,然后才发现如果仅仅只是实现窗口截取的功能,其实很简单。不过第一次实现使用的方式效率实在太差,不得已又去看OBS的源码,OBS的源码有点绕,我说不清是过度封装还是耦合太深,反正对照OBS截窗口的相关源码和各种API文档,也花了很长时间才看懂了截图过程。然后我编写了自己的截窗口图像的代码,效率方面要比OBS相同功能高一点点,因为OBS截图后对纹理会进行一次复制,而我省去了复制纹理的过程,不过对GPU来说,减少这一点处理能提高的性能非常有限。
修改了前几天引入的一个新bug,现象是图层列表中的项目有时删不掉。
这几天还大概看了一眼vaapi,以前用nvidia的NVENC做过视频编码,vaapi倒是第一次接触,目前还不知道怎么用,因为示例和文档我又不知道上哪儿找,东拼西凑想了解全面比较困难。不过仍然打算在把窗口截图代码完善之后,以及把预览界面上一些体验较差的问题改良之后,就尝试用vaapi来编码视频,提高性能,降低CPU消耗。
测试了一把程序在龙芯集显上运行,发现根本不正常,暂时没时间去跟不正常的原因,要优先保证在独显上能比较好地运行。
2020-09-25
现在选窗口时,窗口外框的绘制不会闪了,我两三天的空闲时间居然都在改这个小bug。XWindows的API真难理解,数量很多,文档却不如MSDN详尽,遇到问题想找个例程参考参考都很难找到。不同的窗口管理器有不同的实现,一点儿也不统一,难怪Linux上的桌面程序远远不如Windows上花哨——常规窗口都可能不兼容,玩花活太容易死翘翘。直到现在为止,我取窗口标题文字都还没有完全成功,试了几种API,仍然有些窗口莫名其妙取不到。
层管理窗口放在预览界面中似乎有点影响操作,我把它移出来了。
周末打算看看Xcomposite,又是一个靠谱的例程都没有找到,大概得去翻OBS的源码了。
今天暂时不打测试包,等把 Xcomposite 搞定了再一起打包。
2020-09-21
这个周末修改的bug有点多,所以改完之后就我直接把版本号改成 beta 了。接下来重点要修改的bug,是选取屏幕窗口时绘图闪烁的问题。之后继续测试看看还有没有其它bug,然后就可以继续添加功能了。
另外我测试了一下OBS在龙芯上的表现,在龙梦Fedora28上,仅仅把屏幕加入了预览画面,它就占满了一个CPU核,预览画面的绘制帧率只有5fps,说明不但没有对龙芯优化,实际表现甚至是负数。我的程序本身也没有对任何CPU进行针对性优化,但至少性能表现还算是正常水平吧,目前稍稍领先于也没有针对性优化的SimpleScreenRecorder。不过我从OBS中的窗口捕获中学了个新名词:Xcomposite,捕获的窗口画面不会被桌面其它窗口覆盖,目前我还不知道这是什么,不过相信过一段时间我也会使用这种方式捕获窗口画面,抛弃掉现在通过桌面区域捕获窗口画面的方式。
下面是这两天修改的内容:
改进了内部的数据处理,降低了从GPU取出图像耗时对编码帧率的影响;
改进了屏幕截图的时间控制,时间戳更均匀,播放效果更流畅;
修改了写成mp4文件时,有几率造成某些播放器播放视频时旋转了一定角度显示的bug;
排除了录制鼠标时的内存泄露,以及视频帧缓冲时的内存泄露;
增加了对视频编码的B帧支持,以及相关设置界面;
修改了码率控制方面的bug,以及相关设置界面;
设置界面中改进了分辨率设置,增加了"-"/"+"两个接钮,每按一次就自动贴近常见的标准分辨率。
视频编码设置界面中增加了部分Tooltips文字,鼠标移上去显示相关说明。
2020-09-18
前两天在 loongnix 下测试了龙芯优化过的 x264 编码库,确实能提高近一倍的编码性能。但是由于它是在 loongnix 上的,在 UOS 并没有。我试过直接复制二进制文件到 UOS 中动态加载,但并不能很好地运行,编码出的视频数据有错误,如果想在 UOS 上正常使用,可能必须要用龙芯优化过的源码在 UOS 上重新编译。
另外修改了一些 bug,比如在 loongnix 上录音时程序会崩溃,然后发现是某处内存访问越界造成的,而在 UOS 这个问题没有暴露出来,说不定什么会崩溃。还有一处线程同步死锁,还有编码 B 帧会导致写入视频文件的时间戳顺序错误等问题,测试次数太少还真不容易发觉。现在程序应该是比较稳定了,毕竟我这几天并没有白折腾。
2020-09-14
目前程序功能已经基本完成,还有一些已知的bug没有修改。后续的其它功能添加,还有些问题需要考虑。
今天先学学在Linux下怎么给应用程序打包,先发出去找人用一用再说。
2020-09-13
为了解决昨天发现的录制麦克风时,数据的真实采样率不正确的问题,简单写了段重采样代码,然后发现问题又变了。实际采样率变成了设置值的2倍,正在调试的时候,情况再变,只是重启了一下电脑,这个bug就不复现了。我发誓我真遇了这种诡异的系统级bug,我的代码没有问题,用其它的录音软件甚至无法录音,Qt的录音库也应该没有问题,但它就是发生了,然后又消失了!!!
……数小时之后,音频采样的数据只有900Hz左右的问题又莫名出现了,重采样代码还是用上了。另外,当音频的实际采样率是设置值的两倍时,并不存在有效数据,此时在UOS系统的音频设置中也检测不到麦克风的音量。由于通过时间和数据量计算采样率并不是十分准确,我的重采样代码也是简单的线性插值,因此当麦克风异常时,录音效果并不好。等把各种功能的逻辑调通了,没有其它问题了,再来改进音频重采样的算法。
2020-09-12
写完了录音代码后,自我感觉代码逻辑没有问题,但录音只能录电脑播放的声音,录麦克风总是不对。然后发现一个天坑……估计这也是我测试几款软件在UOS@Loongson(龙梦A1901主板)上都录不到音原因。
在龙芯上,录Micphone时,获得的音频数据有问题。比如设置为 11025Hz 采样时,实际获得的音频数据只有 900Hz 采样。设置为 22050Hz 时,则是 1800Hz 采样……44100Hz 的实际采样率就是 3600Hz 了。但是,也不是总是这样,偶尔又能得到正确的采样率的数据,这令我很迷茫!!!
另外测试了USB麦克风,这个采样是正常的。写代码初步测试时没有发现这个问题,当时只检测了有没有声音数据,后来发现录下的声音总是不正常,才去统计了数据长度。为了确认集成声卡录音是采样率的问题,我把声音写成 wav 文件,然后用音频编辑工具更改回放速率,然后听到了基本正常的声音,说明确实是获得的录音数据采样率不对,这么低的声音采样率,居然以正确的速率回放时还基本清晰,我也觉得很神奇。
看来这至少是个系统级的bug,我无能为力,只是尽量在应用层面进行改善,之前我以为不需要做的声音重采样需要加上了,而且必须先计算录音数据的真实采样率再重新采样。
2020-09-10
做完了录音功能相关的设置及控制界面,已经可以录到声音,但没有完成时间同步和音频编码。我想要分别录制电脑回环声音和麦克风输入的声音,进行混音后再进行AAC编码,并且录像过程中可以随时对两路录音单独进行开关,对音频流的时间同步控制就会比较麻烦。从实验结果来看,声音重采样似乎不需要做,混音也可以做得比较简单。只是白天上班,晚上暴肝,开发效率低于预计,希望明天能把录音功能做完。
2020-09-07
决定把编码画面时间戳误差较大的问题放到以后再改善,先把录音的功能加上。今天用SimpleScreenRecorder测试了ALSA、PulseAudio、JACK几种接口的录音,发现UOS@AMD64上默认情况下只有ALSA能录到声音,而在龙芯上则什么声音都录不到。Qt中应该也是用的ALSA的录音方案,于是翻看Qt的例子代码,打算就直接用Qt的库做录音功能了。但是录音如果采样率与音频编码库及视频文件格式的要求不匹配,还是要自己写一段音频重采样的代码。
2020-09-06
今天继续修改昨天没改完的问题,目前剩下的最严重的问题是画面运动不平顺。由于程序设计为屏幕截图只是画面来源之一,可以随时添加删除,因此画面渲染线程和屏幕截图线程都有独立的帧率控制,以渲染线程的时间作为视频帧的时间戳,而渲染线程只有在图像来源的内容有变化后,且在帧间时间间隔到达后,才会绘制一帧画面送到编码器,这样帧时间戳就与实际的截图时间不一致,并且每帧的时间差距都可能不同。由于帧间时间不平均,造成看起来视频不流畅。可是由于画面内容可以随时增删,帧时间就不能以屏幕截图时间为准备,可能需要对内部的流程做些调整。
今天还和SimpleScreenRecorder简单对比了一下性能。虽然我的程序对图像的处理流程要复杂很多,但在龙芯平台上我的程序效率仍然略高一点。但在x86(i5-4460@3.2GHzx4) UOS上SimpleScreenRecorder的CPU占用率要比我的程序低十个百分点。因为两者使用相同的x264库文件,编码参数也尽量一致,性能差距就只发生在视频编码之前。证实了我以前的判断,因为它对龙芯CPU没有任何优化,所以即使简单的颜色空间转换,也比我更复杂的处理流程消耗了更多的CPU时间。我以前测试过用SSE计算1080@30fps的RGB to YUV 转换,i5级别的CPU消耗在1%~2%之间,未优化的C++直接计算则需要10倍甚至更多的CPU时间。另外还有x264编码库的性能更是瓶颈,代码优化和未优化达到几倍的性能差距很常见,取决于优化的程度,有的程序达到几十倍的性能差距也不罕见。我在龙芯开发者社区看到Loongnix系统中有龙芯优化过的x264库文件,可惜作为Linux新手的我没能在Loongnix成功配置Qt的开发环境,暂时试验不了龙芯优化过的x264编码库有多少性能提升。
2020-09-05
之前对 QWaitCondition 的理解有误,造成编码线程偶尔出现一直 wait 的情况,用惯了 Windows 的 SetEvent 造成了认知障。
设置界面初步搭建完成,然后暴露出许多bug,比如修改分辨率造成重新计算图像绘制坐标时出现混乱,以及预览区域绘制错误。然后发现编码生成的视频文件也有许多问题,时间戳计算有误,画面运动不平滑。还有之前设计的用OpenGL把RGB转换到YUV420也发现结果不正确,是因为奇数分辨率没有处理好,以及行字节没有对齐的原因。
2020-09-04
把OpenGL渲染合成的画面绘制另一个FBO,使用着色器完成了RGB到YUV(I420)的转换,从FBO中取出数据就可以直接送往x264编码器了。
以前在Windows下写过一个从TS/MP4文件提取H.264流,并重新封装为其它文件格式的小工具,现在把写文件相关的代码拿到这个项目中,进行了一些修改,已经整合完成。屏幕、摄像头画面合成->编码为H.264流->保存为FLV/MP4文件的整个流程已经调通,但暂时还是没有音频。
接下来需要完成的是视频编码设置界面,当前连分辨率和帧率都是直接写在代码中,没有设置界面这个软件就没法正常使用。
2020-08-31
把本项目上传到GitHub,并补上日志记录开始过程。
对摄像头数据的处理,是把YUYV数据放入OpenGL纹理,在着色器中转换为RGB再用于渲染,不使用CPU计算。
由于对Linux录音完全不了解,打算先把画面编码为h.264之后再增加录音功能。另外打算把合成的画面渲染到FBO时做一些处理,最好是从FBO取出就是I420的数据,可以直接送到x264编码器编码视频。
2020-08-29
开发环境换回到真机,还是在龙芯3A4000上开发,只是操作系统从Fedora28换成了UOS,不是Fedora28不够好,只是UOS更漂亮,顔值即正义。
验证了Qt支持的数种获取摄像头数据的方法,决定还是取得YUYV原始数据,自己再处理为RGB,我认为这样会有更高的效率。
2020-08-27
实验增加录制摄像头功能。并把较简单的对图像文件的支持加上。
实验录音功能,由于对Linux了解太少,暂时没有头绪,Qt自带的录音类感觉效率有点低。
实践证明在x86+Win10+VMWare+UOS的环境中,摄像头功能和录音功能都跑不通,决定不再尝试在虚拟机中开发。
2020-08-25
注册统信UOS开发者身份成功,下载统信UOS继续折腾操作系统,最后在硬盘上安装了3个操作系统。
在x86笔记本Win10下,尝试在VMWare中安装UOS,然后把项目放入虚拟机,把现有功能调通。
2020-08-23
折腾操作系统,做乱七八糟的实验。反反复复轮流重新安装Fedora28和Loongnix。
2020-08-22
中断了半年的开发继续开始,决定晚上不上网刷帖灌水,尽量抽出时间完成这个软件。
由于上次中断开发时,正在对一些功能进行调试,于是程序重新运行起来什么都不对。梳理了一遍代码,看懂了自己半年前写的东西,重新把已有的功能跑通。
————————————————————————————
开发中断6个月+
————————————————————————————
2020-02-15 左右
公司复工,远程上班,加上孩子网课和作业,精力耗尽,开发中断。
此时完成了在预览界面中编辑图层,以及对其它一些功能的完善。
2020-02-10 左右
开始陪伴孩子上网课,自由时间锐减。
基本完成主界面搭建、选择截图区域的界面、屏幕截图、帧率控制、图像合成和预览、基本的图层管理等。
这段时间的开发,Qt的跨平台特性作用很大。另外就是学习了x11的一些API,完成了屏幕载取相关功能。
虽然使用的截图API与SimpleScreenRecorder这款软件使用的相同,但我对截图数据的缓存和使用方式和它不同,应该更有效率一些。
x11截屏性能不高,可我对Linux还不熟,暂时没有找到其它的截屏相关的技术资料,打算以后读一读OBS的代码,看看它有没有什么不同。
2020-02-01 左右
项目开始。初始开发环境:龙芯3A4000 + 龙梦Fedora28 + Qt5.x。
我把龙芯3A4000电脑原配的AMD R5 230显卡更换成了AMD RX580,龙芯官方的loongnix系统暂时不能驱动它。相对来说龙梦Fedora28对个人用户更友好,虽然对各种办公设备的驱动可能不如loongnix完善,但对个人电脑常见的硬件能够支持得更好一些。