Files
rustdesk/libs/scrap
IronCodeStudios 377547fa11 scrap/wayland: insert videoconvert to fix screencast on COSMIC / DMA-BUF portals (#15063)
On Wayland compositors whose xdg-desktop-portal backend exposes screencast
frames as DMA-BUF buffers — notably xdg-desktop-portal-cosmic 0.1.0 on
Pop!_OS 24.04 / COSMIC — inbound screen capture fails. PipeWireRecorder
links pipewiresrc directly to an appsink whose caps only accept
video/x-raw BGRx/RGBx in system memory. That format set is too narrow for
the portal's buffer-type / modifier negotiation, which collapses with:

  pw.link: negotiating -> error no more output formats (-22)
  gstpipewiresrc: stream error: no more output formats
  gstbasesrc: streaming stopped, reason not-negotiated (-4)
  ERROR src/server/wayland.rs: Failed scrap Element failed to change its state

Inserting a videoconvert element between pipewiresrc and appsink widens
the negotiable format set to any system-memory video/x-raw format, giving
the portal room to settle on a format it can deliver via its SHM path.
videoconvert then converts to the BGRx/RGBx the appsink expects.

Verified on Pop!_OS 24.04 / COSMIC with gst-launch, before and after:

  # fails (current behaviour):
  gst-launch-1.0 pipewiresrc path=N ! video/x-raw,format=BGRx ! fakesink
  # works (with this change):
  gst-launch-1.0 pipewiresrc path=N ! videoconvert ! video/x-raw,format=BGRx ! fakesink

After the change, inbound connections capture and stream the desktop
normally and the "Failed scrap" error no longer occurs.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 16:02:23 +08:00
..
2025-01-20 17:59:36 +08:00
2021-03-29 15:59:14 +08:00
2021-07-29 13:33:26 +08:00

Derived from https://github.com/quadrupleslap/scrap

scrap

Scrap records your screen! At least it does if you're on Windows, macOS, or Linux.

Usage

[dependencies]
scrap = "0.5"

Its API is as simple as it gets!

struct Display; /// A screen.
struct Frame; /// An array of the pixels that were on-screen.
struct Capturer; /// A recording instance.

impl Capturer {
    /// Begin recording.
    pub fn new(display: Display) -> io::Result<Capturer>;

    /// Try to get a frame.
    /// Returns WouldBlock if it's not ready yet.
    pub fn frame<'a>(&'a mut self) -> io::Result<Frame<'a>>;

    pub fn width(&self) -> usize;
    pub fn height(&self) -> usize;
}

impl Display {
    /// The primary screen.
    pub fn primary() -> io::Result<Display>;

    /// All the screens.
    pub fn all() -> io::Result<Vec<Display>>;

    pub fn width(&self) -> usize;
    pub fn height(&self) -> usize;
}

impl<'a> ops::Deref for Frame<'a> {
    /// A frame is just an array of bytes.
    type Target = [u8];
}

The Frame Format

  • The frame format is guaranteed to be packed BGRA.
  • The width and height are guaranteed to remain constant.
  • The stride might be greater than the width, and it may also vary between frames.

System Requirements

OS Minimum Requirements
macOS macOS 10.8
Linux XCB + SHM + RandR
Windows DirectX 11.1