FFMPEG capture data from USB camera


foreword

In the process of learning FFMPEG, we need to obtain image data, which can be downloaded directly, but in the process of processing, we need data in different formats. There happens to be a usb camera, how do we get images from the usb camera. In Android, the underlying device management is generally managed through the v4l2 driver framework. Here we try to use v4l2 to control the usb camera to achieve data collection.


1. What is v4l2?

V4L2 is the abbreviation of Video for linux2, which is the kernel driver for video devices in linux. In Linux, a video device is a device file, which can be read and written like an ordinary file. The camera is under /dev/video*, and if there is only one video device, it is usually /dev/video0. If you use a series of callback functions to achieve these functions. Like setting the camera frequency, frame rate, video compression format and image parameters, etc., the process is as follows
The use process of v4l2

2. Specific practice

1. Initialize the camera

The code is as follows (example):

/**
 * @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. Memory mapping

The code is as follows (example):

/**
 * @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. Start collecting

The code is as follows (example):

/**
 * @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. Write to file

The code is as follows (example):

/**
 * @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. Close code stream

The code is as follows (example):

/**
 * @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. Unmap

The code is as follows (example):

/**
 * @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. Turn off the camera

The code is as follows (example):

/**
 * @brief release_camera  
 */
void release_camera()
{
    
    
    printf("Turn off the device\n");
    close(fd);
}

8. Main program

The code is as follows (example):

/**
 * @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;
}

Summarize

This is mainly to record the use of using v4l2 to capture the usb camera. In the middle, the use of libx264 for encoding was interspersed, but the card owner is still debugging, so remember to record it later.

Guess you like

Origin blog.csdn.net/qq_38750519/article/details/120922372