Pipewiresrc not using full framerate

My physical monitor is set to 800x600 resolution for simplicity.

I’m using the script from Immersed-Linux-Virtual-Monitors/scripts/wayland-scripts/dbus_screen_cast.py at main · augustoicaro/Immersed-Linux-Virtual-Monitors · GitHub which creates a pipewire node and connects the stream to it by path. When run with my monitor set to 800x600 and replacing the sink with fpsdisplaysink video-sink=autovideosink I get a framerate of a whopping 40fps. For an 800x600 stream.

I’m on Ubuntu 24.04 AND Gnome 46. This isn’t acceptable for a stream of this size and a monitor refresh rate of 60hz. Clearly something here isn’t working. I’ve tried playing with keepalive-time and nothing changes.

Someone please point out how you’re supposed to record a 60fps stream with pipewire. OBS seems to manage it fine.

Pipewiresrc comes with some quirks, I believe it does limited allocations, and has no tolerance to delivery delays.

My very first suggestion would be to add a ‘queue’ element right after pipewiresrc. This should help prevent losing frame at the source.

My second recommendation would to to disable the last sample feature on the videosink. This avoid holding on frames, since pipewire does not offer this feature anyway.

Finally, make sure pipewire frames are transformed or copied ASAP, doing zero copy with it often results in starvation (you cannot alloted more buffer, it’s pipewire that decides).

Finally, try a more modern videosink, could be gtk4paintable, glimagesink, waylandsink, qml6sink, it all depends on your end usage.

Thanks for these tips, they don’t seem to help though. I still get a pitiful 40fps with a queue and enable-last-sample set to false.

gst-launch-1.0 pipewiresrc path=$1 ! fpsdisplaysink video-sink=“waylandsink enable-last-sample=false”

This still gives me ~40fps average and it visually appears to be 40.

I don’t think pipewiresrc is going to suit my needs. I’m trying to get a stream of my desktop that I can send over gstreamer in Wayland and it seems I’ll need to find some other method of doing so. I still wonder how OBS is capable of doing this at a solid 60fps while I struggle.

If anyone knows any better methods for capturing a desktop stream that works on Ubuntu with Mutter I would love to know.

You missed the queue part of my comment, I believe dmabuf negotiation is slightly broken in pipewiresrc and you may need to force it, we should really reimplement this in upstream gstreamer so we can keep it up-to-date. OBS does use DMAbuf whenever possible to avoid copied in the path.

gst-launch-1.0 pipewiresrc path=$1 ! video/x-raw\(memory:DMABuf\) ! queue ! fpsdisplaysink video-sink=“waylandsink enable-last-sample=false”

In case you wonder what OBS does:

Oops! I was using a queue but Ubuntu crashed at some point and I forgot to add it back in when I was copy pasting it here.

Forcing DMABuf doesn’t seem to work.

Terminal:~$ gst-launch-1.0 pipewiresrc path=56 ! video/x-raw(memory:DMABuf) ! queue ! fpsdisplaysink video-sink=“waylandsink enable-last-sample=false”
Setting pipeline to PAUSED …
ERROR: from element /GstPipeline:pipeline0/GstPipeWireSrc:pipewiresrc0: No supported formats found
Additional debug info:
../src/gst/gstpipewiresrc.c(983): gst_pipewire_src_negotiate (): /GstPipeline:pipeline0/GstPipeWireSrc:pipewiresrc0:
This element does not have formats in common with the peer
ERROR: pipeline doesn’t want to preroll.
*** pw_stream_set_error called from wrong context, check thread and locking: Operation not permitted
ERROR: from element /GstPipeline:pipeline0/GstPipeWireSrc:pipewiresrc0: stream error: No supported formats found
Additional debug info:
../src/gst/gstpipewiresrc.c(692): on_state_changed (): /GstPipeline:pipeline0/GstPipeWireSrc:pipewiresrc0
ERROR: pipeline doesn’t want to preroll.
ERROR: from element /GstPipeline:pipeline0/GstPipeWireSrc:pipewiresrc0: Internal data stream error.
Additional debug info:
../libs/gst/base/gstbasesrc.c(3177): gst_base_src_loop (): /GstPipeline:pipeline0/GstPipeWireSrc:pipewiresrc0:
streaming stopped, reason not-negotiated (-4)
ERROR: pipeline doesn’t want to preroll.
Failed to set pipeline to PAUSED.
Setting pipeline to NULL …
Freeing pipeline …
Terminal:~$

This is the full log when run with log level 4: Terminal:~$ GST_DEBUG=4 gst-launch-1.0 pipewiresrc path=56 ! video/x-raw\(memory - Pastebin.com

I’m a complete gstreamer noob but it sounds like pipewiresrc isn’t capable of offering DMABuf frames? Or if it is that I’m missing something vital in getting it to do so which might be a source of my problems.

It’s surprising that waylandsink wouldn’t support the same formats, after all it’s buffers coming from the compositor and going back into the compositor.

Meanwhile, does glimagesink or vapostproc works? Otherwise, there is likely some bugs implied.

Sorry it’s been a week but no. I’m still struggling with this and nether glimagesink nor vapostproc work with it.

pipewiresrc does take an fd which I’ve now given it but I’m not sure what it’s supposed to do. Nothing has changed and it still won’t output DMABuf even if forced.

I don’t know if the FPS display for pipewiresink is just broken or what. I take the GStreamer frames into glimagesink which renders them to ANativeWindow and they’re displayed by an Open XR compositor layer in Unity.

With videotestsrc at 4K is looks perfectly fine and no obvious lag or dropped frames.

With the ball pattern the ball moves smoothly and looks good.

With pipewiresrc at 1280x720 it seems to be 60fps for about 3 seconds and then starts to slow with noticeable gaps in between mouse movements. If the same pipelines are adapted for a PC the fpsdisplaysink reports it only being 40FPS despite the Android stream appearing to be 60.

With pipewiresrc at 1920x1080 it’s immediately evident that it’s not 60fps and exhibits the same behavior as above.

My pipelines right now are

Sender

pipewiresrc path={node} fd={fd} ! video/x-raw,width={width},height={height} ! queue max-size-buffers=1 max-size-time=0 max-size-bytes=0 leaky=downstream ! nvh264enc bitrate=25000 gop-size=30 zerolatency=true preset=low-latency-hq rc-mode=cbr qp-min=10 qp-max=20 ! rtph264pay pt=96 mtu=1200 config-interval=1 aggregate-mode=1 ! udpsink host=192.168.0.230 port={port} sync=false async=false qos=false

Receiver

udpsrc port=%d buffer-size=4194304 caps="application/x-rtp, media=video, encoding-name=H264, payload=96" ! queue ! rtph264depay ! h264parse ! amcviddec-c2qtiavcdecoderlowlatency ! video/x-raw(memory:GLMemory) ! glimagesink name=unity sync=false

EDIT: It only looks really awful when recording my physical monitor? It looks great and smooth on virtual ones…

I’m thinking this might just be a Gnome issue: Choopy Desktop Capture using Pipewire and OBS Studio 30.0.2 on Wayland - #24 by agarplayerarlon - Linux - NVIDIA Developer Forums

Which kind of sucks but not much I can do besides update and see if it gets better I guess.

EDIT 2: I’ve tried on KDE and the framerate is so much better but physical screens still exhibit the same stutter after a few seconds that physical screens on GNOME did. I hate pipewire