用Directshow从摄像头(采集卡设备)采集图像并录制成AVI、MP4文件的实现方案

    之前在一家公司做过一个采集卡的SDK,要求支持基于Directshow架构的视频采集设备(采集卡、USB摄像头)和音频采集设备(麦克风或AV音频输入),能同时采集图像和声音,并把视频和音频编码之后录制成AVI或MP4文件;采集过程中可以预览图像,并且支持在任意时间段录制(即可以在不同的时间点开始录,但需要录完一个才能录下一个);录制的视音频必须同步。

   我再补充一些条件,从视频要求编码成H264;音频编码成MP3或AAC。当录制成AVI时,音频用MP3编码;当录制成MP4时,音频用AAC编码。录制成什么格式由用户指定。

   需求看上去很简单,但是怎么实现呢?我比较熟悉Directshow技术,所以开始就想到用Filter来实现各种子功能,比如视频编码器可以用FFDShow Video Encoder Filter、音频编码用AAC Encode和MP3 Encode Filter(这两个Filter有现成的),而录制AVI,MP4也有相应的Muxer。所以,初步构想的实现流程可以用下面的Directshow链路图表示:

用Directshow从摄像头(采集卡设备)采集图像并录制成AVI、MP4文件的实现方案

                                               图1:采集音视频,编码,然后封装成指定容器格式

 注意:上面的链路图应该分两种情况,两种情况下的音频编码和录制封装格式是不同的,所以Audio Encoder和Muxer在不同情况下用不同的Filter。

   其中,录AVI文件的Graph链路图应该如下那样:

用Directshow从摄像头(采集卡设备)采集图像并录制成AVI、MP4文件的实现方案

                                           图2:采集音视频,录制AVI的链路图

   但是要完成需求上的功能还没有这么简单!首先,功能需求要同时预览和采集,也就是说上面还要加Video Renderer和SmartTee Filter,Smart Tee用来分出两个Output Pin,一个Pin连后面的编码、录制文件分支,而另外一个Pin连视频渲染器来显示图像。第二,功能上要求支持动态录制文件,即不停止采集任务可以随时地录制文件,要支持这个功能就比较困难了,上面“经典“的合成文件的链路已经不能满足要求。因为Muxer和Writer是停止采集的时候才写入元数据,并关闭文件句柄,要生成另外一个文件,必须先停止Filter Graph运行,然后调用File Writer的IFileSinkFilter接口的SetFileName方法重新指定一个文件。Filter Graph在运行时,系统的File Writer不允许改变录制的文件名,故不能中断写一个文件后再写另一个;另外AVI和MP4不像TS那种流格式(中间任何片段都可以播放),不能连一个Dump Filter直接把二进制数据写到文件中。

    但是实现方法还是有的,只是复杂一些,我们可以写一个AVI Muxer和MP4 Muxer(兼有File Writer的写文件功能),这样封装容器和写文件的操作都是自己做就可以实现动态生成不同文件。换句话说,我们可以写一个自定义的Renderer Filter,实现封装MP4容器和写AVI的功能,提供接口给外部去设置新的录制文件路径。至于怎么写AVI和MP4文件,找相应的库就行了。写AVI比较容易实现,因为微软提供了一组写AVI文件的API;而写MP4文件要借助第三方库,比如MP4Box,ffmpeg。另外,我们要把它做成一个Directshow Renderer Filter(有2个输入Pin,没有输出Pin)需要重载Renderer Filter和Pin的很多方法,需要考虑Filter内部的很多处理细节(比如媒体类型协商、Filter的状态切换)。

     现在我们优化后的方案基本还是像图1的那种流程,只不过AVI/Mp4 Muxer(Renderer Filter)由我们自己来实现。但是,我们不要忘了其中一个需求:支持同时预览和采集。很明显,图1的流程图不能满足这个需求,它没有对视频进行预览,那我们需要一个分支链路来处理,像前面说的,Video Capture Filter后面加一个Smart Tee和Video Renderer,Smart Tee的一个输出Pin连Video Renderer,而另一个输出Pin后面的连接链路是:Video Encoder-》Muxer。不过,如果这样构建,实际的Filter Graph链路图就变得复杂很多,如果以后还要加上回放音频的功能,那又要加多一个分支处理。并且,预览视频、打开声音并不是每种情况下都需要的,如果用户不预览和回放声音,我们把Video Renderer,Audio Renderer一开始就插入到Graph就显得没有必要了。所以,前面这种方案很不灵活。为了增加灵活性,同时可以兼顾各种情形,我们可以把实现方案继续优化一下:后面不要用Muxer Filter,将视频和音频的链路分别连一个Renderer Filter,这个Renderer Filter只有一个输入Pin,它只有回调数据的功能。只要把数据传给应用层,那上层对数据怎么处理都行,比如做预览、编码和封装文件,这些操作都可以放在回调函数里面执行。这个实现方案的连接链路图如下:

用Directshow从摄像头(采集卡设备)采集图像并录制成AVI、MP4文件的实现方案

                                                图3:每个采集Filter后面连一个Render Filter回调数据给外部处理

   上面的Stream Sender只是一个简单的Render Filter,也可以用系统的Sample Grabber代替。

   这种方案的优点是:架构简单,扩充功能灵活,由于用到比较少的Filter,降低了构建Filter Graph的复杂度,同时将着重点放在业务逻辑的实现上,使开发员从复杂的Directshow Filter开发工作中解放出来。

   具体怎么实现预览、编码、录制文件? 我向大家推荐一个开源项目:MPEG Recorder。MPEG Recorder 是 Windows 平台下用来实时录制和压缩视频输入数据的软件,使用 MFC、DirectX、FFMPEG、MPEG4IP 等技术。程序支持采集音视频、对流进行编码、生成 avi 和 mp4 格式的 文件。下面是在这个软件的截图:

用Directshow从摄像头(采集卡设备)采集图像并录制成AVI、MP4文件的实现方案

项目地址:https://sourceforge.net/projects/mpeg-recorder/