Controlling V4L2 Devices With v4l2-ctl
Learn v4l2-ctl device control on Linux — querying camera capabilities, listing and changing controls, and setting pixel format, resolution, and frame rate, all from the command line.
Topics Covered In This Lecture
Table Of Contents
- What Is v4l2-ctl Device Control?
- What You Will Learn
- Prerequisites
- Inspecting Device Capabilities
- Listing And Changing Controls
- Pixel Format, Resolution, And Frame Rate
- Comparison Table
- Building A Demo Control Program
- Real-World Use Cases
- Common Mistakes And Troubleshooting
- Best Practices
- Summary And Conclusion
- FAQ
What Is v4l2-ctl Device Control?
v4l2-ctl device control refers to using the v4l2-ctl command-line utility, shipped in the v4l-utils package, to query and configure Video4Linux2 devices without writing any code. Under the hood, every v4l2-ctl command is just a thin wrapper around the same ioctl calls a C program would make — VIDIOC_QUERYCAP, VIDIOC_QUERY_EXT_CTRL, VIDIOC_S_CTRL, VIDIOC_S_FMT, and VIDIOC_S_PARM — which makes it the fastest way to explore a new camera driver before you write a single line of application code.
This lecture, part of EmbeddedPathashala’s free Linux kernel development course, covers how to inspect a device’s capabilities, enumerate and change its controls (brightness, exposure, white balance, and more), and pick a pixel format, resolution, and frame rate — then shows the equivalent driver-level ioctl calls in an original C program.
What You Will Learn
Prerequisites
This lecture builds directly on the previous one on V4L2 buffer dequeuing. You should have v4l-utils installed (apt install v4l-utils on Debian/Ubuntu-based systems) and a working camera device such as /dev/video0. Basic familiarity with reading struct definitions in linux/videodev2.h will help when we move to the C example.
Inspecting Device Capabilities
Before configuring anything, always confirm which device node you are targeting and what it supports. List every V4L2 device on the system with:
$ v4l2-ctl --list-devices
USB HD Camera: USB HD Camera (usb-0000:00:14.0-3):
/dev/video0
/dev/video1
Most webcams expose two nodes — one for actual video capture and a second metadata node. If -d is not passed, v4l2-ctl targets /dev/video0 by default. To see full driver information and capability flags for a specific node, use -D:
$ v4l2-ctl -d /dev/video0 -D
Driver Info:
Driver name : uvcvideo
Card type : USB HD Camera: USB HD Camera
Driver version : 6.8.0
Capabilities : 0x84a00001
Video Capture
Metadata Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
The Capabilities field is a bitmask defined by enum v4l2_capability_flags in linux/videodev2.h — the same values VIDIOC_QUERYCAP returns to a C program. Run v4l2-ctl --all for the full picture in one shot, combining driver info, current format, and controls together.
Listing And Changing Controls
Every V4L2 control — brightness, contrast, exposure mode, and so on — is described by a struct v4l2_query_ext_ctrl the driver fills in. List everything the device supports with -L:
$ v4l2-ctl -L
brightness 0x00980900 (int) : min=0 max=255 step=1 default=128 value=128
contrast 0x00980901 (int) : min=0 max=255 step=1 default=32 value=32
saturation 0x00980902 (int) : min=0 max=100 step=1 default=64 value=64
white_balance_temperature 0x0098091a (int) : min=2800 max=6500 step=1 default=4600 value=4600
exposure_auto 0x009a0901 (menu) : min=0 max=3 default=3 value=3
1: Manual Mode
3: Aperture Priority Mode
Change a control with --set-ctrl, using name=value pairs:
$ v4l2-ctl --set-ctrl brightness=192
And read a single control back with --get-ctrl:
$ v4l2-ctl --get-ctrl brightness
brightness: 192
Multiple controls can be set in one call by separating them with commas, e.g. --set-ctrl brightness=192,contrast=40, which internally becomes a single VIDIOC_S_EXT_CTRLS call rather than several round trips.
Pixel Format, Resolution, And Frame Rate
Enumerate every pixel format, resolution, and frame interval a device supports with --list-formats-ext:
$ v4l2-ctl --list-formats-ext
Index : 0
Type : Video Capture
Pixel Format: 'MJPG' (compressed)
Size: Discrete 1920x1080
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.033s (30.000 fps)
Index : 1
Type : Video Capture
Pixel Format: 'YUYV'
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Set the active resolution and pixel format with --set-fmt-video:
$ v4l2-ctl --set-fmt-video=width=1280,height=720,pixelformat=MJPG
And set the frame rate (numerator only — the denominator is fixed at 1) with --set-parm:
$ v4l2-ctl --set-parm=30
Frame rate set to 30.000 fps
Comparison Table: v4l2-ctl Options
| Command | Underlying ioctl | Purpose |
|---|---|---|
| -D / –all | VIDIOC_QUERYCAP | Driver name, version, capability flags |
| -L | VIDIOC_QUERY_EXT_CTRL | Enumerate supported controls |
| –set-ctrl / –get-ctrl | VIDIOC_S_CTRL / VIDIOC_G_CTRL | Read or write a control value |
| –list-formats-ext | VIDIOC_ENUM_FMT / VIDIOC_ENUM_FRAMESIZES | Enumerate formats, sizes, frame intervals |
| –set-fmt-video | VIDIOC_S_FMT | Select pixel format and resolution |
| –set-parm | VIDIOC_S_PARM | Select frame rate |
Building A Demo Control Program
Here is the C equivalent of the v4l2-ctl device control commands above — an original demo, ep_v4l2_ctrl.c, that reads brightness, raises it by 20, and switches the active format to 1280×720 MJPG:
/* ep_v4l2_ctrl.c — EmbeddedPathashala V4L2 control demo */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define EP_DEVICE "/dev/video0"
static void ep_die(const char *msg) { perror(msg); exit(EXIT_FAILURE); }
int main(void)
{
int fd = open(EP_DEVICE, O_RDWR);
if (fd == -1) ep_die("open");
/* 1. Read current brightness */
struct v4l2_control ctrl = {0};
ctrl.id = V4L2_CID_BRIGHTNESS;
if (ioctl(fd, VIDIOC_G_CTRL, &ctrl) == -1) ep_die("VIDIOC_G_CTRL");
printf("current brightness: %d\n", ctrl.value);
/* 2. Raise brightness by 20 */
ctrl.value += 20;
if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) ep_die("VIDIOC_S_CTRL");
printf("brightness set to: %d\n", ctrl.value);
/* 3. Switch to 1280x720 MJPG */
struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 1280;
fmt.fmt.pix.height = 720;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) ep_die("VIDIOC_S_FMT");
printf("format set to: %ux%u MJPG\n",
fmt.fmt.pix.width, fmt.fmt.pix.height);
/* 4. Request 30 fps */
struct v4l2_streamparm parm = {0};
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 1;
parm.parm.capture.timeperframe.denominator = 30;
if (ioctl(fd, VIDIOC_S_PARM, &parm) == -1) ep_die("VIDIOC_S_PARM");
printf("frame rate set to: %u fps\n",
parm.parm.capture.timeperframe.denominator);
close(fd);
return 0;
}
Build and run:
$ gcc -Wall -O2 -o ep_v4l2_ctrl ep_v4l2_ctrl.c
$ ./ep_v4l2_ctrl
Expected output:
current brightness: 128
brightness set to: 148
format set to: 1280x720 MJPG
frame rate set to: 30 fps
You can immediately verify the change from the command line without restarting anything:
$ v4l2-ctl --get-ctrl brightness
brightness: 148
Real-World Use Cases
v4l2-ctl device control is the go-to first step whenever a Linux camera driver is bring-up tested — kernel and BSP engineers use it to confirm a new sensor driver reports sane controls and formats before any application code exists. CI pipelines for embedded camera products often script v4l2-ctl calls to validate that firmware updates haven’t broken exposure or white-balance controls. It is also commonly used in shell scripts that configure a fixed camera setup (resolution, frame rate, disabled auto-exposure) at boot time on kiosk or robotics systems, avoiding the need for a custom configuration binary entirely.
Common Mistakes And Troubleshooting
Setting a control while auto mode is active. Controls like exposure_absolute often have a flags=inactive marker when the corresponding auto control (exposure_auto) is enabled — you must disable auto mode first or the set silently has no effect.
Requesting an unsupported format/resolution pair. VIDIOC_S_FMT does not fail on an unsupported combination — many drivers silently pick the closest match. Always re-read fmt.fmt.pix after the call to confirm what was actually applied.
Forgetting -d for a multi-camera system. Without -d /dev/videoN, every command targets /dev/video0, which can silently reconfigure the wrong camera on multi-camera boards.
Best Practices
Always run v4l2-ctl --list-formats-ext before hardcoding a resolution and pixel format in application code — supported combinations vary between camera modules even when they share the same driver. Prefer setting format before starting a stream, never while VIDIOC_STREAMON is active, since most drivers reject format changes mid-stream with EBUSY.
Performance Considerations
Choosing a compressed format like MJPG over raw YUYV at high resolutions dramatically cuts USB or MIPI CSI bus bandwidth, often enabling a higher achievable frame rate on the same hardware — visible directly in the frame-interval list returned by --list-formats-ext.
Security Considerations
Because v4l2-ctl --set-ctrl requires write access to the device node, restrict which users or system services can invoke it in production images — an unprivileged process should never be able to silently disable a security camera’s exposure or flip its capture format.
Summary And Key Takeaways
v4l2-ctl device control gives you full visibility into and control over a V4L2 camera without writing code: -D for capabilities, -L for controls, --set-ctrl/--get-ctrl for adjusting them, and --list-formats-ext/--set-fmt-video/--set-parm for format and frame rate. Every one of these commands maps directly onto a documented V4L2 ioctl, which is why v4l2-ctl doubles as both a debugging tool and a learning aid for the underlying API.
Conclusion
With device capabilities, controls, and format selection now covered both from the command line and in C, you have the full toolkit for configuring any V4L2 camera on Linux. The next lecture in this free Linux device drivers course moves on to actually capturing and streaming frames to disk using v4l2-ctl, plus how to debug the V4L2 core when something goes wrong.
Frequently Asked Questions
What package provides v4l2-ctl on Linux?
v4l2-ctl is part of the v4l-utils package, installable via apt install v4l-utils on Debian/Ubuntu, dnf install v4l-utils on Fedora, or the equivalent package on other distributions.
How do I list all controls a camera supports?
Run v4l2-ctl -L (or v4l2-ctl -d /dev/videoN -L for a specific device) to enumerate every control with its type, range, default, and current value.
Why does –set-ctrl not change anything?
The control may be marked inactive because a related auto mode (like exposure_auto or white_balance_temperature_auto) is enabled; disable the auto control first, then set the manual value.
How do I change camera resolution with v4l2-ctl?
Use v4l2-ctl –set-fmt-video=width=W,height=H,pixelformat=FOURCC, choosing a combination reported by v4l2-ctl –list-formats-ext.
Can I set frame rate and resolution in the same command?
They are separate v4l2-ctl options (–set-fmt-video and –set-parm) because they map to different ioctls, VIDIOC_S_FMT and VIDIOC_S_PARM, but both can be run back-to-back before starting a stream.
What is the difference between v4l2-ctl and qv4l2?
v4l2-ctl is a scriptable command-line tool ideal for embedded systems and automation, while qv4l2 is its Qt-based graphical equivalent meant for interactive testing on a desktop.
Does v4l2-ctl work over SSH on a headless embedded board?
Yes, since it only talks to the V4L2 device node via ioctl calls and has no GUI dependency, it works perfectly over SSH on headless embedded Linux targets.
Continue Learning Linux Kernel Development — Free
This lecture is part of EmbeddedPathashala’s free Linux kernel development course and free embedded systems course, helping engineers master real driver-level skills at no cost.
Browse The Full Course Join The Community