从0独自写完整个录制系统,连写带学,正则统计2000行,看来还是有一点代码量,记录一下遇到的坑。
大坑::FFMPEG
首先整个录制系统的核心就是音视频处理。目前市面上所有的音视频处理基本都用到了开源库FFMPEG。我们直接使用这个库入手看起来也是比较明智的。
FFMPEG的大多数知识都来源于已经去世的雷博士的博客http://blog.csdn.net/leixiaohua1020?viewmode=contents。不得不说,雷博在这个方向上为太多的人做了入门介绍、翻译的工作。万分感谢!
基本上所有的例程都是一个文件写到尾,一个函数括到头。但对于我们需要N(N>3)路1080P实时编码、录制、预览,还包括一些小的功能需求,例如旋转、缩放、水印。显然这样的代码不能满足要求。于是我找了一些开源代码,最终锁定现在最火的OBS开源串流工具,OBS中也附带了录制的功能,但不能完全满足我们的需求。OBS中也使用了FFMPEG作为编解码的工具,其中分离分解了FFMPEG的数据结构,方便多线程编码,这样的思想帮我实现了最终的功能。
从一开始了解音视频后,并没有直接去了解这些基础性的东西,而是直接对例程更改,实现了一些简单的功能。但最后发现要想完全实现这个系统,还是得对这些东西进行一些系统的了解。
视频像素格式
从原来的基础认知中,以为.MP4 | .MKV | .RMVB这些就是视频的格式。其实并不是,准确的来说,这些只是容器。用于存放一些不同通道的数据,例如视频、音频、字幕等等。而现在我理解的真正的视频是以一定规范存储的编码的流数据。例如H.264码流。对于视频原始码流,是以YUV或者RGB像素格式存放的原始的视频帧,但很无奈的是,这样的数据十分庞大,相当于每秒1920*1080*32(RGBA)*30(帧) = 1.9G字节的数据。这样庞大的数据是根本无法存储的。所以在此颜色之上,出现了根据一些压缩手段。这就是我们一般说的编码Encoder
。
对于从摄像头获取到的数据,我们先通过解码器将其解码为特定的原始码流,然后送入编码器进行编码,最后存储在文件中。在这里,我遇到了一些问题。
一开始并不能理解像素格式的含义还有跟编码器的关系。对于H.264编码器,他只接受特定的某几个像素格式,我们这里用了比较大众化的YUV420P,并非我所直观了解到的以RGB颜色通道存储的原始码流,这给我开始的想法带来了很多困扰。
YUV格式是原来用于兼容彩色和黑白电视机的通用像素格式,他把亮度信息Y和两个色差信号U\V分开储存。节省了带宽,而且对于黑白电视机来说,只需要播放Y分量,放弃掉U\V分量即可播放。对于YUV420P,每4个像素点的亮度信息共享一堆UV信息,这样也减少了数据量。
一开始理所当然的将解码后的原始码流直接送入编码器,发现并不能编码。就是这样像素格式的问题。理解以后,利用FFMPEG自带的转换接口即可实现完整编码。这是我在入坑后解决的第一个重大问题。
音频采样格式
在解决了这个问题后,随之而来的就是音频编码的问题。同样的,原始音频码流也有特定的采样格式。我大学中接触到的数电模电还有信号分析中的一些记忆片段帮助我迅速解决了这个问题。音频采样格式有
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16,