本文讲述在linux下,如何使用ffmpeg采集摄像头yuv数据,并保存为文件。
分为4个部分进行讲解
使用ffmpeg采集摄像头数据,需要具备两个条件:
首先,需要有一个摄像头。我这里使用的是USB摄像头,直接插在电脑主机之后。
其次,需要安装了ffmpeg。我这里是自己编译的ffmpeg。
首先,我们需要知道摄像头的位置。在linux下面,摄像头抽象成了一个设备文件**"/dev/video0"** ,这就是我们访问摄像头的位置。
其次,我们需要知道是通过什么驱动程序,来访问这个摄像头硬件的。linux下面,可以通过驱动video4linux2 来进行访问。
那么,使用如下ffmpeg命令,就可以采集yuv数据了。
ffmpeg -f video4linux2 -s 640x480 -i /dev/video0 out.avi
然后通过 ffplay来播放这个视频就可以。
ffplay out.avi
但是,我们需要采集的是yuv数据,还需要指定yuv的格式才行。这样,我们添加一个格式pix_fmt。
ffmpeg -f video4linux2 -s 640x480 -pix_fmt yuyv422 -i /dev/video0 out.yuv
我这里,摄像头是支持yuyv422格式的,所以我指定了pix_fmt为yuyv422,然后使用ffplay播放一下。
ffplay -s 640x480 -pix_fmt yuyv422 out.yuv
可以看到,播放正常。那就可以后续使用代码进行开发了。
使用ffmpeg代码,采集yuv数据,分成3步:
打开设备,主要通过函数avformat_open_input 来进行,通过这个函数的参数,指定打开的设备路径“/dev/video0”,使用的驱动"video4linux2",以及指定相应的格式pix_fmt为yuyv422以及分辨率为640x480。
AVFormatContext *fmt_ctx = NULL;
AVDictionary *options = NULL;
char *devicename = "/dev/video0";
avdevice_register_all();
AVInputFormat *iformat = av_find_input_format("video4linux2");
av_dict_set(&options,"video_size","640x480",0);
av_dict_set(&options,"pixel_format","yuyv422", 0);
avformat_open_input(&fmt_ctx, devicename, iformat, &options);
avformat_close_input(&fmt_ctx);
读取yuv数据,主要是通过av_read_frame来进行的。
int ret = 0;
AVPacket pkt;
while((ret = av_read_frame(fmt_ctx, &pkt)) == 0) {
av_log(NULL, AV_LOG_INFO,
"packet size is %d(%p)\n",
pkt.size, pkt.data);
av_packet_unref(&pkt); //release pkt
}
保存文件,直接通过fwrite函数写入即可。
char *out = "out.yuv";
FILE *outfile = fopen(out, "wb+");
fwrite(pkt.data, 1, pkt.size, outfile); //614400
fflush(outfile);
fclose(outfile);
#include
#include "libavutil/avutil.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
//@brief
//return
static AVFormatContext* open_dev(){
int ret = 0;
char errors[1024] = {0, };
//ctx
AVFormatContext *fmt_ctx = NULL;
AVDictionary *options = NULL;
//[[video device]:[audio device]]
char *devicename = "/dev/video0";
//register audio device
avdevice_register_all();
//get format
AVInputFormat *iformat = av_find_input_format("video4linux2");
av_dict_set(&options,"video_size","640x480",0);
av_dict_set(&options,"pixel_format","yuyv422", 0);
//open device
if((ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options)) < 0 ){
av_strerror(ret, errors, 1024);
fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errors);
return NULL;
}
return fmt_ctx;
}
void rec_video() {
int ret = 0;
AVFormatContext *fmt_ctx = NULL;
int count = 0;
//pakcet
AVPacket pkt;
//set log level
av_log_set_level(AV_LOG_DEBUG);
//create file
char *out = "out.yuv";
FILE *outfile = fopen(out, "wb+");
//打开设备
fmt_ctx = open_dev();
//read data from device
while((ret = av_read_frame(fmt_ctx, &pkt)) == 0 &&
count++ < 100) {
av_log(NULL, AV_LOG_INFO,
"packet size is %d(%p)\n",
pkt.size, pkt.data);
fwrite(pkt.data, 1, pkt.size, outfile); //614400
fflush(outfile);
av_packet_unref(&pkt); //release pkt
}
__ERROR:
if(outfile){
//close file
fclose(outfile);
}
//close device and release ctx
if(fmt_ctx) {
avformat_close_input(&fmt_ctx);
}
av_log(NULL, AV_LOG_DEBUG, "finish!\n");
return;
}
int main(int argc, char *argv[])
{
rec_video();
return 0;
}
使用命令gcc record_video.c -lavformat -lavutil -lavdevice -lavcodec -o record_video进行编译,然后运行**./record_video**,最后得到一个out.yuv。
然后使用命令
ffplay -s 640x480 -pix_fmt yuyv422 out.yuv
可以看到,正常播放,录制yuv数据成功。
v4l2有一个工具,可以查询摄像头支持的数据格式。我们先安装这个工具,使用如下命令。
sudo apt install v4l-utils
然后使用这个工具,查询我们的摄像头设备。
v4l2-ctl --list-devices
我这里列出了如下内容
USB2.0 Camera: USB2.0 Camera (usb-0000:00:14.0-8):
/dev/video0
说明我这里,只有一个摄像头,然后访问地址为/dev/video0。然后查询一下/dev/video0支持的格式。
sudo v4l2-ctl -d /dev/video0 --list-formats
我这里列出的内容如下
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture
Pixel Format: 'YUYV'
Name : YUYV 4:2:2
就只有这一种,难怪,我想设置成nv12,怎么都不能输出数据。我这摄像头便宜,就只支持这种yuyv422的格式。