FFMPEG 从USB摄像头捕获数据


前言

在学习FFMPEG过程中,需要获取图像数据,简单一点可以直接取下载,但是我们在处理过程中,需要不同格式的数据,正好有个usb摄像头,我们如何从usb摄像头中获取图像。在安卓中,底层的设备管理一般通过v4l2驱动框架来管理,这里我们尝试使用v4l2来控制usb摄像头来实现数据采集。


一、v4l2是什么?

V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。要是用一系列的回调函数来实现这些功能。像设置摄像头的频率、帧频、视频压缩格式和图像参数等,其流程如下
v4l2的使用流程

二、具体实践

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进行编码,但是卡主了,还在调试中,后面记得再记录。

猜你喜欢

转载自blog.csdn.net/qq_38750519/article/details/120922372