You've probably seen it before: a video where the subject's face is sharp and detailed, but the background is a blocky, pixelated mess. This isn't an accident—it's ROI encoding, one of the most powerful and least‑understood features in modern video compression.
ROI (Region of Interest) encoding lets you tell the encoder: "This part of the frame matters more than the rest". The encoder then allocates more bitrate to that region, delivering higher quality where it counts, while saving bits on the background. The result? Better visual quality at the same bitrate—or the same quality at a lower bitrate.[reference:0]
Yet the command‑line documentation is sparse. The addroi filter was added in 2019[reference:1], but most users don't know it exists, and those who do struggle to make it work. This guide changes that.
Key takeaways
- ROI encoding allocates more bitrate to important regions (faces, text, objects) and less to backgrounds.
addroiis the filter that attaches ROI metadata to video frames for the encoder.qoffsetcontrols the quality difference—negative = better quality, positive = worse quality.- Supported encoders: libx264, libx265, libvpx (VP8/VP9), and some hardware encoders.
- Dynamic ROI with
zmqlets you move the region in real time during live streaming. - Multiple regions can be defined, ordered from most to least important.
The Gap: A Powerful Feature, Almost No Command‑Line Guide
The FFmpeg ROI API was introduced in 2019[reference:2], adding support for ROI-based encoding in libx264, libx265, and libvpx[reference:3]. The addroi filter followed shortly after[reference:4], providing a command‑line interface for attaching ROI metadata to video frames.
Yet the documentation is sparse. The filter's qoffset parameter is explained in technical terms that assume you already understand quantization[reference:5]. Online resources are scattered—a mailing list post here, a code comment there[reference:6]. There's no cohesive guide that walks you through the why and how of ROI encoding from the command line.
This guide fills that gap.
The Symptom: "Why Is My Face Blurry?"
You've encoded a video at a reasonable bitrate. The background looks fine—but the subject's face is a blocky, pixelated mess. You increase the bitrate, and the face improves—but now the file is twice as large.
The problem is that traditional encoding treats every pixel equally. A face and a plain wall get the same bitrate allocation. ROI encoding fixes this by telling the encoder: "This face is important. Give it more bits."
This is especially critical for:
- Surveillance footage — faces and license plates need to be identifiable
- Video conferencing — the speaker's face should be crisp
- Live streaming — game HUD elements and faces need clarity
- Narrowband HD — saving bandwidth while maintaining critical detail[reference:7]
Core Concepts: What Is ROI Encoding?
ROI encoding is a video coding technique that allocates more bitrate to regions of interest and less to background areas[reference:8]. It works by adjusting the quantization parameter (QP) on a per‑region basis[reference:9]:
- Lower QP = finer quantization = higher quality = more bits
- Higher QP = coarser quantization = lower quality = fewer bits
ROI encoding doesn't require changing the encoder's core behavior. Instead, it provides a unified interface that translates your ROI specifications into the API calls required by each encoder[reference:10].
The key data structure is AVRegionOfInterest[reference:11], which defines a rectangular region and a quality offset. In the command line, this structure is populated by the addroi filter.
The addroi Filter – Your Command‑Line Tool
The addroi filter attaches ROI metadata to video frames[reference:12]. It doesn't change the pixels—it just tells the encoder which regions should get special treatment.
Syntax:
addroi=x:y:w:h:qoffset[:clear]
x— Distance from the left edge of the frame (in pixels)[reference:13]y— Distance from the top edge of the frame (in pixels)[reference:14]w— Width of the region (in pixels)[reference:15]h— Height of the region (in pixels)[reference:16]qoffset— Quality offset, from -1.0 to +1.0[reference:17]clear— Remove existing ROIs before adding new ones (optional)[reference:18]
If you need to define multiple ROIs on the same frame, use multiple addroi filters chained together—they'll accumulate[reference:19].
Understanding qoffset – The Quality Knob
qoffset is the most important—and most misunderstood—parameter in ROI encoding[reference:20].
qoffset = 0— No quality change[reference:21]qoffset = negative— Better quality (lower QP, more bits)[reference:22]qoffset = positive— Worse quality (higher QP, fewer bits)[reference:23]
The range is calibrated so that extreme values indicate the largest possible offset[reference:24]:
qoffset = -1.0— Encode the region at the best possible quality (QP = -12 in 10‑bit H.264)[reference:25]qoffset = +1.0— Encode the region at the worst possible quality[reference:26]
In practice, a value of qoffset = -0.1 to -0.3 is often sufficient to make a visible difference[reference:27]. For a 10‑bit H.264 stream where most of the frame is encoded at QP ≈ 30, a qoffset of -0.1 would lower the QP by about 6.3, resulting in noticeably better quality[reference:28].
Practical Example 1: Face Priority Encoding
Let's encode a video where the subject's face (centered in the frame) gets higher quality than the background.
# Encode with face region at 1080p, center 600x600 area gets better quality ffmpeg -i input.mp4 -vf "addroi=420:240:600:600:-0.2" -c:v libx264 -crf 23 -b:v 2M -c:a copy output_face_roi.mp4
Breaking it down:
addroi=420:240:600:600:-0.2— Defines a 600×600 region centered at (420, 240) in a 1920×1080 frameqoffset=-0.2— This region gets better quality (lower QP) than the rest of the frame-c:v libx264 -crf 23 -b:v 2M— H.264 encoding with a 2 Mbps target bitrate
The result? The face is sharper and more detailed, while the background uses fewer bits. At the same overall bitrate, the subjective quality is higher[reference:29].
Practical Example 2: Text Overlay Quality
If your video has important text overlays (subtitles, lower thirds, game HUD), you can prioritize those regions.
# Prioritize the bottom third of the frame where subtitles appear ffmpeg -i input.mp4 -vf "addroi=0:720:1920:360:-0.15" -c:v libx264 -crf 23 -b:v 2M -c:a copy output_subtitle_roi.mp4
This ensures that subtitles and lower‑third graphics remain crisp and readable, even at lower bitrates.
Practical Example 3: Multiple ROIs
You can define multiple regions by chaining addroi filters. Each region can have a different qoffset value[reference:30].
# Two ROIs: face (high priority) and subtitles (medium priority) ffmpeg -i input.mp4 -vf "addroi=420:240:600:600:-0.3,addroi=0:720:1920:360:-0.1" -c:v libx264 -crf 23 -b:v 2M -c:a copy output_multi_roi.mp4
Important ordering rules: When multiple regions overlap, the first region in the chain takes priority[reference:31]. Regions should be ordered from most to least important, as some encoders have a limited number of region slots[reference:32][reference:33].
The clear parameter removes existing ROIs before adding new ones—use it if you're building a complex filtergraph and need to start fresh[reference:34].
Practical Example 4: Dynamic ROI with zmq
One of the most exciting features of ROI encoding is the ability to move the region in real time during live streaming[reference:35].
# Start encoding with a dynamic ROI filter listening for zmq commands ffmpeg -i input.mp4 -vf "addroi=iw-200:ih-200:iw/2:ih/2:-1,zmq" -c:v libx264 -crf 23 -f mpegts udp://127.0.0.1:1234 # In another terminal, move the ROI to a new position echo "Parsed_addroi_0 reinit x=200:y=200:w=300:h=300" | ./zmqsend
This is incredibly powerful for live streaming[reference:36]. Imagine a security camera that follows a moving subject, always keeping the face in the high‑quality ROI[reference:37]. Or a sports broadcast that tracks the ball. The zmq filter makes this possible.
Encoder Support – What Works Where
ROI encoding is not supported by all encoders. Here's what works[reference:38]:
| Encoder | ROI Support | Notes |
|---|---|---|
| libx264 | ✅ Full support | Supports different QP offsets for different macroblocks[reference:39] |
| libx265 | ✅ Full support | Similar to libx264, with CTU‑level control[reference:40] |
| libvpx (VP8/VP9) | ✅ Full support | Requires aq_mode=0, cpu_used>=5, and deadline=realtime[reference:41] |
| h264_vaapi / hevc_vaapi | ✅ Partial support | Hardware‑accelerated, but may have region count limits[reference:42] |
| nvenc (NVIDIA) | ⚠️ Limited | Some support via NVENC API, but not fully integrated |
| libaom-av1 | ❌ Not yet | ROI API support is still in development |
| Software encoders (e.g., mpeg4) | ❌ No | Only modern encoders with ROI APIs support this |
Important: The FFmpeg ROI API was introduced in version 4.2.1[reference:43]. Ensure you're using a recent build.
Debugging Common ROI Issues
Here are the most frequent problems and their solutions.
| Problem | Likely Cause | Fix |
|---|---|---|
| No visible quality difference | qoffset is too small or encoder doesn't support ROI |
Try qoffset=-0.5; check encoder support; use -c:v libx264 |
| Encoder ignores ROI | Using an unsupported encoder or older FFmpeg | Use libx264, libx265, or libvpx with FFmpeg ≥ 4.2.1 |
| Region is misaligned | Encoder requires region alignment (e.g., 16‑pixel boundaries for H.264) | Align x, y, w, h to multiples of 16 (or 32 for H.265)[reference:44] |
| Multiple regions not working | Encoder has a limited number of region slots | Order regions from most to least important; truncate the list if needed[reference:45] |
| VP8/VP9 ROI not working | Missing required encoder parameters | Set aq_mode=0, cpu_used>=5, and deadline=realtime[reference:46] |
| Dynamic ROI with zmq not responding | zmq filter not built or filter name mismatch | Check that FFmpeg was built with --enable-libzmq; use the correct filter name in the command |
Visualize ROI with the FFmpegLab IDE
ROI encoding is much easier to debug with visual feedback. The FFmpegLab IDE lets you:
- Overlay ROI rectangles on the video preview—see exactly which regions will get better quality
- Adjust
qoffsetwith a slider and see how it affects the encoder's decisions - Compare before and after with side‑by‑side views
- Simulate multiple ROIs with visual ordering feedback
- Generate the exact command with your tuned parameters
Here's how ROI visualization looks in the FFmpegLab IDE.
# Visual ROI configuration # Drag rectangles on the preview to define regions; adjust qoffset with sliders
The IDE shows you exactly what's happening—which regions are defined, what qoffset values are applied, and how the encoder will prioritize quality. You can drag rectangles to define regions, adjust qoffset with sliders, and see the visual feedback in real time.
Frequently Asked Questions (FAQ)
What is ROI encoding and why would I use it?
ROI (Region of Interest) encoding allocates more bitrate to important regions of a frame (like faces or text) and less to backgrounds[reference:47]. This improves visual quality where it matters most without increasing the overall bitrate—perfect for surveillance, video conferencing, and live streaming[reference:48].
How do I use ROI encoding from the command line?
Use the addroi filter: -vf "addroi=x:y:w:h:qoffset"[reference:49]. For example, -vf "addroi=420:240:600:600:-0.2" gives better quality to a 600×600 region centered in a 1080p frame. Chain multiple addroi filters for multiple regions.
What does qoffset mean?
qoffset controls the quality offset, ranging from -1.0 to +1.0[reference:50]. Negative values give better quality (lower QP, more bits). Positive values give worse quality (higher QP, fewer bits)[reference:51]. A value of -0.2 is often enough to make a visible difference[reference:52].
Which encoders support ROI encoding?
libx264, libx265, and libvpx (VP8/VP9) have full support[reference:53]. Hardware encoders like h264_vaapi and hevc_vaapi have partial support[reference:54]. libaom-av1 and older encoders do not yet support ROI. Ensure you're using FFmpeg ≥ 4.2.1[reference:55].
Can I move the ROI during encoding?
Yes—using the zmq filter with addroi[reference:56]. You can send commands to update the ROI position in real time. This is ideal for live streaming where the subject moves[reference:57]. Example: echo "Parsed_addroi_0 reinit x=200:y=200:w=300:h=300" | ./zmqsend[reference:58].
Why isn't my ROI encoding working?
Common reasons: (1) Using an unsupported encoder—use libx264, libx265, or libvpx; (2) qoffset is too small—try -0.5; (3) Region coordinates need alignment—align x, y, w, h to multiples of 16 for H.264[reference:59]; (4) For VP8/VP9, ensure aq_mode=0 and cpu_used>=5[reference:60].
Final Word
ROI encoding is one of the most powerful—and least‑understood—features in modern FFmpeg. By telling the encoder which parts of the frame matter most, you can deliver better visual quality at the same bitrate, or the same quality at a lower bitrate.
The addroi filter makes this accessible from the command line[reference:61]. With qoffset values ranging from -1.0 to +1.0, you have fine‑grained control over quality allocation[reference:62]. Multiple regions can be defined[reference:63], and dynamic ROI with zmq opens the door to real‑time applications like live streaming and surveillance[reference:64].
With the FFmpegLab IDE's visual feedback, you can experiment, debug, and perfect your ROI settings without the guesswork. Drag rectangles, adjust qoffset with sliders, and see the results in real time.
Next time you're encoding a video where some parts matter more than others, reach for ROI encoding. Your viewers—and your bandwidth budget—will thank you.