annotate libervia/frontends/tools/webrtc_remote_control.py @ 4306:94e0968987cd

plugin XEP-0033: code modernisation, improve delivery, data validation: - Code has been rewritten using Pydantic models and `async` coroutines for data validation and cleaner element parsing/generation. - Delivery has been completely rewritten. It now works even if server doesn't support multicast, and send to local multicast service first. Delivering to local multicast service first is due to bad support of XEP-0033 in server (notably Prosody which has an incomplete implementation), and the current impossibility to detect if a sub-domain service handles fully multicast or only for local domains. This is a workaround to have a good balance between backward compatilibity and use of bandwith, and to make it work with the incoming email gateway implementation (the gateway will only deliver to entities of its own domain). - disco feature checking now uses `async` corountines. `host` implementation still use Deferred return values for compatibility with legacy code. rel 450
author Goffi <goffi@goffi.org>
date Thu, 26 Sep 2024 16:12:01 +0200
parents 0d7bb4df2343
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/env python3
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
2
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
3 # Libervia Remote-Control via WebRTC implementation.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
4 # Copyright (C) 2009-2024 Jérôme Poisson (goffi@goffi.org)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
5
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
6 # This program is free software: you can redistribute it and/or modify
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # it under the terms of the GNU Affero General Public License as published by
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # the Free Software Foundation, either version 3 of the License, or
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # (at your option) any later version.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
10
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 # This program is distributed in the hope that it will be useful,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # GNU Affero General Public License for more details.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
15
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
16 # You should have received a copy of the GNU Affero General Public License
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
18
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
19
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
20 import asyncio
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 from functools import partial
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
22 import logging
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 from typing import Any, Awaitable, Callable
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
24
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 import gi
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 from gi.overrides.Gst import GLib
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
27 from gi.repository import GstWebRTC
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
28
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
29 import cbor2
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
30 from libervia.backend.core.i18n import _
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 from libervia.backend.tools.common import data_format
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
32 from libervia.frontends.tools import aio, jid, webrtc
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
33 from libervia.frontends.tools.webrtc_models import (
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
34 CallData,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
35 SourcesNone,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
36 SourcesPipeline,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
38
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 gi.require_versions({"Gst": "1.0", "GstWebRTC": "1.0"})
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
40 OnOpenCbType = Callable[["WebRTCRemoteController"], None | Awaitable[None]]
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
41 MOUSE_BTN_LEFT = 0x110
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 MOUSE_BTN_RIGHT = 0x111
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
43 MOUSE_BTN_MIDDLE = 0x112
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
44 MOUSE_BTN_FORWARD = 0x115
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
45 MOUSE_BTN_BACK = 0x116
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
46
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
47
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
48 log = logging.getLogger(__name__)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
49
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
50
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 class WebRTCRemoteController:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
52
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
53 def __init__(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
54 self,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
55 bridge,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
56 profile: str,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 on_call_start_cb: Callable[[dict], Any] | None = None,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
58 end_call_cb: Callable[[], Any] | None = None,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 ) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 """Initializes the File Sender.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
61
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 @param bridge: An async Bridge instance.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 @param profile: The profile name to be used.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 @param on_call_start_cb: A blocking or async callable that accepts a dict as its
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 only argument.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
66 @param end_call_cb: A callable to be invoked at the end of a call.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 """
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
68
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
69 self.bridge = bridge
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
70 self.profile = profile
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
71 self.on_call_start_cb = on_call_start_cb
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
72 self.end_call_cb = end_call_cb
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 self.loop = asyncio.get_event_loop()
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
74 self.data_channel: GstWebRTC.WebRTCDataChannel | None = None
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
75
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
76 def send_input(self, input_data: dict) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
77 """Send an input data to controlled device
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
78
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
79 @param input_data: data of the input event.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
80 """
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
81 assert self.data_channel is not None
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
82 self.data_channel.send_data(GLib.Bytes(cbor2.dumps(input_data)))
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
83
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
84 async def _on_webrtc_call_start(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 self,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
86 options: dict,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 callee: str,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 call_data: dict,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
89 profile: str,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 ) -> str:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
91 rc_data = {
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
92 "webrtc": True,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 "call_data": call_data,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
94 }
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
95 rc_data.update(options)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 remote_control_data_s = await self.bridge.remote_control_start(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
97 str(callee),
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
98 data_format.serialise(rc_data),
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
99 profile,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
100 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
101 remote_control_data = data_format.deserialise(remote_control_data_s)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
102
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
103 if self.on_call_start_cb is not None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 await aio.maybe_async(self.on_call_start_cb(remote_control_data))
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
105 return remote_control_data["session_id"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
106
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 def _on_dc_close(self, webrtc_call, data_channel: GstWebRTC.WebRTCDataChannel):
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 if webrtc_call is not None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
109 aio.run_from_thread(self._end_call_and_quit, webrtc_call, loop=self.loop)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
110
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
111 async def _end_call_and_quit(self, webrtc_call):
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
112 await webrtc_call.webrtc.end_call()
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
113 if self.end_call_cb is not None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
114 await aio.maybe_async(self.end_call_cb())
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
115
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
116 def _on_dc_open(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
117 self, on_open_cb: OnOpenCbType, data_channel: GstWebRTC.WebRTCDataChannel
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
118 ) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
119 """Called when datachannel is open"""
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
120 self.data_channel = data_channel
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
121 aio.run_from_thread(self.on_dc_opened, on_open_cb, loop=self.loop)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
122
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
123 async def on_dc_opened(self, on_open_cb: OnOpenCbType) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
124 await aio.maybe_async(on_open_cb(self))
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
125
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
126 async def start(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
127 self, callee: jid.JID, options: dict, on_open_cb: OnOpenCbType
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
128 ) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
129 """Start a remote control session with ``callee``
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
130
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
131 @param callee: The JID of the recipient to send the file to.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
132 @param options: Options such as which devices to control.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
133 @param on_open_cb: Method to call when the Data Channel is open.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
134 The WebRTCRemoteController instance used as argument can then be used to send
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
135 input events to received.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 """
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
137 call_data = CallData(callee=callee)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
138 self.webrtc_call = await webrtc.WebRTCCall.make_webrtc_call(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
139 self.bridge,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
140 self.profile,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
141 call_data,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 sources_data=webrtc.SourcesDataChannel(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
143 dc_open_cb=partial(self._on_dc_open, on_open_cb),
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
144 ),
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
145 call_start_cb=partial(self._on_webrtc_call_start, options),
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
146 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
147
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
148
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
149 class WebRTCRemoteControlReceiver:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
150
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
151 def __init__(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
152 self,
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
153 bridge,
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
154 profile: str,
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
155 on_close_cb: Callable[[], Any] | None = None,
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
156 verbose: bool = False,
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
157 ) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
158 """Initializes the File Receiver.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
159
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
160 @param bridge: An async Bridge instance.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
161 @param profile: The profile name to be used.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
162 @param on_close_cb: Called when the Data Channel is closed.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
163 @param verbose: if True, print input events.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
164 """
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
165 self.bridge = bridge
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
166 self.profile = profile
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
167 self.on_close_cb = on_close_cb
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
168 self.loop = asyncio.get_event_loop()
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
169 self.desktop_portal = None
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
170 self.remote_desktop_data: dict | None = None
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
171 self.stream_node_id: int | None = None
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
172 self.verbose = verbose
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
173
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
174 async def do_input(self, data: dict) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
175 assert self.desktop_portal is not None
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
176 try:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
177 type_ = data["type"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
178 if type_.startswith("mouse"):
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
179 try:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
180 try:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
181 x, y = data["x"], data["y"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
182 except KeyError:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
183 dx, dy = data["movementX"], data["movementY"]
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
184 await self.desktop_portal.notify_pointer_motion(dx, dy)
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
185 else:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
186 assert self.stream_node_id is not None
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
187 await self.desktop_portal.notify_pointer_motion_absolute(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
188 self.stream_node_id, x, y
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
189 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
190 except Exception:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
191 log.exception("Can't send input")
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
192
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
193 if type_ in ("mouseup", "mousedown"):
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
194 buttons = data["buttons"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
195 state = 1 if type_ == "mousedown" else 0
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
196 # see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons#value
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
197 if buttons & 1:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
198 await self.desktop_portal.notify_pointer_button(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
199 MOUSE_BTN_LEFT, state
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
200 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
201 if buttons & 2:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
202 await self.desktop_portal.notify_pointer_button(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
203 MOUSE_BTN_RIGHT, state
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
204 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
205 if buttons & 4:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
206 await self.desktop_portal.notify_pointer_button(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
207 MOUSE_BTN_MIDDLE, state
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
208 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
209 if buttons & 8:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
210 await self.desktop_portal.notify_pointer_button(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
211 MOUSE_BTN_BACK, state
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
212 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
213 if buttons & 16:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
214 await self.desktop_portal.notify_pointer_button(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
215 MOUSE_BTN_FORWARD, state
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
216 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
217 elif type_ == "wheel":
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
218 dx = data.get("deltaX", 0)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
219 dy = data.get("deltaY", 0)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
220 delta_mode = data["deltaMode"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
221 if delta_mode == 0:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
222 # deltas are in pixels
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
223 await self.desktop_portal.notify_pointer_axis(dx, dy)
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
224 else:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
225 # deltas are in steps (see
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
226 # https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event#event_properties)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
227 if dx:
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
228 await self.desktop_portal.notify_pointer_axis(1, dx)
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
229 if dy:
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
230 await self.desktop_portal.notify_pointer_axis(0, dy)
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
231 elif type_.startswith("key"):
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
232 # FIXME: this is a really naive implementation, it needs tot be improved.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
233 key = data["key"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
234 if data.get("shiftKey", False):
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
235 key = key.upper()
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
236 await self.desktop_portal.notify_keyboard_keysym(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
237 ord(key), 1 if type_ == "keydown" else 0
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
238 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
239
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
240 except Exception:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
241 log.exception(f"Can't handle input {data}")
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
243 def _on_dc_message_data(self, data_channel, glib_data) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
244 """A data chunk of the file has been received."""
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
245 raw = glib_data.get_data()
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
246 data = cbor2.loads(raw)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
247 if self.verbose:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
248 print(data)
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
249 aio.run_from_thread(self.do_input, data, loop=self.loop)
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
250
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
251 def _on_dc_close(self, data_channel) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
252 """Data channel is closed
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
253
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
254 The file download should be complete, we close it.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
255 """
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
256 aio.run_from_thread(self._on_close, loop=self.loop)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
257
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
258 async def _on_close(self) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
259 if self.on_close_cb is not None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
260 await aio.maybe_async(self.on_close_cb())
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
261
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
262 def _on_data_channel(self, webrtcbin, data_channel) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
263 """The data channel has been opened."""
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
264 data_channel.connect("on-message-data", self._on_dc_message_data)
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
265 data_channel.connect("on-close", self._on_dc_close)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
266
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
267 async def request_remote_desktop(self, with_screen_sharing: bool) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
268 """Request autorisation to remote control desktop.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
269
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
270 @param with_screen_sharing: True if screen must be shared.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
271 """
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
272 from .portal_desktop import DesktopPortal
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
273
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
274 self.desktop_portal = DesktopPortal()
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
275 self.remote_desktop_data = await self.desktop_portal.request_remote_desktop(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
276 with_screen_sharing
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
277 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
278 print(self.remote_desktop_data)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
279
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
280 async def start_receiving(
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
281 self, from_jid: jid.JID, session_id: str, screenshare: dict
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
282 ) -> None:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
283 """Receives a file via WebRTC and saves it to the specified path.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
284
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
285 @param from_jid: The JID of the entity sending the file.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
286 @param session_id: The Jingle FT Session ID.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
287 @param file_path: The local path where the received file will be saved.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
288 If a file already exists at this path, it will be overwritten.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
289 @param file_data: Additional data about the file being transferred.
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
290 """
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
291 call_data = CallData(callee=from_jid, sid=session_id)
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
292 if "video" in screenshare and self.remote_desktop_data:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
293 try:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
294 stream_data = self.remote_desktop_data["stream_data"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
295 log.debug(f"{stream_data=}")
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
296 self.stream_node_id = stream_data["node_id"]
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
297
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
298 sources_data = SourcesPipeline(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
299 video_pipeline="pipewiresrc",
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
300 audio_pipeline="",
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
301 video_properties={
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
302 "path": str(self.stream_node_id),
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
303 "do-timestamp": 1,
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
304 },
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
305 )
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
306 except KeyError:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
307 sources_data = SourcesNone()
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
308 else:
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
309 sources_data = SourcesNone()
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
310
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
311 await webrtc.WebRTCCall.make_webrtc_call(
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
312 self.bridge,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
313 self.profile,
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
314 call_data,
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
315 sources_data=sources_data,
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
316 sinks_data=webrtc.SinksNone(),
4270
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
317 dc_data_list=[
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
318 webrtc.SinksDataChannel(
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
319 dc_on_data_channel=self._on_data_channel,
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
320 )
0d7bb4df2343 Reformatted code base using black.
Goffi <goffi@goffi.org>
parents: 4242
diff changeset
321 ],
4242
8acf46ed7f36 frontends: remote control implementation:
Goffi <goffi@goffi.org>
parents:
diff changeset
322 )