Capture an image with V4L2
Brief
As we has seen before, cameras in Linux could be a complex [1] story and you have to watch every step you take to get it right. libcamera [2] does a great job to simplify this in a platform independent way and should be used whenever it's possible.
But not all cameras does have a complex flow-chart. Some cameras (e.g. web cameras) are "self-contained" where the image data goes straight from the camera to the user application, without any detours through different IP blocks for image processing on its way.
The V4L2 framework is perfectly suited to those simple cameras.
When I searched around for a simple example application that explained the necessary steps to just capture images from a camera, I had hard to find what I was looking for. This is my attempt to provide what I failed to find.
V4L2 user space API
Video devices is represented by character devices in a Linux system. The devices shows up as /dev/video* and supports the following operations:
- open() - Open a video device
- close() - Close a video device
- ioctl() - Send ioctl commands to the device
- mmap() - Map memory to a driver allocated buffer
- read() - Read from video device
- write() - Write to the device
The V4L2 API basically relies on a very large set of IOCTL commands to configure properties and behavior of the camera. The whole API is available from the following header: :
1#include <linux/videodev2.h>
Here is a list of the most common IOCTL commands:
- VIDIOC_QUERYCAP - Query a list of the supported capabilities. Always query the capabilities to ensure that the camera supports the buffer mode you intend to use.
- VIDIOC_ENUM_FMT - Enumerate supported image formats.
- VIDIOC_G_FMT - Get the current image format.
- VIDIOC_S_FMT - Set a new image format.
- VIDIOC_REQBUFS - Request a number of buffers that can later be memory mapped by the user application. The application should always check the actual number that are granted as the driver may allocate mor or less than the requested.
- VIDIOC_QUERYBUF - Get buffer information for those buffers earlier requested by VIDIOC_REQBUFS. The information could then be passed to the mmap() system call in order to map that buffer to user space.
- VIDIOC_QBUF - Queue one of the requested buffers to make it available for the driver to fill with image data. Once the buffer is filled, it's no longer available for new data and should be dequeued by the user.
- VIDEOC_DQBUF - Dequeue a filled buffer. The command will block if no buffer is available unless O_NONBLOCK was passed to open().
- VIDIOC_STREAMON - Turn on streaming. Queued buffers will be filled as soon data is available.
- VIDIOC_STREAMOFF - Turn off streaming. This command also flushes the buffer queue.
Buffer management
The V4L2 core maintain two buffer queues internally; one queue (referred to as IN) for incoming (camera->driver) image data and one (referred to as OUT) for outgoing (driver->user) image data.
Buffers are put into the IN queue via the VIDIOC_QBUF command. Once the buffer is filled, the buffer is dequeued from IN and put into the OUT queue, which where the data is available for to the user.
Whenever the user want to dequeue a buffer with VIDIOC_DQBUF, and a buffer is available, it's taken from the OUT queue and pushed to the user application. If no buffer is available the dequeue operation will wait until a buffer is filled and available unless the file descriptor is opened with O_NONBLOCK.
Video data can be pushed to userspace in a few different ways:
- Read I/O - simply perform a read() operation and do not mess with buffers
- User pointer - The user application allocates buffers and provide to driver
- DMA buf - Mostly used for mem2mem devices
- mmap - Let driver allocate buffers and mmap(2) these to userspace.
This post will *only* focus on mmap:ed buffers!
Typical workflow
We will follow these steps in order to acquire frames from the camera:
Query capabilities
VIDIOC_QUERYCAP is used to query the supported capabilities. What is most interesting is to verify that it supports the mode (V4L2_CAP_STREAMING) we want to work with. It's also a good manners to verify that it actually is a capture device (V4L2_CAP_VIDEO_CAPTURE) we have opened and nothing else.
The V4L2 API uses a struct v4l2_capability that is passed to the IOCTL. This structure is defined as follows:
1 /**
2 * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP
3 *
4 * @driver: name of the driver module (e.g. "bttv")
5 * @card: name of the card (e.g. "Hauppauge WinTV")
6 * @bus_info: name of the bus (e.g. "PCI:" + pci_name(pci_dev) )
7 * @version: KERNEL_VERSION
8 * @capabilities: capabilities of the physical device as a whole
9 * @device_caps: capabilities accessed via this particular device (node)
10 * @reserved: reserved fields for future extensions
11 */
12 struct v4l2_capability {
13 __u8 driver[16];
14 __u8 card[32];
15 __u8 bus_info[32];
16 __u32 version;
17 __u32 capabilities;
18 __u32 device_caps;
19 __u32 reserved[3];
20 };
The v4l2_capability.capabilities field is decoded as follows:
1 /* Values for 'capabilities' field */
2 #define V4L2_CAP_VIDEO_CAPTURE 0x00000001 /* Is a video capture device */
3 #define V4L2_CAP_VIDEO_OUTPUT 0x00000002 /* Is a video output device */
4 #define V4L2_CAP_VIDEO_OVERLAY 0x00000004 /* Can do video overlay */
5 #define V4L2_CAP_VBI_CAPTURE 0x00000010 /* Is a raw VBI capture device */
6 #define V4L2_CAP_VBI_OUTPUT 0x00000020 /* Is a raw VBI output device */
7 #define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040 /* Is a sliced VBI capture device */
8 #define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */
9 #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */
10 #define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay */
11 #define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */
12 #define V4L2_CAP_RDS_OUTPUT 0x00000800 /* Is an RDS encoder */
13
14 /* Is a video capture device that supports multiplanar formats */
15 #define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000
16 /* Is a video output device that supports multiplanar formats */
17 #define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000
18 /* Is a video mem-to-mem device that supports multiplanar formats */
19 #define V4L2_CAP_VIDEO_M2M_MPLANE 0x00004000
20 /* Is a video mem-to-mem device */
21 #define V4L2_CAP_VIDEO_M2M 0x00008000
22
23 #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */
24 #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */
25 #define V4L2_CAP_RADIO 0x00040000 /* is a radio device */
26 #define V4L2_CAP_MODULATOR 0x00080000 /* has a modulator */
27
28 #define V4L2_CAP_SDR_CAPTURE 0x00100000 /* Is a SDR capture device */
29 #define V4L2_CAP_EXT_PIX_FORMAT 0x00200000 /* Supports the extended pixel format */
30 #define V4L2_CAP_SDR_OUTPUT 0x00400000 /* Is a SDR output device */
31 #define V4L2_CAP_META_CAPTURE 0x00800000 /* Is a metadata capture device */
32
33 #define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
34 #define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
35 #define V4L2_CAP_META_OUTPUT 0x08000000 /* Is a metadata output device */
36
37 #define V4L2_CAP_TOUCH 0x10000000 /* Is a touch device */
38
39 #define V4L2_CAP_IO_MC 0x20000000 /* Is input/output controlled by the media controller */
40
41 #define V4L2_CAP_DEVICE_CAPS 0x80000000 /* sets device capabilities field */
Example code on how to use VIDIOC_QUERYCAP:
1void query_capabilites(int fd)
2{
3 struct v4l2_capability cap;
4
5 if (-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap)) {
6 perror("Query capabilites");
7 exit(EXIT_FAILURE);
8 }
9
10 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
11 fprintf(stderr, "Device is no video capture device\\n");
12 exit(EXIT_FAILURE);
13 }
14
15 if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
16 fprintf(stderr, "Device does not support read i/o\\n");
17 }
18
19 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
20 fprintf(stderr, "Devices does not support streaming i/o\\n");
21 exit(EXIT_FAILURE);
22 }
23}
Capabilities could also be read out with v4l2-ctl:
1 marcus@goliat:~$ v4l2-ctl -d /dev/video4 --info
2 Driver Info:
3 Driver name : uvcvideo
4 Card type : USB 2.0 Camera: USB Camera
5 Bus info : usb-0000:00:14.0-8.3.1.1
6 Driver version : 6.0.8
7 Capabilities : 0x84a00001
8 Video Capture
9 Metadata Capture
10 Streaming
11 Extended Pix Format
12 Device Capabilities
13 Device Caps : 0x04200001
14 Video Capture
15 Streaming
16 Extended Pix Format
Set format
The next step after we know for sure that the device is a capture device and supports the certain mode we want to use, is to setup the video format. The application could otherwise receive video frames in a format that it could not deal with.
Supported formats can be quarried with VIDIOC_ENUM_FMT and the current video format can be read out with VIDIOC_G_FMT.
Current format could be fetched by v4l2-ctl:
1 marcus@goliat:~$ v4l2-ctl -d /dev/video4 --get-fmt-video
2 Format Video Capture:
3 Width/Height : 320/240
4 Pixel Format : 'YUYV' (YUYV 4:2:2)
5 Field : None
6 Bytes per Line : 640
7 Size Image : 153600
8 Colorspace : sRGB
9 Transfer Function : Rec. 709
10 YCbCr/HSV Encoding: ITU-R 601
11 Quantization : Default (maps to Limited Range)
12 Flags :
The v4l2_format struct is defined as follows:
1 /**
2 * struct v4l2_format - stream data format
3 * @type: enum v4l2_buf_type; type of the data stream
4 * @pix: definition of an image format
5 * @pix_mp: definition of a multiplanar image format
6 * @win: definition of an overlaid image
7 * @vbi: raw VBI capture or output parameters
8 * @sliced: sliced VBI capture or output parameters
9 * @raw_data: placeholder for future extensions and custom formats
10 * @fmt: union of @pix, @pix_mp, @win, @vbi, @sliced, @sdr, @meta
11 * and @raw_data
12 */
13 struct v4l2_format {
14 __u32 type;
15 union {
16 struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
17 struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
18 struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
19 struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
20 struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
21 struct v4l2_sdr_format sdr; /* V4L2_BUF_TYPE_SDR_CAPTURE */
22 struct v4l2_meta_format meta; /* V4L2_BUF_TYPE_META_CAPTURE */
23 __u8 raw_data[200]; /* user-defined */
24 } fmt;
25 };
To set you have to set the v4l2_format.type field to the relevant format.
Example code on how to use VIDIOC_S_FMT:
1int set_format(int fd)
2{
3 struct v4l2_format format = {0};
4 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5 format.fmt.pix.width = 320;
6 format.fmt.pix.height = 240;
7 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
8 format.fmt.pix.field = V4L2_FIELD_NONE;
9
10 int res = ioctl(fd, VIDIOC_S_FMT, &format);
11 if(res == -1) {
12 perror("Could not set format");
13 exit(EXIT_FAILURE);
14 }
15
16 return res;
17}
Request buffers
Next step once we are done with the format preparations we should allocate buffers to have somewhere to store the images.
This is exactly what VIDIOC_REQBUFS ioctl does for you. The command does take a struct v4l2_requestbuffers as argument:
1 struct v4l2_requestbuffers {
2 __u32 count;
3 __u32 type; /* enum v4l2_buf_type */
4 __u32 memory; /* enum v4l2_memory */
5 __u32 capabilities;
6 __u8 flags;
7 __u8 reserved[3];
8 };
Some of these fields must be populated before we can use it:
- v4l2_requestbuffers.count - Should be set to the number of memory buffers that should be allocated. It's important to set a number high enough so that frames won't be dropped due to lack of queued buffers. The driver is the one who decides what the minimum number is. The application should always check the return value of this field as the driver could grant a bigger number of buffers than then application actually requested.
- v4l2_requestbuffers.type - As we are going to use a camera device, set this to V4L2_BUF_TYPE_VIDEO_CAPTURE.
- v4l2_requestbuffers.memory - Set the streaming method. Available values are V4L2_MEMORY_MMAP, V4L2_MEMORY_USERPTR and V4L2_MEMORY_DMABUF.
Example code on how to use VIDIOC_REQBUF:
1int request_buffer(int fd, int count)
2{
3 struct v4l2_requestbuffers req = {0};
4 req.count = count;
5 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
6 req.memory = V4L2_MEMORY_MMAP;
7
8 if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
9 perror("Requesting Buffer");
10 exit(EXIT_FAILURE);
11 }
12
13 return req.count;
14}
Query buffer
After the buffers are allocated by the kernel, we have to query the physical address of each allocated buffer in order to mmap() those.
The VIDIOC_QUERYBUF ioctl works with the struct v4l2_buffer:
1 /**
2 * struct v4l2_buffer - video buffer info
3 * @index: id number of the buffer
4 * @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for
5 * multiplanar buffers);
6 * @bytesused: number of bytes occupied by data in the buffer (payload);
7 * unused (set to 0) for multiplanar buffers
8 * @flags: buffer informational flags
9 * @field: enum v4l2_field; field order of the image in the buffer
10 * @timestamp: frame timestamp
11 * @timecode: frame timecode
12 * @sequence: sequence count of this frame
13 * @memory: enum v4l2_memory; the method, in which the actual video data is
14 * passed
15 * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP;
16 * offset from the start of the device memory for this plane,
17 * (or a "cookie" that should be passed to mmap() as offset)
18 * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR;
19 * a userspace pointer pointing to this buffer
20 * @fd: for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF;
21 * a userspace file descriptor associated with this buffer
22 * @planes: for multiplanar buffers; userspace pointer to the array of plane
23 * info structs for this buffer
24 * @m: union of @offset, @userptr, @planes and @fd
25 * @length: size in bytes of the buffer (NOT its payload) for single-plane
26 * buffers (when type != *_MPLANE); number of elements in the
27 * planes array for multi-plane buffers
28 * @reserved2: drivers and applications must zero this field
29 * @request_fd: fd of the request that this buffer should use
30 * @reserved: for backwards compatibility with applications that do not know
31 * about @request_fd
32 *
33 * Contains data exchanged by application and driver using one of the Streaming
34 * I/O methods.
35 */
36 struct v4l2_buffer {
37 __u32 index;
38 __u32 type;
39 __u32 bytesused;
40 __u32 flags;
41 __u32 field;
42 struct timeval timestamp;
43 struct v4l2_timecode timecode;
44 __u32 sequence;
45
46 /* memory location */
47 __u32 memory;
48 union {
49 __u32 offset;
50 unsigned long userptr;
51 struct v4l2_plane *planes;
52 __s32 fd;
53 } m;
54 __u32 length;
55 __u32 reserved2;
56 union {
57 __s32 request_fd;
58 __u32 reserved;
59 };
60 };
The structure contains a lot of fields, but in our mmap() example, we only need to fill out a few:
- v4l2_buffer.type - Buffer type, we use V4L2_BUF_TYPE_VIDEO_CAPTURE.
- v4l2_buffer.memory - Memory method, still go for V4L2_MEMORY_MMAP.
- v4l2_buffer.index - As we probably have requested multiple buffers and want to mmap each of them we have to distinguish the buffers somehow. The index field is buffer id reaching from 0 to v4l2_requestbuffers.count.
Example code on how to use VIDIOC_QUERYBUF:
1int query_buffer(int fd, int index, unsigned char **buffer)
2{
3 struct v4l2_buffer buf = {0};
4 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5 buf.memory = V4L2_MEMORY_MMAP;
6 buf.index = index;
7
8 if(ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
9 perror("Could not query buffer");
10 exit(EXIT_FAILURE);
11 }
12
13 *buffer = (u_int8_t*)mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
14 return buf.length;
15}
Queue buffers
Before the buffers can be filled with data, the buffers has to be enqueued. Enqueued buffers will lock the memory pages used so that those cannot be swapped out during usage. The buffers remain locked until that are dequeued, the device is closed or streaming is turned off.
VIDIOC_QBUF takes the same argument as VIDIOC_QUERYBUF and has to be populated the same way.
Example code on how to use VIDIOC_QBUF:
1int queue_buffer(int fd, int index)
2{
3 struct v4l2_buffer bufd = {0};
4 bufd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5 bufd.memory = V4L2_MEMORY_MMAP;
6 bufd.index = index;
7
8 if (ioctl(fd, VIDIOC_QBUF, &bufd) == -1) {
9 perror("Queue Buffer");
10 exit(EXIT_FAILURE);
11 }
12
13 return bufd.bytesused;
14}
Start stream
Finally all preparations is done and we are up to start the stream! VIDIOC_STREAMON is basically informing the v4l layer that it can start acquire video frames and use the queued buffers to store them.
Example code on how to use VIDIOC_STREAMON:
1int start_streaming(int fd)
2{
3 unsigned int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
4 if(ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
5 perror("VIDIOC_STREAMON");
6 exit(EXIT_FAILURE);
7 }
8
9 return 0;
10}
Dequeue buffer
Once buffers are filled with video data, those are ready to be dequeued and consumed by the application. This ioctl will be blocking (unless O_NONBLOCK is used) until a buffer is available.
As soon the buffer is dequeued and processed, the application has to immediately queue back the buffer so that the driver layer can fill it with new frames. This is usually part of the application main-loop.
VIDIOC_DQBUF works similar to VIDIOC_QBUF but it populates the v4l2_buffer.index field with the index number of the buffer that has been dequeued.
Example code on how to use VIDIOC_DQBUF:
1int dequeue_buffer(int fd)
2{
3 struct v4l2_buffer bufd = {0};
4 bufd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5 bufd.memory = V4L2_MEMORY_MMAP;
6 bufd.index = 0;
7
8 if (ioctl(fd, VIDIOC_DQBUF, &bufd) == -1) {
9 perror("DeQueue Buffer");
10 exit(EXIT_FAILURE);
11 }
12
13 return bufd.index;
14}
Stop stream
Once we are done with the video capturing, we can stop the streaming. This will unlock all enqueued buffers and stop capture frames.
Example code on how to use VIDIOC_STREAMOFF:
1int stop_streaming(int fd)
2{
3 unsigned int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
4 if(ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
5 perror("VIDIOC_STREAMON");
6 exit(EXIT_FAILURE);
7 }
8
9 return 0;
10}
Full example
It's not the most beautiful example, but it's at least something to work with.
1#include <stdio.h>
2#include <stdlib.h>
3
4#include <fcntl.h>
5#include <unistd.h>
6#include <errno.h>
7#include <sys/mman.h>
8#include <sys/ioctl.h>
9#include <linux/videodev2.h>
10
11#define NBUF 3
12
13void query_capabilites(int fd)
14{
15 struct v4l2_capability cap;
16
17 if (-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap)) {
18 perror("Query capabilites");
19 exit(EXIT_FAILURE);
20 }
21
22 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
23 fprintf(stderr, "Device is no video capture device\\n");
24 exit(EXIT_FAILURE);
25 }
26
27 if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
28 fprintf(stderr, "Device does not support read i/o\\n");
29 }
30
31 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
32 fprintf(stderr, "Devices does not support streaming i/o\\n");
33 exit(EXIT_FAILURE);
34 }
35}
36
37int set_format(int fd)
38{
39 struct v4l2_format format = {0};
40 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
41 format.fmt.pix.width = 320;
42 format.fmt.pix.height = 240;
43 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
44 format.fmt.pix.field = V4L2_FIELD_NONE;
45
46 int res = ioctl(fd, VIDIOC_S_FMT, &format);
47 if(res == -1) {
48 perror("Could not set format");
49 exit(EXIT_FAILURE);
50 }
51
52 return res;
53}
54
55int request_buffer(int fd, int count)
56{
57 struct v4l2_requestbuffers req = {0};
58 req.count = count;
59 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
60 req.memory = V4L2_MEMORY_MMAP;
61
62 if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
63 perror("Requesting Buffer");
64 exit(EXIT_FAILURE);
65 }
66
67 return req.count;
68}
69
70int query_buffer(int fd, int index, unsigned char **buffer)
71{
72 struct v4l2_buffer buf = {0};
73 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
74 buf.memory = V4L2_MEMORY_MMAP;
75 buf.index = index;
76
77 if(ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
78 perror("Could not query buffer");
79 exit(EXIT_FAILURE);
80 }
81
82 *buffer = (u_int8_t*)mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
83 return buf.length;
84}
85
86int queue_buffer(int fd, int index)
87{
88 struct v4l2_buffer bufd = {0};
89 bufd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
90 bufd.memory = V4L2_MEMORY_MMAP;
91 bufd.index = index;
92
93 if (ioctl(fd, VIDIOC_QBUF, &bufd) == -1) {
94 perror("Queue Buffer");
95 exit(EXIT_FAILURE);
96 }
97
98 return bufd.bytesused;
99}
100
101int start_streaming(int fd)
102{
103 unsigned int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
104 if(ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
105 perror("VIDIOC_STREAMON");
106 exit(EXIT_FAILURE);
107 }
108
109 return 0;
110}
111
112int dequeue_buffer(int fd)
113{
114 struct v4l2_buffer bufd = {0};
115 bufd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
116 bufd.memory = V4L2_MEMORY_MMAP;
117 bufd.index = 0;
118
119 if (ioctl(fd, VIDIOC_DQBUF, &bufd) == -1) {
120 perror("DeQueue Buffer");
121 exit(EXIT_FAILURE);
122 }
123
124 return bufd.index;
125}
126
127
128int stop_streaming(int fd)
129{
130 unsigned int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
131 if(ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
132 perror("VIDIOC_STREAMON");
133 exit(EXIT_FAILURE);
134 }
135
136 return 0;
137}
138
139int main()
140{
141 unsigned char *buffer[NBUF];
142 int fd = open("/dev/video4", O_RDWR);
143 int size;
144 int index;
145 int nbufs;
146
147 /* Step 1: Query capabilities */
148 query_capabilites(fd);
149
150 /* Step 2: Set format */
151 set_format(fd);
152
153 /* Step 3: Request Format */
154 nbufs = request_buffer(fd, NBUF);
155 if ( nbufs > NBUF) {
156 fprintf(stderr, "Increase NBUF to at least %i\n", nbufs);
157 exit(EXIT_FAILURE);
158 }
159
160 for (int i = 0; i < NBUF; i++) {
161 /* Step 4: Query buffers
162 * Assume all sizes is equal.
163 * */
164 size = query_buffer(fd, i, &buffer[i]);
165
166 /* Step 5: Queue buffer */
167 queue_buffer(fd, i);
168 }
169
170 /* Step 6: Start streaming */
171 start_streaming(fd);
172
173 fd_set fds;
174 FD_ZERO(&fds);
175 FD_SET(fd, &fds);
176 struct timeval tv = {0};
177 tv.tv_sec = 2;
178 int r = select(fd+1, &fds, NULL, NULL, &tv);
179 if(-1 == r){
180 perror("Waiting for Frame");
181 exit(1);
182 }
183
184 /* Step 7: Dequeue buffer */
185 index = dequeue_buffer(fd);
186
187 int file = open("output.raw", O_RDWR | O_CREAT, 0666);
188 write(file, buffer[index], size);
189
190 /* Step 8: Stop streaming */
191 stop_streaming(fd);
192
193
194 /* Cleanup the resources */
195 for (int i =0; i < NBUF; i++) {
196 munmap(buffer[i], size);
197 }
198 close(file);
199 close(fd);
200
201 return 0;
202}