Welcome, Habr!
We continue the cycle of articles about programming camcorders in Linux. In the first part
[1] , we examined the mechanism for opening and reading the primary parameters of a video device. A simple utility catvd was written. Today we will expand the functionality of our small program.
but first you need to write a wrapper for the ioctl function.
Xioctl method codeint videodevice::xioctl(int fd, int request, void *arg) { int r; r = ioctl (fd, request, arg); if(r == -1) { if (errno == EAGAIN) return EAGAIN; stringstream ss; ss << "ioctl code " << request << " "; errno_exit(ss.str()); } return r; }
This wrapper allows you to interrupt the program if there was an error and show the message.
Let's try to read the picture from the camera and save it to a file.
GetFrame method code void videodevice::getFrame(string file_name) { initMMAP(); startCapturing(); long int i = 0; for (;;) { if(readFrame(file_name)) break; i++; } cout << "iter == " << i << endl; stopCapturing(); freeMMAP(); }
method readFrame - is responsible for reading and processing the resulting image.
methods initMMAP (), freeMMAP () - create / clear device memory buffer.
methods startCapturing (), stopCapturing () - enable / disable streaming mode in a video device. The presence of these functions in the camera can be checked with the flag V4L2_CAP_STREAMING
[*] .
')
Let's sort the initMMAP method
InitMMAP Method Code void videodevice::initMMAP() { struct v4l2_requestbuffers req; req.count = 1; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; xioctl(fd, VIDIOC_REQBUFS, &req); devbuffer = (buffer*) calloc(req.count, sizeof(*devbuffer)); struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; xioctl(fd, VIDIOC_QUERYBUF, &buf); devbuffer->length = buf.length; devbuffer->start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (devbuffer->start == MAP_FAILED) errno_exit("mmap"); }
VIDIOC_REQBUFS
[↓] function allows to initialize the memory buffer inside the device. The v4l2_requestbuffers structure sets initialization parameters
struct v4l2_requestbuffers { __u32 count;
After the buffer has been initialized, it must be mapped to the memory area.
The function VIDIOC_QUERYBUF
[↓] allows you to read the buffer parameters that will be used to create a memory-mapping area. The structure of v4l2_buffer is large, I will describe the required fields:
struct v4l2_buffer { // VIDIOC_QUERYBUF __u32 index; // ( v4l2_requestbuffers.cout > 1) __u32 type; // ( v4l2_requestbuffers.type) // VIDIOC_QUERYBUF memory-mapping union { __u32 offset; // } m; __u32 length; // };
The system function mmap ()
[3] allows you to map a file or area of ​​device memory into RAM. To use mmap () you need to connect
<sys/mman.h>
Next, you need to switch the camera to capture mode.
StartCapturing method code void videodevice::startCapturing() { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; xioctl(fd, VIDIOC_QBUF, &buf); enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; xioctl(fd, VIDIOC_STREAMON, &type); }
The function VIDIOC_QBUF
[↓] puts the buffer in the processing queue of the device driver. The fields used are the same as for VIDIOC_REQBUFS or VIDIOC_QUERYBUF.
The VIDIOC_STREAMON
[↓] function turns the camera into capture mode.
The camera is now turned on and captures images. But still need to get the picture.
ReadFrame method code int videodevice::readFrame(string file_name) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (xioctl(fd, VIDIOC_DQBUF, &buf) == EAGAIN) return 0; buffer *temp = devbuffer; FILE *out_file = fopen(file_name.c_str(),"w"); fwrite(temp->start,temp->length,1,out_file); fclose(out_file); return 1; }
The VIDIOC_DQBUF
[↓] function releases the buffer from the driver's processing queue. As a result, we can get the error EAGAIN. There is nothing dangerous in this, you need to call VIDIOC_DQBUF again. This is because the driver is still processing the request and cannot clear the buffer from the queue. With the successful implementation of this function, we get in the "hands" of our picture. At the very beginning of the article, an iterator was added to the code. The iterator allows you to track how many iterations the idle cycle through to the successful execution of VIDIOC_DQBUF.
Compilation
$ cmake . $ make
The output of the program is the following
$./getimage Open device /dev/video0 Init mmap Start capturing read frame from buffer and write to file iter == 831013 stop Capturing free mmap Close device /dev/video0
From “iter == 831013” it can be seen that the picture is dropped into the buffer for quite a long time. To speed up, you can use several buffers and pull the image from the first free one, etc.
Today was considered the initialization of the memory buffer and reading pictures from it. The image is saved in raw format. You can open the program Shotwell. In the next article, we will discuss the output to the image in texture (via SDL2), some image formats and camera settings are affected.
Resources used in the article:
- Work with usb video camera in Linux. Part 1
- video for Linux API
- more about mmap ()
References to used functions:
Source