Search existing issues
Describe the bug
On GNOME/Wayland with an AMD GPU, recordings of the whole monitor play back frozen/juddery — the video sticks on a frame for a fraction of a second, then jumps. Recording a single window of the same content is noticeably smoother, and OBS recording the same monitor on the same machine is smooth.
After instrumenting the PipeWire helper and comparing it against OBS, this is not an encoder problem — it is a frame delivery problem. On this compositor the helper is forced onto the shm/memfd capture path, and mutter delivers far fewer frames over shm than over dmabuf (which OBS uses).
Measured on the same machine (full monitor 1920×1080, VAAPI encode). Distinct/non-duplicate frames via ffmpeg -vf mpdecimate:
| Capture |
Distinct fps |
| OpenScreen — full monitor, editor scrolling |
2.1 – 10.9 |
| OpenScreen — window |
~9.6 |
| OBS — full monitor, editor scrolling |
24.3 |
The saved files are valid 60fps CFR, but the content only changes a few times per second; the rest are held duplicates (the "stuck" look). The helper's dropped-frame counter is ~0 every run — it is not discarding frames it received; mutter simply never sends them. The encoder is fine and ruled out: backend vaapi, ~3 ms/frame, 0–7 dropped frames per recording.
Expected behavior
A whole-monitor recording should capture motion as smoothly as a window recording (and as OBS does on the same machine) — i.e. deliver frames at up to the negotiated maxFramerate (~60), not collapse to a few distinct frames per second.
To Reproduce
- GNOME/Wayland session on an AMD GPU (mutter is the ScreenCast producer).
- Record the whole monitor while a mostly-static app updates a small region (e.g. scrolling/typing in a WYSIWYG editor).
- Play back the file — motion is frozen/juddery. Recording the same app as a window is smoother.
- Confirm objectively:
ffmpeg -i <file> -vf mpdecimate -an -f null - reports only a few distinct frames/sec despite a 60fps file.
Screenshots
No response
OS
Linux
OS Version
Arch Linux — GNOME/Wayland (mutter), AMD Radeon (radeonsi/renoir), PipeWire 1.6.8, app 1.10.0
Other OS
No response
Browser
None
Browser Version
No response
Other Browser
No response
Device Type
Desktop
Other Device
No response
Additional context
Root cause. The helper offers the shm format first and dmabuf second, and on GNOME/KDE shm always wins (by design — electron/native/pipewire-capture/csrc/pw_shim.c, ~L1318-1342). The hidden cost is that mutter's shm delivery for a whole monitor is heavily throttled (it must GPU→CPU copy each full-screen frame), so the recorder is starved of distinct frames and pads to 60fps CFR by holding the last picture.
Negotiated PipeWire format (pw-cli enum-params <node> Format) — framerate is identical; the only difference is the buffer type:
| Param |
OBS |
OpenScreen helper |
framerate |
0/1 (variable) |
0/1 (variable) |
maxFramerate |
~60 |
~60 |
| buffer type |
dmabuf (modifier present) |
shm/memfd (no modifier) |
The helper cannot use dmabuf on this hardware. Forcing it (OPENSCREEN_PIPEWIRE_FORCE_DMABUF=1) fails outright:
stream-error: PipeWire stream reported an error in state error: no more input formats
osc_build_enum_format_dmabuf only advertises LINEAR/INVALID modifiers because frames are read via a CPU mmap (only linear buffers are mmap-able), but on AMD/mutter the monitor framebuffer is tiled — so mutter can't return a linear dmabuf and negotiation dies. That is exactly why the code defaults to shm. OBS works because it consumes the tiled dmabuf on the GPU, never touching the shm path. A window is less affected because the smaller surface lets mutter's per-frame shm copy keep up.
Suggested fix. Consume tiled dmabuf on the GPU (EGL/Vulkan import → GPU encode, or GPU blit to linear) instead of CPU-mmap-ing linear buffers — i.e. match OBS's capture path. This is the only way off the throttled shm path on AMD/GNOME, and it is a substantial change to the dmabuf read path in pw_shim.c + capture.rs, not a config tweak. Framerate/maxFramerate negotiation is not a lever — the helper already matches OBS there.
Related but distinct: #287 and #92 are dmabuf/negotiation failures on niri/wlroots; this issue is about degraded delivery on GNOME where capture succeeds but is starved over shm.
Search existing issues
Describe the bug
On GNOME/Wayland with an AMD GPU, recordings of the whole monitor play back frozen/juddery — the video sticks on a frame for a fraction of a second, then jumps. Recording a single window of the same content is noticeably smoother, and OBS recording the same monitor on the same machine is smooth.
After instrumenting the PipeWire helper and comparing it against OBS, this is not an encoder problem — it is a frame delivery problem. On this compositor the helper is forced onto the shm/memfd capture path, and mutter delivers far fewer frames over shm than over dmabuf (which OBS uses).
Measured on the same machine (full monitor 1920×1080, VAAPI encode). Distinct/non-duplicate frames via
ffmpeg -vf mpdecimate:The saved files are valid 60fps CFR, but the content only changes a few times per second; the rest are held duplicates (the "stuck" look). The helper's dropped-frame counter is ~0 every run — it is not discarding frames it received; mutter simply never sends them. The encoder is fine and ruled out: backend
vaapi, ~3 ms/frame, 0–7 dropped frames per recording.Expected behavior
A whole-monitor recording should capture motion as smoothly as a window recording (and as OBS does on the same machine) — i.e. deliver frames at up to the negotiated
maxFramerate(~60), not collapse to a few distinct frames per second.To Reproduce
ffmpeg -i <file> -vf mpdecimate -an -f null -reports only a few distinct frames/sec despite a 60fps file.Screenshots
No response
OS
Linux
OS Version
Arch Linux — GNOME/Wayland (mutter), AMD Radeon (radeonsi/renoir), PipeWire 1.6.8, app 1.10.0
Other OS
No response
Browser
None
Browser Version
No response
Other Browser
No response
Device Type
Desktop
Other Device
No response
Additional context
Root cause. The helper offers the shm format first and dmabuf second, and on GNOME/KDE shm always wins (by design —
electron/native/pipewire-capture/csrc/pw_shim.c, ~L1318-1342). The hidden cost is that mutter's shm delivery for a whole monitor is heavily throttled (it must GPU→CPU copy each full-screen frame), so the recorder is starved of distinct frames and pads to 60fps CFR by holding the last picture.Negotiated PipeWire format (
pw-cli enum-params <node> Format) — framerate is identical; the only difference is the buffer type:framerate0/1(variable)0/1(variable)maxFramerate~60~60modifierpresent)modifier)The helper cannot use dmabuf on this hardware. Forcing it (
OPENSCREEN_PIPEWIRE_FORCE_DMABUF=1) fails outright:osc_build_enum_format_dmabufonly advertisesLINEAR/INVALIDmodifiers because frames are read via a CPUmmap(only linear buffers are mmap-able), but on AMD/mutter the monitor framebuffer is tiled — so mutter can't return a linear dmabuf and negotiation dies. That is exactly why the code defaults to shm. OBS works because it consumes the tiled dmabuf on the GPU, never touching the shm path. A window is less affected because the smaller surface lets mutter's per-frame shm copy keep up.Suggested fix. Consume tiled dmabuf on the GPU (EGL/Vulkan import → GPU encode, or GPU blit to linear) instead of CPU-
mmap-ing linear buffers — i.e. match OBS's capture path. This is the only way off the throttled shm path on AMD/GNOME, and it is a substantial change to the dmabuf read path inpw_shim.c+capture.rs, not a config tweak. Framerate/maxFrameratenegotiation is not a lever — the helper already matches OBS there.Related but distinct: #287 and #92 are dmabuf/negotiation failures on niri/wlroots; this issue is about degraded delivery on GNOME where capture succeeds but is starved over shm.