Diagnosing OpenCV VideoWriter Failures: FourCC, Codecs, and Silent Corruption
OpenCV's VideoWriter fails silently for bad FourCC codes, missing FFMPEG backends, and frame-size mismatches. A diagnostic order that finds the cause fast.
12 Dec 2025, 19:02 UTC

The symptom: a file that exists but won't play
You call cv::VideoWriter (or cv2.VideoWriter in Python), write frames without an exception, and end up with a 0-byte file, a file that plays only audio-less green garbage, or a writer that never opened in the first place. In almost every case the root cause is one of four things: a FourCC the FFMPEG backend can't map to the container, an FFMPEG backend that isn't compiled in, a frame-size mismatch, or a codec/container combination your platform's FFMPEG build doesn't support. This guide walks through the checks in the order that finds the problem fastest.
Assumptions below: OpenCV 4.x with the default FFMPEG video backend on desktop Linux/Windows/macOS. Behavior in OpenCV 3.x is similar but API details differ slightly.
Cause and diagnostic table
| Observation | Likely cause | First check |
|---|---|---|
isOpened() returns false immediately | Unsupported FourCC, bad container extension, or no FFMPEG backend | Print backend name and test a known-good FourCC |
| Writer opens but output file is 0 bytes or unplayable | Silent fallback, or frames never actually written | Check return value of write() and frame size |
| Some frames missing / video shorter than expected | Frame size differs from writer's declared size | Compare frame.size() to the writer's Size argument |
| Latency spikes during writing | CUDA GpuMat being downloaded implicitly per frame | Explicit download() before write() |
| Works on one machine, fails on another | Different OpenCV build (WITH_FFMPEG off) or different system codecs | cv::getBuildInformation() |
Check 1: Confirm the writer actually opened
The single most common mistake is never checking isOpened(). VideoWriter fails silently by design — no exception is thrown for a bad codec. Run this where your application runs, with the same OpenCV library your app loads:
import cv2
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('test.mp4', fourcc, 30.0, (640, 480))
print('opened:', out.isOpened())
print('backend:', cv2.getBuildInformation()) # search for FFMPEG: YES
out.release()
No special permissions are needed beyond write access to the output directory. If isOpened() is false, do not proceed to writing frames — fix the open failure first. In C++ the equivalent is writer.isOpened() after construction.
Check 2: Verify FFMPEG is in your OpenCV build
If OpenCV was built without WITH_FFMPEG, most compressed codecs simply won't work regardless of the FourCC you pass. In Python, run print(cv2.getBuildInformation()) and look for the FFMPEG: YES line in the Video I/O section. If it says NO, no FourCC tuning will help — you need a different OpenCV build (the official opencv-python wheels ship with FFMPEG enabled; some distro or custom minimal builds do not).
Check 3: Match the FourCC to the container
FourCC (four-character code) identifies the codec; the file extension identifies the container. FFMPEG must support the pair. Known-good starting points:
| Container | FourCC | Notes |
|---|---|---|
| .mp4 | mp4v | MPEG-4 Part 2; widely supported, moderate compression |
| .avi | MJPG | Motion JPEG; large files, very compatible, but missing on some minimal builds |
| .avi | XVID | Good fallback when MJPG fails |
| .mkv | H264 | Depends on an H.264 encoder being present in FFMPEG; often absent in default builds due to licensing |
If your preferred codec fails, don't guess randomly — test candidates in a loop and keep the first one where isOpened() returns true. Note that a failed VideoWriter may leave a 0-byte file behind; delete it before retrying the same path.
Check 4: Enforce frame size consistency
The Size passed to the writer is a contract: every frame written must match it exactly (width, height, and channel layout — BGR, 3 channels). A frame from a camera at 1280x720 written to a writer declared at 640x480 typically produces dropped frames or a corrupt stream, again without an exception. Resize explicitly before writing:
W, H = 640, 480
out = cv2.VideoWriter('out.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 30.0, (W, H))
if not out.isOpened():
raise RuntimeError('writer failed to open — check FourCC/backend')
for frame in frame_source:
if frame.shape[1] != W or frame.shape[0] != H:
frame = cv2.resize(frame, (W, H))
if frame.ndim == 2: # grayscale source
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
out.write(frame)
out.release() # required: finalizes the container
Two details matter here. First, release() is not optional — without it the container's index/moov atom is never written and the file is unplayable even if every frame succeeded. Second, if you write from multiple threads, serialize the write() calls with a lock; VideoWriter is not thread-safe.
Check 5: CUDA frames and latency spikes
If frames come from a CUDA pipeline (cv::cuda::GpuMat), the writer needs host memory. Relying on implicit conversion causes an uncontrolled GPU-to-CPU copy per frame. Download once, explicitly, and time it if you suspect stalls:
cv::cuda::GpuMat gpuFrame = /* ... */;
cv::Mat hostFrame;
gpuFrame.download(hostFrame); // explicit, measurable transfer
writer.write(hostFrame);
If download() dominates your per-frame budget, batch frames or move encoding off the critical path rather than shrinking the video.
Verify the output independently
Don't trust your own player. Use ffprobe (from the FFmpeg tools, run in a shell, no elevated permissions needed) to confirm what actually landed on disk:
ffprobe -v error -show_entries stream=codec_name,width,height,nb_frames \
-of default=noprint_wrappers=1 out.mp4
Check that codec_name matches what you requested, dimensions match the writer's Size, and nb_frames equals the number of write() calls you made. A mismatch in frame count points back to Check 4; a wrong codec points to Check 3.
When to escalate
Stop debugging your code and look at the environment when: (a) a known-good FourCC like mp4v in .mp4 fails isOpened() on a minimal 10-line test program, (b) getBuildInformation() shows FFMPEG missing, or (c) the same code works on one machine and fails on another with identical input. At that point the fix is a different OpenCV build or a system-level codec issue, not application code. Capture the minimal test program, the build information output, and the ffprobe result before handing the problem off — those three artifacts distinguish a build problem from a usage problem immediately.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.