如何用FFmpeg API采集摄像头视频和麦克风音频,并实现录制文件的功能 |
您所在的位置:网站首页 › 音频视频采集录制设备 › 如何用FFmpeg API采集摄像头视频和麦克风音频,并实现录制文件的功能 |
之前一直用Directshow技术采集摄像头数据,但是觉得涉及的细节比较多,要开发者比较了解Directshow的框架知识,学习起来有一点点难度。最近发现很多人问怎么用FFmpeg采集摄像头图像,事实上FFmpeg很早就支持通过DShow获取采集设备(摄像头、麦克风)的数据了,只是网上提供的例子比较少。如果能用FFmpeg实现采集、编码和录制(或推流),那整个实现方案就简化很多,正因为这个原因,我想尝试做一个FFmpeg采集摄像头视频和麦克风音频的程序。经过一个星期的努力,终于做出来了。我打算把开发的心得和经验分享给大家。我分三部分来讲述:首先第一部分介绍如何用FFmpeg的官方工具(ffmpeg.exe)通过命令行来枚举DShow设备和采集摄像头图像,这部分是基础,能够快速让大家熟悉怎么用FFmpeg测试摄像头采集;第二部分介绍我写的采集程序的功能和用法;第三部分讲解各个模块包括采集、编码、封装和录制是如何实现的。 1.用命令行枚举采集设备和采集数据打开Cmd命令行控制台,进入FFmpeg的Bin目录,输入如下命令: ffmpeg -list_devices true -f dshow -i dummy 则在我的机器上显示如下结果: 在上面的命令行窗口中列出了两个设备,一个是视频采集设备,另外是一个音频采集设备。另外,我们发现:音频设备的名称有乱码,因为其中有中文名称,后面在讲到用API采集数据的时候会提到解决这个问题的方法。 接着我们输入另外一个命令行: ffmpeg -list_options true -f dshow -i video="USB 2861 Device" 这个命令行的作用是获取指定视频采集设备支持的分辨率、帧率和像素格式等属性,返回的是一个列表,结果如下: 这里我们看到采集设备支持的最大分辨率是720x576,输出像素格式是yuyv422,支持的帧率为29.97和25FPS。 下面我们执行另外一条命令,将摄像头的图像和麦克风的音频录制保存成一个文件。命令如下: ffmpeg -f dshow -i video="USB 2861 Device" -f dshow -i audio="线路 (3- USB Audio Device)" -vcodec libx264 -acodec aac -strict -2 mycamera.mkv 上面的命令行用video=指定视频设备,用audio=指定音频设备,后面的参数是定义编码器的格式和属性,输出为一个名为mycamera.mkv的文件。 命令运行之后,控制台打印FFmpeg的运行日志,按“Q”键则中止命令。 这里有些读者可能会问:采集设备不是支持多个分辨率吗?怎么设置采集时用哪一种分辨率输出?答案是用“-s”参数设置,若在上面的命令行加上“-s 720x576”,则FFmpeg就会以720x576的分辨率进行采集,如果不设置,则以默认的分辨率输出。 注意:如果你运行上面命令ffmpeg报如下错误:Could not run filter Video=XXX:Input/output error 则说明该版本的ffmpeg不支持该采集设备。这是由于旧版本的FFmpeg一个Bug引起的,不支持需要连接crossbar连接的视频采集设备(详情可参考这个帖子:https://ffmpeg.zeranoe.com/forum/viewtopic.php?t=722)。新版本的FFmpeg(avdevice-58)修复了这个问题。所以如果你遇到这个问题,可以通过升级FFmpeg来解决。 好,关于命令行的内容就介绍完了。 2.采集程序的使用这个程序叫“AVCapture”,能从视频采集设备(摄像头,采集卡)获取图像,支持图像预览;还可以采集麦克风音频;支持对视频和音频编码,支持录制成文件。这是一个MFC开发的窗口程序,界面比较简洁,如下图: 开始采集前需要选择设备,点击文件菜单的“打开设备”,弹出一个设备选择对话框,如下图所示: 在对话框里选择任意一个视频设备和音频设备,如果想启用某种设备,必须勾选右边的“启用”选项,但如果只需要用其中一种采集设备,则可以把其中一个禁用掉。 按“确定”则开始采集数据了。视频和音频会编码后保存到一个文件中,这个文件的路径是在配置文件中设置的,打开程序目录下的Config.ini文件,则显示如下字段: [Client] file_path = D:\camera.mkv File_path就是录制文件的路径。 采集的图像默认显示到中间的窗口中,如果不想预览,可以在主菜单栏的“编辑”菜单中取消勾选“预览视频”。 3.功能模块实现该采集程序实现了枚举采集设备,采集控制、显示图像、视频/音频编码和录制的功能,其中输入(Input)、输出(Output)和显示(Paint)这三个模块分别用一个单独的类进行封装:CAVInputStream,CAVOutputStream,CImagePainter。CAVInputStream负责从采集设备获取数据,提供接口获取采集设备的属性,以及提供回调函数把数据传给上层。CAVOutputStream负责对采集的视频和音频流进行编码、封装,保存成一个文件。而CImagePainter则用来显示图像,使用了GDI绘图,把图像显示到主界面的窗口。 3.1 枚举采集设备采集前我们需要先选择设备,把所有的设备名称列出来,其中一个方法可以用第一节介绍的运行ffmpeg命令行工具来列举,但是这样有两个问题:第一,假如设备名称带中文,则显示的名称有乱码,因此,我们不知道它真实的名称。第二,ffmpeg没有API返回系统中安装的采集设备列表,虽然FFmpeg提供了API把设备名称列举出来,但是是打印到控制台的,不是通过参数来返回,如下面这段代码只能打印输出结果到控制台。但是对于窗口界面程序,没有控制台,怎么获取命令行结果呢? [cpp] view plain copy AVFormatContext *pFmtCtx = avformat_alloc_context(); AVDictionary* options = NULL; av_dict_set(&options, "list_devices", "true", 0); AVInputFormat *iformat = av_find_input_format("dshow"); //printf("Device Info=============\n"); avformat_open_input(&pFmtCtx, "video=dummy", iformat, &options); //printf("========================\n"); 我用了一种最传统的做法来解决,就是通过Directshow的COM接口来枚举设备,工程里面的EnumDevice接口就实现了枚举设备的功能,函数原型如下: [cpp] view plain copy //枚举指定类型的所有采集设备的名称 ENUMDEVICE_API HRESULT EnumDevice(CAPTURE_DEVICE_TYPE type, char * deviceList[], int nListLen, int & iNumCapDevices); 当然,如果读者用的采集设备是固定一种,那么可以固定采集设备的名称,这样做可以省点事。 3.2 注册FFmpeg库[cpp] view plain copy av_register_all(); avdevice_register_all(); 这两个API可以在程序的构造函数和窗口初始化里面调用。 3.3 打开输入设备首先需要指定采集设备的名称。如果是视频设备类型,则名称以“video=”开头;如果是音频设备类型,则名称以“audio=”开头。调用avformat_open_input接口打开设备,将设备名称作为参数传进去,注意这个设备名称需要转成UTF-8编码。然后调用avformat_find_stream_info获取流的信息,得到视频流或音频流的索引号,之后会频繁用到这个索引号来定位视频和音频的Stream信息。接着,调用avcodec_open2打开视频解码器或音频解码器,实际上,我们可以把设备也看成是一般的文件源,而文件一般采用某种封装格式,要播放出来需要进行解复用,分离成裸流,然后对单独的视频流、音频流进行解码。虽然采集出来的图像或音频都是未编码的,但是按照FFmpeg的常规处理流程,我们需要加上“解码”这个步骤。 [cpp] view plain copy int i; m_pInputFormat = av_find_input_format("dshow"); ASSERT(m_pInputFormat != NULL); if(!m_video_device.empty()) { int res = 0; string device_name = "video=" + m_video_device; string device_name_utf8 = AnsiToUTF8(device_name.c_str(), device_name.length()); //转成UTF-8,解决设备名称包含中文字符出现乱码的问题 //Set own video device's name if ((res = avformat_open_input(&m_pVidFmtCtx, device_name_utf8.c_str(), m_pInputFormat, &device_param)) != 0) { ATLTRACE("Couldn't open input video stream.(无法打开输入流)\n"); return false; } //input video initialize if (avformat_find_stream_info(m_pVidFmtCtx, NULL) streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { m_videoindex = i; break; } } if (m_videoindex == -1) { ATLTRACE("Couldn't find a video stream.(没有找到视频流)\n"); return false; } if (avcodec_open2(m_pVidFmtCtx->streams[m_videoindex]->codec, avcodec_find_decoder(m_pVidFmtCtx->streams[m_videoindex]->codec->codec_id), NULL) |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |