Capture an image with V4L2

Posted by Marcus Folkesson on Sunday, March 19, 2023

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.

/media/camera-sketch.png

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:

/media/v4l2-workflow.png

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}