The android.display.video data source records what each physical display showed while a trace was being captured. Perfetto stores the frames as an encoded video stream inside the trace, and the UI adds a per-display timeline track that decodes them in the browser. You can hover the track to preview a frame, play the frames back like a video, and click any frame to line it up with the tracks below it.
It records the actual contents of the screen, so any trace that contains it is sensitive: it shows exactly what was on the display. On userdebug (debuggable) devices it is available out of the box. On user (production) builds it is disabled by default and must be unlocked with a system property first — see Prerequisite on user builds.
This guide covers:
user builds, then the three ways to turn it on — the on-device toggle, the record page, and a raw config with full control over quality and size.While the data source is enabled, the device encodes what each display shows into a video stream stored in the trace — one frame each time the screen changes — and the UI decodes it back in the browser. This has two costs:
scale and max_stream_size_bytes options below keep it bounded.When the data source is off, it costs nothing.
There are three ways to turn on display-video capture, from the simplest to the most control. On user builds there is also a one-time-per-boot property to set first — see the prerequisite below.
user buildsOn userdebug (debuggable) devices, display-video capture works out of the box, and you can skip this step. On user (production) builds it is disabled by default: unlock it first by setting a system property over ADB.
adb shell setprop debug.tracing_video_allowed true
This property is not persistent — it is cleared on the next reboot. After the device restarts you have to set it again before you can capture display video. Once it is set, capture works through any of the methods below: the on-device toggle, the record page, or a raw config.
The System Tracing app has a Record display video toggle under Trace settings. Enable it, then record a trace as usual — the capture is included automatically. This is the quickest route on a device you are holding — no config to write.
Open the Perfetto UI record page, find Display video frames under the Android probes, and enable it. This adds the android.display.video data source to the generated config with the producer's default settings.
For full control, write the config yourself. Enable android.display.video, and add a display_video_config to set quality and size. With no options, each display uses the device's default settings:
data_sources {
config {
name: "android.display.video"
}
}
Add a display_video_config to tune the capture. Every field is optional; an unset or zero field uses the producer default.
data_sources {
config {
name: "android.display.video"
display_video_config {
scale: 0.5
format: FORMAT_H264
key_frame_interval_secs: 2
bitrate_bps: 8000000 # 8 Mbps
max_stream_size_bytes: 67108864 # 64 MiB per display
}
}
}
| Option | Description |
|---|---|
scale | Factor applied to each display's resolution before capture, e.g. 0.5 for half size or 0.25 for quarter. Lower scale means less encoder load and a smaller trace, at the cost of detail. |
format | FORMAT_H264 (the default) or FORMAT_HEVC. HEVC produces a smaller stream at the same quality, but the device must support HEVC encoding to capture it and the browser must support HEVC decoding to preview it. |
key_frame_interval_secs | How often a keyframe is emitted. Smaller values make seeking snappier but grow the trace; larger values are more compact but slower to scrub. |
bitrate_bps | Target encoder bitrate, in bits per second. Trace size is roughly bitrate × duration, so this trades quality against size at a fixed resolution (unlike scale, which lowers the resolution). Left unset, the device picks a default. |
max_stream_size_bytes | A per-display cap on emitted bytes. When a display hits it, its stream is torn down (a size-cap error) rather than growing without bound. Left unset, the device applies a default cap of 256 MiB per display. |
Display video is limited to 256 MiB per display, enforced in two separate places:
max_stream_size_bytes caps how much each stream emits (256 MiB by default; a size-cap error is recorded when it is hit).On a long session or at a high resolution, the video can therefore stop before the end of the trace. Both limits exist because of the memory cost of holding the stream, not a fundamental constraint. If they get in your way, comment on and upvote the tracking issue so it can be prioritised: perfetto#6609.
A trace that contains display video shows a Video Frames group with one track per display (for a phone, typically a single Built-in Screen). Each slice on the track is one captured frame, labelled with a frame number. That number is just a sequential counter of captured frames — it has nothing to do with the vsync ids in the frame timeline.
Move the pointer along the track to preview frames. The frame under the cursor is decoded and shown as a thumbnail above the row, so you can scrub to find the frame you want.
Click a frame to open its details. The panel shows the frame number and timestamp on the left and a decoded Preview on the right, with playback controls in the header: previous frame, play/pause, next frame, and a playback-speed selector. Press play and the capture runs as a video: the preview advances through the frames and the timeline selection moves with it, so the rest of the UI stays lined up with what is on screen. Step one frame at a time with the previous/next buttons, or change the speed selector to play back slower (down to 0.1×) or faster (up to 2×).
tools/trace_video_conv.py pulls the captured video out of a trace into an .mp4 using ffmpeg (the encoded frames are copied as-is, not re-encoded). It needs ffmpeg on the PATH; trace_processor is downloaded automatically, or pass --trace-processor to use a local build.
# List the video streams in a trace. tools/trace_video_conv.py TRACE.perfetto-trace --list # Convert the whole video to an .mp4. tools/trace_video_conv.py TRACE.perfetto-trace -o out.mp4 # Clip to a time range (trace ts, ns), or to whatever a query selects # (the query returns a `ts` column, and optionally `dur`). tools/trace_video_conv.py TRACE.perfetto-trace -o clip.mp4 --start <ts> --end <ts> tools/trace_video_conv.py TRACE.perfetto-trace -o clip.mp4 \ --query "SELECT ts, dur FROM slice WHERE name = 'my_cuj'" # Slow motion (0.5x) or 2x faster. tools/trace_video_conv.py TRACE.perfetto-trace -o out.mp4 --speed 0.5 # Two traces side by side, each captioned (defaults to the file names). tools/trace_video_conv.py before.perfetto-trace --compare after.perfetto-trace \ -o compare.mp4 --title Before --title2 After
| Option | Description |
|---|---|
-o, --output | Output .mp4 path. |
--list | List the trace's video streams and exit. |
--display-id | Which stream to use, for a trace with more than one display. |
--start, --end | Clip to a time range, in trace ts nanoseconds. |
--query | Clip to the region a SQL query selects (returns ts, optionally dur). |
--speed | Playback speed of the output: 2 = twice as fast, 0.5 = slow motion. |
--compare | A second trace, placed to the right for a side-by-side comparison. |
--display-id2 | Which stream to use from the --compare trace. |
--start2, --end2 | Clip the --compare trace to a time range. |
--query2 | Clip the --compare trace to a SQL-selected region. |
--title, --title2 | Captions for the first and second videos (default: the file names). |
--trace-processor | Path to a local trace_processor build (otherwise one is downloaded). |