FFMPEG学习 从USB摄像头捕获数据
前言
在学习FFMPEG过程中,需要获取图像数据,简单一点可以直接取下载,但是我们在处理过程中,需要不同格式的数据,正好有个usb摄像头,我们如何从usb摄像头中获取图像。在安卓中,底层的设备管理一般通过v4l2驱动框架来管理,这里我们尝试使用v4l2来控制usb摄像头来实现数据采集。
一、v4l2是什么?
V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。要是用一系列的回调函数来实现这些功能。像设置摄像头的频率、帧频、视频压缩格式和图像参数等,其流程如下
二、具体实践
1.初始化摄像头
代码如下(示例):
/**
* @brief init_camera Initialize camera device properties
* @param dev Equipment name
* @return
*/
int init_camera(const char* dev)
{
fd = open(dev, O_RDWR);
if(fd < 0){
printf("open \"%s\" error\n", dev);
return -1;
}
/**
* Query device properties
*/
struct v4l2_capability cap;
int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if (ret < 0) {
printf("VIDIOC_QUERYCAP error\n");
return -1;
}
printf("Driver name : %s\n",cap.driver);
printf("Device name : %s\n",cap.card);
printf("Bus information : %s\n",cap.bus_info);
printf("Drive version number : 0x%x\n",cap.version);
if(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE){
printf("Video capture device:\n");
if(cap.capabilities & V4L2_CAP_STREAMING){
printf("\tSupport video stream capture\n");
}else{
printf("Video stream capture is not supported\n");
}
}else {
printf("Non video stream capture device\n");
return -1;
}
#if 1
printf("\tQuery supported image formats:\n");
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
{
printf("\t\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
#endif
/*Format set*/
//struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//Camera buffer
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//Pixel format (this parameter needs to be set according to the actual equipment situation)
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; //OK
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; //OK YU12
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
{
printf("Format: %d failed\n",fmt.fmt.pix.pixelformat);
return -1;
}
printf("Set the format of the acquired image V4L2_PIX_FMT_YUV420 success!\n");
return 0;
}
2.内存映射
代码如下(示例):
/**
* @brief mmap_buffer Allocate user buffer memory and establish memory mapping
* @return
*/
int mmap_buffer()
{
printf("Allocate user space buffer\n");
//struct BUFTYPE *usr_buf;
usr_buf = (struct BUFTYPE *)calloc(buf_num, sizeof(struct BUFTYPE));
if (!usr_buf) {
printf("calloc \"frame buffer\" error : Out of memory\n");
return -1;
}
printf("Allocate kernel buffers (including the number of frame buffers)\n");
struct v4l2_requestbuffers req;
req.count = buf_num; //num of frame buf
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //buf type of videocap
req.memory = V4L2_MEMORY_MMAP; //mem mmap type
if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
printf("VIDIOC_REQBUFS assignt kernel buffers failed \n");
return -1;
}
/*mmap kernel buffers to user buffers*/
printf("Mapping kernel buffer to user buffer\n");
unsigned int i = 0;
for(i = 0; i < buf_num; ++i)
{
/*query kernel buffers*/
struct v4l2_buffer v4l2_buf;
memset(&v4l2_buf, 0, sizeof(v4l2_buf));
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
v4l2_buf.index = i;
if(ioctl(fd , VIDIOC_QUERYBUF, &v4l2_buf) < 0)
{
printf("VIDIOC_QUERYBUF failed\n");
return -1;
}
/* establish mmaping
* keep v4l2_buf.index and usr_buf.index the same
* when kernel buffer DQbug to queue,get kernel buffer index by user buffers index,
* then get the index data user want
*/
usr_buf[i].length = v4l2_buf.length;
usr_buf[i].start = (char *)mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buf.m.offset);
if (MAP_FAILED == usr_buf[i].start)
{
//mmap failed
printf("mmap failed: %d\n",i);
return -1;
}else
{
if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0)
{
// mmap success,qbuf to queue
printf("VIDIOC_QBUF failed\n");
return -1;
}
}
}
printf("Mapping kernel buffer to user buffer success\n");
return 0;
}
3.开始采集
代码如下(示例):
/**
* @brief stream_on
* @return
*/
int stream_on()
{
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
{
printf("VIDIOC_STREAMON failed\n");
return -1;
}
printf("stream on success!\n");
return 0;
}
4.写入文件
代码如下(示例):
/**
* @brief write_frame read a frame
* @return return frame index,error for -1
*/
int write_frame(int index)
{
int writenum = 0;
struct v4l2_buffer v4l2_buf;
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
v4l2_buf.index = index;
if(ioctl(fd, VIDIOC_DQBUF, &v4l2_buf) < 0) // Kernel buffer out of queue
{
printf("VIDIOC_DQBUF failed, dropped frame\n");
return -1;
}
printf("VIDIOC_DQBUF size[%d]\n",v4l2_buf.bytesused);
/*
* mmp kernel buffer to user buffer,get data by user buffer
*/
char buffer[256];
sprintf(buffer,"/data/V4L2_PIX_FMT_YVYU_%x-%d.yuv",fmt.fmt.pix.pixelformat,v4l2_buf.index); //defined by fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
int file_fd = open(buffer,O_RDWR | O_CREAT); // didnot save this frame
printf("open or creat a pic success\n");
if(file_fd > 0)
{
//printf("data [%d] start:%s\n",v4l2_buf.index,usr_buf[v4l2_buf.index].start);
//printf("saving the %d frame image , size:%d\n",v4l2_buf.index,v4l2_buf.bytesused);
//itenum = write(file_fd,usr_buf[v4l2_buf.index].start,v4l2_buf.bytesused);
printf("saving the [%d] frame image , size[%d]\n",v4l2_buf.index,v4l2_buf.bytesused);
writenum = write(file_fd,usr_buf[v4l2_buf.index].start,v4l2_buf.bytesused);
printf("write %d byte\n",writenum);
if (0 < writenum )
{
printf("Write successful %d byte\n",writenum);
}
else
{
printf("Write failed%d\n",writenum);
}
close(file_fd);
}
else
{
printf("file_fd <= 0");
return -1;
}
if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0) //Buffer rejoining to queue
{
printf("VIDIOC_QBUF failed, dropped frame\n");
return -1;
}
return v4l2_buf.index;
}
5.关闭码流
代码如下(示例):
/**
* @brief stream_off
* @return
*/
int stream_off()
{
printf("Turn off video streaming\n");
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(fd,VIDIOC_STREAMOFF,&type) == -1)
{
printf("Fail to ioctl 'VIDIOC_STREAMOFF'");
return -1;
}
return 0;
}
6.解除映射
代码如下(示例):
/**
* @brief unmap_buffer
* @return
*/
int unmap_buffer()
{
printf("Unmap kernel buffer to user buffer\n");
unsigned int i = 0;
for(i = 0; i < buf_num; i++)
{
int ret = munmap(usr_buf[i].start, usr_buf[i].length);
if (ret < 0)
{
printf("munmap failed\n");
return -1;
}
}
free(usr_buf);
return 0;
}
7.关闭摄像头
代码如下(示例):
扫描二维码关注公众号,回复:
14743485 查看本文章
/**
* @brief release_camera
*/
void release_camera()
{
printf("Turn off the device\n");
close(fd);
}
8.主程序
代码如下(示例):
/**
* @brief release_camera
*/
void release_camera()
{
printf("Turn off the device\n");
close(fd);
}
int main(void)
{
int ret = init_camera("/dev/video1");
if(ret < 0){
printf("init_camera error\n");
return -1;
}
#if 1
ret = mmap_buffer();
if(ret < 0){
printf("mmap_buffer error\n");
return -1;
}
ret = stream_on();
if(ret < 0){
printf("stream_on error\n");
return -1;
}
int i=0;
for(i=0;i<5;i++)
{
write_frame(i);
encode_aframe(i);
}
ret = stream_off();
if(ret < 0){
printf("stream_off error\n");
return -1;
}
ret = unmap_buffer();
if(ret < 0){
printf("unmap_buffer error\n");
return -1;
}
#endif
release_camera();
return 0;
}
总结
这里主要是记录了使用v4l2采集usb摄像头的使用,中间本来穿插了使用libx264进行编码,但是卡主了,还在调试中,后面记得再记录。