pythonglibdbuswaylandobs

Wayland XDG remote desktop portal restore_token not working in python3


I am working on a project based off of this example https://gitlab.gnome.org/-/snippets/39

I changed this function:

def on_create_session_response(response, results):
    if response != 0:
        print("Failed to create session: %d"%response)
        terminate()
        return

    global session
    session = results['session_handle']
    print("session %s created"%session)

    remote_desktop_call(portal.SelectDevices, on_select_devices_response,
                        session,
                        options={ 'types': dbus.UInt32(7) })

to this:

def on_create_session_response(response, results):
        if response != 0:
                print("Failed to create session: %d"%response)
                terminate()
                os._exit(2)
                return

        global session
        session = results['session_handle']
        #print(session)
        print("session %s created"%session)
        if restore_token is not None:
            remote_desktop_call(portal.SelectDevices, on_select_devices_response,
                                                session,
                                                options={ 'types': dbus.UInt32(3), 'restore_token': restore_token ,'persist_mode': dbus.UInt32(2)})
            x =GLib.Variant.lookup_value(results,"restore_token",GLib.VariantType("s"))
            print(x)
        else:
            remote_desktop_call(portal.SelectDevices, on_select_devices_response,
                                                session,
                                                options={ 'types': dbus.UInt32(3), 'restore_token': "NULL" ,'persist_mode': dbus.UInt32(2)})

according to the XDG Docs and some browsing of the OBS Studio source code, I should be able to now get a restore token i can capture in this function:

def on_start_response(response, results):
    if response != 0:
        print("Failed to start: %s"%response)
        terminate()
        return
    print(results)
    print("streams:")
    if 'streams' in results:
        for (node_id, stream_properties) in results['streams']:
            print("stream {}".format(node_id))
            play_pipewire_stream(node_id)
    else:
        print("no screen casting")

    print("devices: {}".format(results['devices']))


However I am not getting a restore token. my operating system is ubuntu 23.10 amd64. I have flipped thru the OBS source code screencast-portal.c to check my work. and i tried moving the print statement to try and locate the missing token but it isnt coming up. I should be able to get in the results a value that says "restore_token"


Solution

  • Wayland Remote Desktop doesn't seem to support the restore token but screenshot does.

    I think I was using a unsupported version of Ubuntu and doing what I was trying to do wrong.

    this is how it worked for me.

    #!/usr/bin/python3
    
    import re
    import signal
    import dbus
    import sys
    from gi.repository import Lib
    from dbus.mainloop.glib import DBusGMainLoop
    
    import gi
    gi.require_version('Gst', '1.0')
    from gi.repository import GObject, Gst
    
    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'
    
    restore_token = ''
    if len(sys.argv) > 1:
        restore_token = sys.argv[1]
    
    pipeline = None
    
    def terminate():
        if pipeline is not None:
            self.player.set_state(Gst.State.NULL)
        loop.quit()
    
    request_token_counter = 0
    session_token_counter = 0
    sender_name = re.sub(r'\.', r'_', bus.get_unique_name()[1:])
    
    def new_request_path():
        global request_token_counter
        request_token_counter = request_token_counter + 1
        token = 'u%d'%request_token_counter
        path = '/org/freedesktop/portal/desktop/request/%s/%s'%(sender_name, token)
        return (path, token)
    
    def new_session_path():
        global session_token_counter
        session_token_counter = session_token_counter + 1
        token = 'u%d'%session_token_counter
        path = '/org/freedesktop/portal/desktop/session/%s/%s'%(sender_name, token)
        return (path, 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)
    
    def on_gst_message(bus, message):
        type = message.type
        if type == Gst.MessageType.EOS or type == Gst.MessageType.ERROR:
            terminate()
    
    def play_pipewire_stream(node_id):
        empty_dict = dbus.Dictionary(signature="sv")
        fd_object = portal.OpenPipeWireRemote(session, empty_dict,
                                              dbus_interface=screen_cast_iface)
        fd = fd_object.take()
        pipeline = Gst.parse_launch('pipewiresrc fd=%d path=%u ! videoconvert ! video/x-raw,format=RGB ! videoconvert ! pngenc ! filesink location=output.png'%(fd, node_id))
        pipeline.set_state(Gst.State.PLAYING)
        pipeline.get_bus().connect('message', on_gst_message)
    
    def on_start_response(response, results):
        if response != 0:
            print("Failed to start: %s"%response)
            terminate()
            return
        
        print(f"on start response, restore token: {results['restore_token']}")
    
        print("streams:")
        for (node_id, stream_properties) in results['streams']:
            print("stream {}".format(node_id))
            play_pipewire_stream(node_id)
    
    def on_select_sources_response(response, results):
        if response != 0:
            print("Failed to select sources: %d"%response)
            terminate()
            return
    
        print("sources selected")
    
        global session
        screen_cast_call(portal.Start, on_start_response,
                         session, '')
    
    def on_create_session_response(response, results):
        if response != 0:
            print("Failed to create session: %d"%response)
            terminate()
            return
    
        global session
        session = results['session_handle']
        print("session %s created"%session)
    
        options = { 'multiple': False,
                    'persist_mode': dbus.UInt32(2),
                    'types': dbus.UInt32(1|2) }
        if restore_token != '':
            options['restore_token'] = dbus.String(restore_token)
        screen_cast_call(portal.SelectSources, on_select_sources_response,
                         session,
                         options=options)
    
    portal = bus.get_object('org.freedesktop.portal.Desktop',
                                 '/org/freedesktop/portal/desktop')
    
    (session_path, session_token) = new_session_path()
    screen_cast_call(portal.CreateSession, on_create_session_response,
                     options={ 'session_handle_token': session_token 
    
    try:
        loop.run()
    except KeyboardInterrupt:
        terminate()
    

    according to what I read in the XDG docs Remote Desktop does not work with the restore token. so I had to do some redesign.