B and C open on the same shot. There’s a man in headphones, a black rectangle behind him, and no sign of the blue checkerboard underneath. Both files report ALPHA_MODE=1. One stays opaque. The other reveals the checkerboard for about 83 milliseconds.
All three clips come from the same BGRemover demo. A keeps the cutout. B and C are test files made from it. The checkerboard is behind each decoded frame so you can see where the alpha lets it through.
All three files give the same answer
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,pix_fmt:stream_tags=alpha_mode \
-of default=noprint_wrappers=1 C.webm
FFprobe 8.0.1 reported this for A, B and C.
codec_name=vp9
pix_fmt=yuv420p
TAG:ALPHA_MODE=1
ALPHA_MODE=1 describes what the track signals. A frame can still have an alpha plane filled with 255, which makes the entire picture opaque. The yuv420p line is easy to misread here too. All three files exposed an alpha plane when decoded with libvpx.
The decoder changes what you can inspect
ffmpeg -c:v libvpx-vp9 -i A.webm -vf alphaextract -frames:v 1 alpha.png
On the test build, omitting -c:v libvpx-vp9 gave Requested planes not available on A. Put it before -i to select the input decoder. The PNG that comes out shows the alpha plane in grayscale. Black is transparent and white is opaque. FFmpeg filter reference
The flash is on frame 14
| File | Alpha tag | First frame passes? | Frames that pass |
|---|---|---|---|
| A | 1 |
Yes | 24 / 24 |
| B | 1 |
No | 0 / 24 |
| C | 1 |
No | 1 / 24 |
Frame 14 of C starts about 1.08 seconds in. Checking at 0, 0.5, 1 and 1.5 seconds skips it entirely. If you paused B and C at the start and inspected the alpha plane, both would look solid white.
Recreate the test
Save the demo as source.webm. The commands below take two seconds at 12 fps. B sets alpha to 255 throughout. C does the same except at index 13, which is frame 14 when counting from one. Download the demo WebM
Commands to build A, B and C (Bash)
encode() {
ffmpeg -v error -c:v libvpx-vp9 -i source.webm -map 0:v:0 \
-vf "fps=12,scale=640:360,format=rgba,$2" -frames:v 24 -an \
-c:v libvpx-vp9 -lossless 1 -auto-alt-ref 0 "$1.webm"
}
encode A "format=yuva420p"
encode B "lutrgb=a=maxval,format=yuva420p"
encode C "lutrgb=a=maxval:enable='not(eq(n,13))',format=yuva420p"
To count passing frames, the script checks for both a visible subject and room for a background to show through. On the same frame, at least 1% of pixels must have alpha below 245 and at least 1% must have alpha above 10. It measures a 320 × 180 nearest-neighbor copy of the alpha plane. That’s enough to catch a solid rectangle in this test. For a one-pixel fringe, inspect the mask at full resolution.
Python script to scan every decoded frame
import subprocess
import sys
if len(sys.argv) != 2:
raise SystemExit('Usage: python scan-alpha.py INPUT.webm')
# One byte per pixel after alphaextract and a fixed scale.
frame_bytes = 320 * 180
cmd = [
'ffmpeg', '-nostdin', '-v', 'error', '-c:v', 'libvpx-vp9',
'-i', sys.argv[1], '-map', '0:v:0',
'-vf', 'alphaextract,scale=320:180:flags=neighbor',
'-fps_mode', 'passthrough', '-c:v', 'rawvideo', '-threads:v', '1',
'-pix_fmt', 'gray', '-f', 'rawvideo', 'pipe:1',
]
frames = mixed = 0
examples = []
with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
while frame := proc.stdout.read(frame_bytes):
if len(frame) != frame_bytes:
raise SystemExit('Incomplete decoded frame')
frames += 1
clear = sum(a < 245 for a in frame)
visible = sum(a > 10 for a in frame)
if clear * 100 >= frame_bytes and visible * 100 >= frame_bytes:
mixed += 1
if len(examples) < 5:
examples.append(frames)
if proc.wait() != 0 or frames == 0:
raise SystemExit('No usable alpha scan; check the decoder and input')
print(f'{mixed}/{frames} frames pass; first passing frame numbers: {examples}')
Save it as scan-alpha.py and run python scan-alpha.py C.webm. The test run returned 1/24 frames pass; first passing frame numbers: [14]. You’ll need Python 3.8+ and an FFmpeg build with the libvpx VP9 encoder and decoder.
If a cutout should stay usable for the whole shot, check every frame. If the last few frames are supposed to cover the screen, restrict that check to the part of the clip that will be composited.
The source cutout was made using BGRemover’s video tool. Try the video background remover
For a CI check that samples a few frames with Node.js, see the FFmpeg and CI walkthrough.
If a two-second overlay goes opaque for one frame in the middle, would you fail the build or ship it with a warning?
The prose was drafted and rewritten with AI. Original stock footage is from Mixkit. Bearded announcer, close up


Top comments (0)