VIDIOC_REQBUFS Buffer Allocation Guide
Free Linux Kernel Development Course — V4L2 User Space API, Part 4
Topics Covered In This Free Linux Device Drivers Course Lecture
Welcome back to EmbeddedPathashala’s free Linux kernel development course.
In the previous lecture of this free embedded Linux course we negotiated the
pixel format of a video capture device using VIDIOC_G_FMT and
VIDIOC_S_FMT. Format negotiation only tells the driver what each frame
looks like — it says nothing about how fast frames should arrive, and it says nothing
about where those frames will actually be stored in memory. This lecture closes both gaps. We
will look at how a V4L2 capture application asks the driver for a specific frame rate using
VIDIOC_G_PARM/VIDIOC_S_PARM, and then walk through the first half of
buffer management: the VIDIOC_REQBUFS ioctl and the struct
v4l2_requestbuffers structure that tells the kernel how many buffers to allocate, of
what type, and using which memory model. By the end you will be able to request user-pointer
buffers and allocate the matching user-space memory correctly on a modern kernel.
What You Will Learn
- How V4L2 frame rate negotiation works through
struct v4l2_streamparmandstruct v4l2_captureparm - Why a driver is allowed to silently coerce your requested frame rate, and how to detect it
- The exact meaning of every field in
struct v4l2_requestbuffers - How modern kernels expose supported streaming I/O methods without trial-and-error probing
- How to request and allocate
V4L2_MEMORY_USERPTRbuffers correctly - Common mistakes that cause
VIDIOC_REQBUFSto fail withEINVALorEBUSY
Prerequisites
- Completion of the earlier lectures in this V4L2 user-space series (device open, capability query, format negotiation)
- Comfort with the V4L2 ioctl catalog and the
xioctl()EINTR-retry wrapper - Basic familiarity with a Linux system that exposes a
/dev/videoNnode (a USB webcam is enough) - A working C toolchain — this is part of our free embedded systems course series and assumes no prior kernel driver-writing experience
Why Frame Rate Negotiation Is a Separate Step
A camera sensor is not a single fixed-speed device. The same sensor mode that produces a
1920×1080 frame at 30 frames per second can often also run at 15 fps with lower bandwidth, or
the driver may refuse to change the rate at all and simply report back whatever it is capable
of. Because of this, V4L2 treats frame timing as a negotiation, not a command: the application
proposes a rate, and the driver is free to grant it, coerce it to the nearest supported value, or
ignore the request entirely on hardware that cannot vary its frame interval. This negotiation
happens through the VIDIOC_G_PARM and VIDIOC_S_PARM ioctls, both of
which operate on the same struct v4l2_streamparm used for both capture and output
devices.
struct v4l2_streamparm and struct v4l2_captureparm
v4l2_streamparm is a union-style structure, exactly like v4l2_format
from the previous lecture — its active member depends on the buffer type you set. For
a capture device you fill in parm.capture, of type struct
v4l2_captureparm. The two fields that matter for frame rate work are:
- capability — a bitmask the driver fills in on
VIDIOC_G_PARM. If theV4L2_CAP_TIMEPERFRAMEbit is set, the driver supports changing the frame interval throughVIDIOC_S_PARM. If it is clear, don’t bother callingVIDIOC_S_PARM— the driver will just reject or ignore it - timeperframe — a
struct v4l2_fractwithnumerator/denominatorfields expressing the interval between frames. A 30 fps stream is expressed as numerator = 1, denominator = 30, not as a plain integer — this lets V4L2 represent non-integer rates like 29.97 fps cleanly
Frame Rate Negotiation Flow
On the latest stable kernel, this API surface has not changed structurally, but most modern
sensor drivers built on the V4L2 subdev framework now also expose frame interval selection through
VIDIOC_SUBDEV_S_FRAME_INTERVAL at the subdevice level and
VIDIOC_ENUM_FRAMEINTERVALS at the video-node level, letting an application discover
exactly which discrete or stepwise rates a given resolution supports before ever calling
VIDIOC_S_PARM. We will cover frame interval enumeration in a later lecture of this
free linux device drivers course; for now, VIDIOC_S_PARM remains the
correct call to actually apply the rate.
Requesting a Frame Rate — Modern Example
Below is an original demo, ep_v4l2_parmbuf, rewritten against the current V4L2
headers. It first queries the current parameters, checks the capability bit, then requests 30 fps
and reports whether the driver honoured it.
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define EP_TARGET_FPS 30
int ep_set_frame_rate(int fd)
{
struct v4l2_streamparm parm = {0};
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* Step 1: find out if the driver supports variable frame rate */
if (ioctl(fd, VIDIOC_G_PARM, &parm) == -1) {
perror("VIDIOC_G_PARM");
return -1;
}
if (!(parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) {
fprintf(stderr, "ep_v4l2_parmbuf: driver has a fixed frame rate\n");
return 0; /* not fatal, just nothing to negotiate */
}
/* Step 2: request the target frame rate */
memset(&parm, 0, sizeof(parm));
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 1;
parm.parm.capture.timeperframe.denominator = EP_TARGET_FPS;
if (ioctl(fd, VIDIOC_S_PARM, &parm) == -1) {
perror("VIDIOC_S_PARM");
return -1;
}
/* Step 3: the driver may have coerced our request */
if (parm.parm.capture.timeperframe.denominator != EP_TARGET_FPS) {
printf("ep_v4l2_parmbuf: fps coerced from %d to %d\n",
EP_TARGET_FPS, parm.parm.capture.timeperframe.denominator);
} else {
printf("ep_v4l2_parmbuf: frame rate set to %d fps\n", EP_TARGET_FPS);
}
return 0;
}
Expected output on a UVC webcam that supports the request:
$ ./ep_v4l2_parmbuf /dev/video0
ep_v4l2_parmbuf: frame rate set to 30 fps
On hardware that can only run at 25 fps for the current resolution, you would instead see:
$ ./ep_v4l2_parmbuf /dev/video0
ep_v4l2_parmbuf: fps coerced from 30 to 25
VIDIOC_REQBUFS — Requesting Video Buffers
Once the format and frame rate are settled, the driver still owns no memory for actual video
data. Video capture is a high-bandwidth job, so V4L2 never copies frame data through a single
ioctl the way VIDIOC_G_FMT passes structure fields. Instead it uses a
streaming I/O model: a pool of buffers is allocated once, then repeatedly handed
back and forth between the application and the driver as frames are captured. The first step of
setting up that pool is VIDIOC_REQBUFS, which takes a fresh
struct v4l2_requestbuffers.
struct v4l2_requestbuffers Fields
- count — the number of buffers you want. Two or three buffers is the practical minimum to avoid dropped frames under normal scheduling jitter; the driver may grant fewer than you asked for, and it writes the actual granted count back into this same field, so you must always re-read it after the call
- type — the buffer type, an
enum v4l2_buf_typevalue such asV4L2_BUF_TYPE_VIDEO_CAPTUREfor a capture device orV4L2_BUF_TYPE_VIDEO_OUTPUTfor an output device - memory — the streaming memory model:
V4L2_MEMORY_MMAP(driver-allocated, kernel-mapped buffers),V4L2_MEMORY_USERPTR(application-allocated buffers handed to the driver), orV4L2_MEMORY_DMABUF(buffers imported from another DMA-BUF exporter, such as a GPU or another V4L2 device) - capabilities — on current kernels the driver fills this field on return
with flags such as
V4L2_BUF_CAP_SUPPORTS_MMAP,V4L2_BUF_CAP_SUPPORTS_USERPTR, andV4L2_BUF_CAP_SUPPORTS_DMABUF, telling you in one call exactly which memory models this driver accepts
Discovering Supported I/O Methods — Old Way vs Modern Way
Older driver code had no clean way to ask “which streaming methods does this driver support” —
the only option was to call VIDIOC_REQBUFS once per memory type and see which calls
succeeded or failed with EINVAL, then undo any buffers that were accidentally
allocated in the process. Current kernels avoid this entirely: a single
VIDIOC_REQBUFS call with count set to 0 allocates nothing
but still returns the capabilities bitmask, letting an application query support
cleanly before committing to a real allocation.
| Memory Type | Who Allocates | Typical Use Case | Copy Overhead |
|---|---|---|---|
| V4L2_MEMORY_MMAP | Kernel driver | Most common, simplest zero-copy path | None (mapped) |
| V4L2_MEMORY_USERPTR | Application (malloc/aligned alloc) | Custom buffer placement, legacy drivers | Driver may still copy internally |
| V4L2_MEMORY_DMABUF | External exporter (GPU, another V4L2 device) | Zero-copy pipelines between subsystems | None (shared DMA-BUF) |
Requesting User Pointer Buffers
Below, our original demo ep_v4l2_reqbufs requests four
V4L2_MEMORY_USERPTR buffers and checks the granted count:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define EP_BUF_COUNT 4
int ep_request_userptr_buffers(int fd, unsigned int *granted_count)
{
struct v4l2_requestbuffers req = {0};
req.count = EP_BUF_COUNT;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_USERPTR;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
if (errno == EINVAL) {
fprintf(stderr,
"ep_v4l2_reqbufs: driver does not support user pointer I/O\n");
} else {
perror("VIDIOC_REQBUFS");
}
return -1;
}
if (req.count < 2) {
fprintf(stderr,
"ep_v4l2_reqbufs: insufficient buffer memory (got %u)\n", req.count);
return -1;
}
printf("ep_v4l2_reqbufs: driver granted %u of %u requested buffers\n",
req.count, EP_BUF_COUNT);
*granted_count = req.count;
return 0;
}
With user pointer I/O selected, the kernel does not allocate the buffer memory itself — the application must. Here is the matching allocation step:
struct ep_buffer {
void *start;
size_t length;
};
struct ep_buffer *ep_allocate_userptr_buffers(unsigned int count, size_t buf_size)
{
struct ep_buffer *buffers = calloc(count, sizeof(*buffers));
unsigned int i;
if (!buffers) {
fprintf(stderr, "ep_v4l2_reqbufs: out of memory\n");
return NULL;
}
for (i = 0; i < count; ++i) {
buffers[i].length = buf_size;
buffers[i].start = aligned_alloc(4096, buf_size);
if (!buffers[i].start) {
fprintf(stderr, "ep_v4l2_reqbufs: allocation failed for buffer %u\n", i);
free(buffers);
return NULL;
}
}
return buffers;
}
Expected output:
$ ./ep_v4l2_reqbufs /dev/video0
ep_v4l2_reqbufs: driver granted 4 of 4 requested buffers
Notice the switch from plain malloc() in old reference code to
aligned_alloc(4096, buf_size) here. On current kernels, user-pointer buffers handed
to a DMA-capable capture driver generally need to be page-aligned so the kernel can pin the
correct set of pages for DMA; unaligned buffers are a frequent cause of VIDIOC_QBUF
failures we will cover in the next lecture when we enqueue these buffers for capture.
Real-World Use Cases
Frame rate negotiation matters most in bandwidth-constrained pipelines — a USB 2.0 webcam
streaming uncompressed YUYV at high resolution simply cannot sustain 60 fps, so applications like
video conferencing software query V4L2_CAP_TIMEPERFRAME and fall back gracefully.
User-pointer buffer allocation shows up in embedded vision pipelines where frames need to land in
a specific pre-registered memory region — for example, a buffer that a downstream image signal
processor or a GStreamer element has already reserved — rather than wherever the kernel driver
happens to allocate memory internally.
Common Mistakes and Troubleshooting
- Calling VIDIOC_S_PARM without checking V4L2_CAP_TIMEPERFRAME first — many drivers simply ignore the call on fixed-rate hardware; always check the capability bit so your logs aren’t misleading
- Assuming the requested frame rate was granted — always re-read
timeperframe.denominatorafterVIDIOC_S_PARMreturns - Ignoring the granted req.count after VIDIOC_REQBUFS — the driver can silently grant fewer buffers than requested; code that assumes the original count will index out of bounds later
- Calling VIDIOC_REQBUFS a second time while buffers are still queued —
this returns
EBUSY; you must stream off and free existing buffers first - Using unaligned malloc() for USERPTR buffers — leads to intermittent
VIDIOC_QBUFfailures on DMA-based capture hardware
Best Practices, Performance and Security Considerations
- Prefer
V4L2_MEMORY_MMAPorV4L2_MEMORY_DMABUFoverV4L2_MEMORY_USERPTRwhen possible — both avoid the extra page-pinning and validation overhead the kernel performs on every user-supplied pointer - Request 3-4 buffers as a starting point; more buffers add latency, fewer buffers risk dropped frames under scheduling pressure
- Always free previously allocated user-pointer memory before requesting a new buffer count, to avoid leaking pinned pages
- Validate
req.countandparm.parm.capture.timeperframeafter every call rather than trusting the values you sent in — this is the single most common source of subtle bugs in V4L2 applications on this free linux kernel development course - From a security standpoint, user-pointer buffers are pinned and DMA-mapped by the kernel; never pass pointers into memory regions you don’t fully own, since a malicious or buggy capture path could otherwise write video data into unrelated process memory
Interview Questions
Why does V4L2_S_PARM sometimes not change the frame rate at all?
Because not every capture device supports variable frame rate. The driver advertises this
through the V4L2_CAP_TIMEPERFRAME bit returned by VIDIOC_G_PARM. If
that bit is clear, the hardware runs at a fixed rate for the current mode and
VIDIOC_S_PARM has nothing to negotiate.
What is the difference between V4L2_MEMORY_MMAP and V4L2_MEMORY_USERPTR?
With MMAP, the kernel driver allocates the buffer memory and the application maps it into
its address space with mmap(). With USERPTR, the application allocates the
memory itself (typically page-aligned) and simply hands the pointer and length to the driver,
which pins those pages for DMA.
Why must an application re-check req.count after VIDIOC_REQBUFS?
The driver is permitted to grant fewer buffers than requested, based on available memory or
hardware limits, and it reports the actual number by overwriting the same count
field. Code that keeps using the originally requested count instead of the returned one can
index past the allocated buffer array.
Summary and Key Takeaways
Frame rate negotiation and buffer requesting are the two steps that turn a correctly
formatted video stream into an actual capture pipeline. VIDIOC_G_PARM and
VIDIOC_S_PARM let you query and request a frame interval, always subject to driver
coercion that your code must detect. VIDIOC_REQBUFS then asks the kernel to
reserve a pool of buffers of a given type and memory model, and on current kernels its returned
capabilities field tells you exactly which streaming methods — MMAP, USERPTR, or
DMABUF — the driver actually supports, without any trial-and-error probing. For
V4L2_MEMORY_USERPTR specifically, the application owns the allocation, and
page-aligned memory is the safest choice on modern DMA-capable hardware. In the next lecture of
this free linux development course we complete the picture with
VIDIOC_QUERYBUF and mmap() for kernel-allocated buffers, followed by
the DMA-BUF export path.
Frequently Asked Questions
What does VIDIOC_REQBUFS actually do in the Linux kernel?
It asks the V4L2 driver to reserve a specified number of video buffers of a given memory
type (MMAP, USERPTR, or DMABUF) for a given buffer type such as
V4L2_BUF_TYPE_VIDEO_CAPTURE, and returns how many buffers were actually
granted.
Can I call VIDIOC_REQBUFS with count set to zero?
Yes. On current kernels, requesting a count of zero frees any previously allocated buffers
for that memory type and buffer type, and can also be used purely to read back the driver’s
supported capabilities flags without allocating anything.
How many V4L2 buffers should I request for smooth video capture?
Three or four buffers is a practical starting point for most USB and embedded camera pipelines. Fewer buffers risk dropped frames under scheduling jitter, while significantly more buffers mainly add latency without a proportional benefit.
Why does the driver change the frame rate I requested with VIDIOC_S_PARM?
Hardware sensors typically only support a discrete or stepwise set of frame intervals for a given resolution. If your requested rate isn’t one of them, the driver coerces it to the nearest supported value and reports the actual rate back in the same structure.
Is V4L2_MEMORY_USERPTR still recommended on modern kernels?
It is still supported and useful when buffers must live in application-controlled memory, but MMAP and DMABUF are generally preferred today because they avoid the extra page-pinning overhead and integrate more cleanly with zero-copy pipelines.
What error does VIDIOC_REQBUFS return if a memory type isn’t supported?
The ioctl fails with errno set to EINVAL. On current kernels you
can avoid guessing by first checking the capabilities bitmask returned from a
zero-count VIDIOC_REQBUFS call.
Where can I practice this as part of a free Linux kernel development course?
EmbeddedPathashala’s free embedded systems course walks through this entire V4L2 chapter lecture by lecture, from device capability queries through to a full streaming capture loop, using original example code you can build and run on any Linux machine with a USB webcam.
Continue the Free Linux Kernel Development Course
Next up: requesting memory-mappable buffers with VIDIOC_QUERYBUF and mmap(), plus the DMA-BUF export path — part of EmbeddedPathashala’s free linux device drivers course.
Next Lecture Full Course Index