Skip to content

Instantly share code, notes, and snippets.

@pojntfx
Last active June 11, 2024 05:33
Show Gist options
  • Save pojntfx/c3bbbf6dbf3759e75607c5e085ab8740 to your computer and use it in GitHub Desktop.
Save pojntfx/c3bbbf6dbf3759e75607c5e085ab8740 to your computer and use it in GitHub Desktop.
Stream Wayland desktop over network with Pipewire, GStreamer and MJPEG
#!/bin/bash
gst-launch-1.0 tcpclientsrc host=localhost port=5000 ! multipartdemux ! jpegdec ! videoconvert ! autovideosink
#!/usr/bin/python3
# See https://gitlab.gnome.org/-/snippets/19
import re
import signal
import dbus
from gi.repository import GLib, GObject, Gst
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
Gst.init(None)
loop = GLib.MainLoop()
bus = dbus.SessionBus()
request_iface = 'org.freedesktop.portal.Request'
screen_cast_iface = 'org.freedesktop.portal.ScreenCast'
pipeline = None
request_token_counter = 0
session_token_counter = 0
sender_name = re.sub(r'\.', '_', bus.get_unique_name()[1:])
def new_request_path():
global request_token_counter
request_token_counter += 1
token = f'u{request_token_counter}'
return f'/org/freedesktop/portal/desktop/request/{sender_name}/{token}', token
def new_session_path():
global session_token_counter
session_token_counter += 1
token = f'u{session_token_counter}'
return f'/org/freedesktop/portal/desktop/session/{sender_name}/{token}', token
def screen_cast_call(method, callback, *args, options={}):
request_path, request_token = new_request_path()
bus.add_signal_receiver(callback, 'Response', request_iface, 'org.freedesktop.portal.Desktop', request_path)
options['handle_token'] = request_token
method(*args, options, dbus_interface=screen_cast_iface)
portal = bus.get_object('org.freedesktop.portal.Desktop', '/org/freedesktop/portal/desktop')
screen_cast_call(portal.CreateSession,
lambda response, results: (
print(f"Failed to create session: {response}") if response != 0 else (
globals().update({'session': results['session_handle']}),
print(f"session {results['session_handle']} created"),
screen_cast_call(portal.SelectSources,
lambda response, results: (
print(f"Failed to select sources: {response}") if response != 0 else (
print("sources selected"),
screen_cast_call(portal.Start,
lambda response, results: (
print(f"Failed to start: {response}") if response != 0 else (
print("streams:"),
[(
print(f"stream {node_id}"),
(pipeline := Gst.parse_launch(f'pipewiresrc fd={portal.OpenPipeWireRemote(session, dbus.Dictionary(signature="sv"), dbus_interface=screen_cast_iface).take()} path={node_id} ! videorate ! video/x-raw,framerate=60/1 ! videoconvert ! clockoverlay shaded-background=true font-desc="Sans 38" ! queue ! jpegenc ! multipartmux ! queue ! tcpserversink host=0.0.0.0 port=5000')).set_state(Gst.State.PLAYING),
pipeline.get_bus().connect('message',
lambda bus, message: (
(pipeline.set_state(Gst.State.NULL), loop.quit()) if message.type in (Gst.MessageType.EOS, Gst.MessageType.ERROR) else None
)
)
) for node_id, _ in results['streams']]
)
),
session, ''
)
)
),
session, options={'multiple': False, 'types': dbus.UInt32(1 | 2)}
)
)
),
options={'session_handle_token': new_session_path()[1]}
)
try:
loop.run()
except KeyboardInterrupt:
pipeline.set_state(Gst.State.NULL) if pipeline else None
loop.quit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment