What Is signalstats

signalstats is an FFmpeg filter that analyzes video. For each frame, it calculates statistics for brightness (luma, Y) and color (chroma, Cb/Cr) and stores them as frame metadata (extra information attached to each frame). The values cover the whole frame, so to get the backdrop color for green-screen compositing with chromakey, crop to an area that shows only the backdrop first. With yuv=1, chromakey reads the color as YUV hex (0xYYUUVV).

Common uses:

  • QC (quality control) of video for broadcast
  • Finding clipping (areas where luma is stuck at 0 or 255)
  • Analyzing the overall noise level of a video, for example before and after applying a box blur
  • Tracking brightness changes frame by frame

Available Statistics

Statistic Description
YMIN Minimum luma value (0–255)
YMAX Maximum luma value (0–255)
YAVG Average luma value
YDIF Average luma difference from the previous frame
UMIN/UMAX/UAVG Cb (blue-difference) component
VMIN/VMAX/VAVG Cr (red-difference) component
SATMIN/SATMAX/SATAVG Saturation
HUEMED/HUEAVG Median and average hue
TOUT Indicator for temporal outlier pixels
VREP Vertical line repetition (detects artifacts such as those from VHS)
BRNG Indicator for pixels outside broadcast range

Basic Usage

Add metadata=mode=print to print the statistics in the terminal (standard error):

ffmpeg -i input.mp4 -vf "signalstats,metadata=mode=print" -f null /dev/null

Example output:

[Parsed_metadata_1 @ ...] lavfi.signalstats.YMIN=16
[Parsed_metadata_1 @ ...] lavfi.signalstats.YMAX=235
[Parsed_metadata_1 @ ...] lavfi.signalstats.YAVG=118.2

Retrieving It as Frame Metadata

To write the signalstats values (frame metadata) to a file, use the file option of the metadata filter:

ffmpeg -i input.mp4 -vf signalstats=stat=tout+vrep+brng,metadata=mode=print:file=stats.txt -f null /dev/null

The per-frame statistics are written to stats.txt.

Exporting as CSV for Graphing

ffprobe -f lavfi -i "movie=input.mp4,signalstats" \
  -show_frames -select_streams v \
  -print_format csv \
  -show_entries frame_tags=lavfi.signalstats.YAVG,lavfi.signalstats.YMIN,lavfi.signalstats.YMAX \
  > signalstats_output.csv

You can graph the exported CSV in Excel or Python.

Overlaying the Average Luma in Real Time

With drawtext, you can draw the statistics on each frame and burn them into the video:

ffmpeg -i input.mp4 \
  -vf "signalstats,drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf:text='YAVG\: %{metadata\:lavfi.signalstats.YAVG}':x=10:y=10:fontsize=20:fontcolor=white" \
  output_stats.mp4

Adding TOUT, VREP and BRNG (the stat Option)

ffmpeg -i input.mp4 \
  -vf "signalstats=stat=tout+vrep+brng,metadata=mode=print" \
  -f null /dev/null
stat value Statistic added
tout TOUT (ratio of temporal-outlier pixels)
vrep VREP (vertical line repetition)
brng BRNG (out-of-broadcast-range pixels)

To specify multiple values, join them with +.

A Practical Broadcast QC Example

Broadcast QC often checks for frames whose luma goes below 16 or above 235. This command lists YMIN and YMAX for every frame so you can find them:

ffprobe -f lavfi \
  -i "movie=input.mp4,signalstats" \
  -show_frames -select_streams v \
  -print_format flat \
  -show_entries frame_tags=lavfi.signalstats.YMIN,lavfi.signalstats.YMAX \
  2>/dev/null | grep -E "(YMIN|YMAX)"

Saving the YAVG Trend to a Text File

Save only the average luma of each frame (YAVG) to a text file:

ffmpeg -i input.mp4 \
  -vf "signalstats,metadata=mode=print:key=lavfi.signalstats.YAVG:file=yavg.txt" \
  -f null /dev/null

Common Usage Summary

Goal Command
Print all statistics to standard error -vf "signalstats,metadata=mode=print" -f null /dev/null
Also print TOUT/BRNG -vf "signalstats=stat=tout+brng,metadata=mode=print" -f null /dev/null
Overlay onto frames signalstats,drawtext=text='%{metadata\:...}'
Save as CSV ffprobe -f lavfi -i "movie=input.mp4,signalstats" ...

Measured: time and size

The measured command:

ffprobe -f lavfi -i "movie=input.mp4,signalstats" -show_frames -select_streams v -print_format csv -show_entries frame_tags=lavfi.signalstats.YAVG
Metric Measured
Wall-clock time 29.56 s
Speed vs realtime 4.06x
Output file none (analysis only)

Test machine: Intel Core i9-14900KF (32 threads), FFmpeg 8.1 (gyan.dev). Source: 1920x1080, 30 fps, 120 s, 351.4 MB (Big Buck Bunny, looped, CC BY 3.0). Measured 2026-09-05, one run at a time. The raw numbers are in the dataset.

Why the number lands there

Skipping the re-encode does not make it fast. Re-encoding the whole source with libx264 (-crf 23 -preset medium) took 27.97 s. The signalstats pass writes no video at all, yet it took 29.56 s, longer than the full re-encode.

Most of the time goes into the statistics, not into decoding (turning the compressed video back into pictures). On the same source, jobs that only copy the streams finished in 0.41 s (stream mapping) and 0.49 s (moving the moov atom), because -c copy never decodes a frame. scdet (scene detection) decodes every frame and took 7.09 s. signalstats also decodes every frame, then calculates luma and chroma statistics over every pixel. Decoding takes about 7 s at most, so the remaining 20-odd seconds are the statistics. Without the x264 encode, the decoding and the statistics are still there, so the time ends up close to the re-encode.

Expect signalstats to take much longer than decoding the clip. The 4.06x figure covers decoding and the statistics together. A clip of twice the length takes roughly twice the time.

The measured command printed one value (-show_entries frame_tags=lavfi.signalstats.YAVG) and sent the CSV to standard output instead of a file, so there is no output file size. 10-bit or 4K material decodes more slowly and takes longer, and 4K also has four times as many pixels to calculate statistics over.

Common Pitfalls

  • Symptom: no metadata is printed. Cause: signalstats on its own stores the values as frame metadata but prints nothing to the screen or the log. Fix: add metadata=mode=print after it (e.g. -vf "signalstats,metadata=mode=print"). The values go to standard error. When you don’t need an output file, use -f null /dev/null.

  • Symptom: 2>/dev/null fails on Windows. Cause: /dev/null is the place Linux and macOS use to throw output away, and it does not exist on Windows. -f null /dev/null still works there, because the null muxer never opens the file. Fix: use 2>NUL in cmd and 2>$null in PowerShell. Saving to a file with metadata=...:file=stats.txt works on every OS.

  • Symptom: TOUT, VREP, and BRNG are missing from the output. Cause: they are not calculated by default and appear only when stat= turns them on. Fix: turn them on, e.g. signalstats=stat=tout+vrep+brng. Join multiple values with +.

  • Symptom: the CSV has an unexpected number of rows. Cause: the frame rate or the total number of frames was not what you expected. Fix: the CSV has one row per frame, so rows ≈ total frames (roughly duration_seconds × fps). For a 2-minute 30 fps clip, expect about 3,600 rows.

FAQ

Q. Does signalstats re-encode the video? A. Not for analysis. If you throw the output away with -f null, nothing is re-encoded, and most of the time goes into the statistics. You need a re-encode only when you burn the values into the video with drawtext.

Q. How do I detect clipping (blown highlights or crushed blacks)? A. YMIN<16 or YMAX>235 finds frames that go outside the broadcast range (16–235 in 8-bit). The broadcast QC example above lists those values for every frame. Clipping can also happen inside that range, so YMIN and YMAX alone cannot detect it. If YHIGH (the brightness that the top 10% of pixels reach) equals YMAX, at least a tenth of the picture is stuck at the maximum. Check crushed blacks the same way with YLOW and YMIN.

Q. What are the units and ranges of the values? A. For 8-bit video, Y, U, and V all run from 0 to 255. For 10-bit sources the range is different, so adjust your thresholds to the bit depth of the source.

Q. Can I collect just one statistic cheaply? A. Not with stat=. The basic statistics (YMIN, YAVG and so on) are always calculated. stat= only adds TOUT, VREP and BRNG on top, so it adds work. To print only one value, pick it with metadata=mode=print:key=lavfi.signalstats.YAVG.

Q. Can I graph the CSV directly? A. Yes. A file written with -print_format csv opens in Excel or pandas (a Python data library). The first column is the word frame, followed by the frame’s own fields (timestamps, picture format and so on). The frame_tags values come last, in the order the filter outputs them (YMIN, YAVG, YMAX), not the order you asked for. Some rows end with an extra side-data (SEI) column.